def setUp(self): self.directory = tempfile.mkdtemp() # Creating random file just to test specific part of code tempfile.NamedTemporaryFile(dir=self.directory, delete=True) store = TransactionCompactStorage(self.directory) reactor = Clock() super().setUp(TransactionCacheStorage(store, reactor, capacity=5))
def setUp(self): super().setUp() store = TransactionMemoryStorage() self.cache_storage = TransactionCacheStorage(store, self.clock, capacity=5) self.cache_storage._manually_initialize() self.cache_storage.pre_init() self.genesis = self.cache_storage.get_all_genesis() self.genesis_blocks = [tx for tx in self.genesis if tx.is_block] self.genesis_txs = [tx for tx in self.genesis if not tx.is_block] # Save genesis metadata self.cache_storage.save_transaction(self.genesis_txs[0], only_metadata=True) self.manager = self.create_peer('testnet', tx_storage=self.cache_storage, unlock_wallet=True)
def setUp(self): store = TransactionMemoryStorage() reactor = Clock() super().setUp(TransactionCacheStorage(store, reactor, capacity=5))
def setUp(self): self.directory = tempfile.mkdtemp() store = TransactionBinaryStorage(self.directory) reactor = Clock() super().setUp(TransactionCacheStorage(store, reactor, capacity=5))
def prepare(self, args: Namespace) -> None: import hathor from hathor.conf import HathorSettings from hathor.manager import HathorManager, TestMode from hathor.p2p.peer_discovery import BootstrapPeerDiscovery, DNSPeerDiscovery from hathor.p2p.peer_id import PeerId from hathor.p2p.utils import discover_hostname from hathor.transaction import genesis from hathor.transaction.storage import ( TransactionStorage, TransactionCacheStorage, TransactionCompactStorage, TransactionMemoryStorage, ) from hathor.wallet import HDWallet, Wallet settings = HathorSettings() if args.recursion_limit: sys.setrecursionlimit(args.recursion_limit) if not args.peer: peer_id = PeerId() else: data = json.load(open(args.peer, 'r')) peer_id = PeerId.create_from_json(data) print('Hathor v{} (genesis {})'.format(hathor.__version__, genesis.GENESIS_HASH.hex()[:7])) print('My peer id is', peer_id.id) def create_wallet(): if args.wallet == 'hd': print('Using HDWallet') kwargs = { 'words': args.words, } if args.passphrase: wallet_passphrase = getpass.getpass( prompt='HD Wallet passphrase:') kwargs['passphrase'] = wallet_passphrase.encode() if args.data: kwargs['directory'] = args.data return HDWallet(**kwargs) elif args.wallet == 'keypair': print('Using KeyPairWallet') if args.data: wallet = Wallet(directory=args.data) else: wallet = Wallet() wallet.flush_to_disk_interval = 5 # seconds if args.unlock_wallet: wallet_passwd = getpass.getpass(prompt='Wallet password:'******'Invalid type for wallet') tx_storage: TransactionStorage if args.data: wallet_dir = args.data print('Using Wallet at {}'.format(wallet_dir)) if args.rocksdb_storage: from hathor.transaction.storage import TransactionRocksDBStorage tx_dir = os.path.join(args.data, 'tx.db') tx_storage = TransactionRocksDBStorage( path=tx_dir, with_index=(not args.cache)) print('Using TransactionRocksDBStorage at {}'.format(tx_dir)) else: tx_dir = os.path.join(args.data, 'tx') tx_storage = TransactionCompactStorage( path=tx_dir, with_index=(not args.cache)) print('Using TransactionCompactStorage at {}'.format(tx_dir)) if args.cache: tx_storage = TransactionCacheStorage(tx_storage, reactor) if args.cache_size: tx_storage.capacity = args.cache_size if args.cache_interval: tx_storage.interval = args.cache_interval print( 'Using TransactionCacheStorage, capacity {}, interval {}s'. format(tx_storage.capacity, tx_storage.interval)) tx_storage.start() else: # if using MemoryStorage, no need to have cache tx_storage = TransactionMemoryStorage() print('Using TransactionMemoryStorage') self.tx_storage = tx_storage if args.wallet: self.wallet = create_wallet() else: self.wallet = None if args.hostname and args.auto_hostname: print('You cannot use --hostname and --auto-hostname together.') sys.exit(-1) if not args.auto_hostname: hostname = args.hostname else: print('Trying to discover your hostname...') hostname = discover_hostname() if not hostname: print('Aborting because we could not discover your hostname.') print('Try again or run without --auto-hostname.') sys.exit(-1) print('Hostname discovered and set to {}'.format(hostname)) network = settings.NETWORK_NAME self.manager = HathorManager(reactor, peer_id=peer_id, network=network, hostname=hostname, tx_storage=self.tx_storage, wallet=self.wallet, wallet_index=args.wallet_index, stratum_port=args.stratum, min_block_weight=args.min_block_weight, ssl=True) if args.allow_mining_without_peers: self.manager.allow_mining_without_peers() dns_hosts = [] if settings.BOOTSTRAP_DNS: dns_hosts.extend(settings.BOOTSTRAP_DNS) if args.dns: dns_hosts.extend(args.dns) if dns_hosts: self.manager.add_peer_discovery(DNSPeerDiscovery(dns_hosts)) if args.bootstrap: self.manager.add_peer_discovery( BootstrapPeerDiscovery(args.bootstrap)) if args.test_mode_tx_weight: self.manager.test_mode = TestMode.TEST_TX_WEIGHT if self.wallet: self.wallet.test_mode = True for description in args.listen: self.manager.add_listen_address(description) self.start_manager() self.register_resources(args)
def prepare(self, args: Namespace) -> None: import hathor from hathor.cli.util import check_or_exit from hathor.conf import HathorSettings from hathor.conf.get_settings import get_settings_module from hathor.daa import TestMode, _set_test_mode from hathor.manager import HathorManager from hathor.p2p.peer_discovery import BootstrapPeerDiscovery, DNSPeerDiscovery from hathor.p2p.peer_id import PeerId from hathor.p2p.utils import discover_hostname from hathor.transaction import genesis from hathor.transaction.storage import ( TransactionCacheStorage, TransactionCompactStorage, TransactionMemoryStorage, TransactionRocksDBStorage, TransactionStorage, ) from hathor.wallet import HDWallet, Wallet settings = HathorSettings() settings_module = get_settings_module() # only used for logging its location self.log = logger.new() from setproctitle import setproctitle setproctitle('{}hathor-core'.format(args.procname_prefix)) if args.recursion_limit: sys.setrecursionlimit(args.recursion_limit) else: sys.setrecursionlimit(5000) try: import resource except ModuleNotFoundError: pass else: (nofile_soft, _) = resource.getrlimit(resource.RLIMIT_NOFILE) if nofile_soft < 256: print('Maximum number of open file descriptors is too low. Minimum required is 256.') sys.exit(-2) if not args.peer: peer_id = PeerId() else: data = json.load(open(args.peer, 'r')) peer_id = PeerId.create_from_json(data) python = f'{platform.python_version()}-{platform.python_implementation()}' self.check_unsafe_arguments(args) self.log.info( 'hathor-core v{hathor}', hathor=hathor.__version__, pid=os.getpid(), genesis=genesis.GENESIS_HASH.hex()[:7], my_peer_id=str(peer_id.id), python=python, platform=platform.platform(), settings=settings_module.__file__, ) def create_wallet(): if args.wallet == 'hd': kwargs = { 'words': args.words, } if args.passphrase: wallet_passphrase = getpass.getpass(prompt='HD Wallet passphrase:') kwargs['passphrase'] = wallet_passphrase.encode() if args.data: kwargs['directory'] = args.data return HDWallet(**kwargs) elif args.wallet == 'keypair': print('Using KeyPairWallet') if args.data: wallet = Wallet(directory=args.data) else: wallet = Wallet() wallet.flush_to_disk_interval = 5 # seconds if args.unlock_wallet: wallet_passwd = getpass.getpass(prompt='Wallet password:'******'Invalid type for wallet') tx_storage: TransactionStorage if args.memory_storage: check_or_exit(not args.data, '--data should not be used with --memory-storage') # if using MemoryStorage, no need to have cache tx_storage = TransactionMemoryStorage() assert not args.x_rocksdb_indexes, 'RocksDB indexes require RocksDB data' self.log.info('with storage', storage_class=type(tx_storage).__name__) elif args.json_storage: check_or_exit(args.data, '--data is expected') assert not args.x_rocksdb_indexes, 'RocksDB indexes require RocksDB data' tx_storage = TransactionCompactStorage(path=args.data, with_index=(not args.cache)) else: check_or_exit(args.data, '--data is expected') if args.rocksdb_storage: self.log.warn('--rocksdb-storage is now implied, no need to specify it') cache_capacity = args.rocksdb_cache use_memory_indexes = not args.x_rocksdb_indexes tx_storage = TransactionRocksDBStorage(path=args.data, with_index=(not args.cache), cache_capacity=cache_capacity, use_memory_indexes=use_memory_indexes) self.log.info('with storage', storage_class=type(tx_storage).__name__, path=args.data) if args.cache: check_or_exit(not args.memory_storage, '--cache should not be used with --memory-storage') tx_storage = TransactionCacheStorage(tx_storage, reactor) if args.cache_size: tx_storage.capacity = args.cache_size if args.cache_interval: tx_storage.interval = args.cache_interval self.log.info('with cache', capacity=tx_storage.capacity, interval=tx_storage.interval) self.tx_storage = tx_storage self.log.info('with indexes', indexes_class=type(tx_storage.indexes).__name__) if args.wallet: self.wallet = create_wallet() self.log.info('with wallet', wallet=self.wallet, path=args.data) else: self.wallet = None if args.hostname and args.auto_hostname: print('You cannot use --hostname and --auto-hostname together.') sys.exit(-1) if not args.auto_hostname: hostname = args.hostname else: print('Trying to discover your hostname...') hostname = discover_hostname() if not hostname: print('Aborting because we could not discover your hostname.') print('Try again or run without --auto-hostname.') sys.exit(-1) print('Hostname discovered and set to {}'.format(hostname)) network = settings.NETWORK_NAME enable_sync_v1 = not args.x_sync_v2_only enable_sync_v2 = args.x_sync_v2_only or args.x_sync_bridge self.manager = HathorManager( reactor, peer_id=peer_id, network=network, hostname=hostname, tx_storage=self.tx_storage, wallet=self.wallet, wallet_index=args.wallet_index, stratum_port=args.stratum, ssl=True, checkpoints=settings.CHECKPOINTS, enable_sync_v1=enable_sync_v1, enable_sync_v2=enable_sync_v2, soft_voided_tx_ids=set(settings.SOFT_VOIDED_TX_IDS), ) if args.allow_mining_without_peers: self.manager.allow_mining_without_peers() if args.x_localhost_only: self.manager.connections.localhost_only = True dns_hosts = [] if settings.BOOTSTRAP_DNS: dns_hosts.extend(settings.BOOTSTRAP_DNS) if args.dns: dns_hosts.extend(args.dns) if dns_hosts: self.manager.add_peer_discovery(DNSPeerDiscovery(dns_hosts)) if args.bootstrap: self.manager.add_peer_discovery(BootstrapPeerDiscovery(args.bootstrap)) if args.test_mode_tx_weight: _set_test_mode(TestMode.TEST_TX_WEIGHT) if self.wallet: self.wallet.test_mode = True if args.x_full_verification: self.manager._full_verification = True if args.x_fast_init_beta: self.log.warn('--x-fast-init-beta is now the default, no need to specify it') for description in args.listen: self.manager.add_listen_address(description) self.start_manager(args) self.register_resources(args)
class BaseCacheStorageTest(unittest.TestCase): __test__ = False def setUp(self): super().setUp() store = TransactionMemoryStorage() self.cache_storage = TransactionCacheStorage(store, self.clock, capacity=5) self.cache_storage._manually_initialize() self.cache_storage.pre_init() self.genesis = self.cache_storage.get_all_genesis() self.genesis_blocks = [tx for tx in self.genesis if tx.is_block] self.genesis_txs = [tx for tx in self.genesis if not tx.is_block] # Save genesis metadata self.cache_storage.save_transaction(self.genesis_txs[0], only_metadata=True) self.manager = self.create_peer('testnet', tx_storage=self.cache_storage, unlock_wallet=True) def tearDown(self): super().tearDown() def _get_new_tx(self, nonce): tx = Transaction(nonce=nonce, storage=self.cache_storage) tx.update_hash() meta = TransactionMetadata(hash=tx.hash) tx._metadata = meta return tx def test_write_read(self): txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) txs2 = [self.cache_storage.get_transaction(tx.hash) for tx in txs] self.assertEqual(txs, txs2) def test_dirty_set(self): txs = [self._get_new_tx(nonce) for nonce in range(CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) for tx in txs: self.assertIn(tx.hash, self.cache_storage.dirty_txs) # should flush to disk and empty dirty set self.cache_storage._flush_to_storage( self.cache_storage.dirty_txs.copy()) self.assertEqual(0, len(self.cache_storage.dirty_txs)) def test_capacity(self): # cache should not grow over its capacity txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) self.assertEqual(CACHE_SIZE, len(self.cache_storage.cache)) def test_read_adds_to_cache(self): # make sure reading also adds to cache, not only writes txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) # by now, tx[0] will already have left the cache self.assertNotIn(txs[0].hash, self.cache_storage.cache) # read tx self.cache_storage.get_transaction(txs[0].hash) # now it should be in cache self.assertIn(txs[0].hash, self.cache_storage.cache) def test_read_moves_to_end(self): # when we read a tx from cache, it should be moved to the end of cache so it's evicted later txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for i in range(CACHE_SIZE): self.cache_storage.save_transaction(txs[i]) # first tx added would be the first to leave cache if we add one more tx # let's read it from cache so it goes to the end self.cache_storage.get_transaction(txs[0].hash) # add a new tx to cache, so it will evict a tx self.cache_storage.save_transaction(txs[-1]) # first tx should be in cache self.assertIn(txs[0].hash, self.cache_storage.cache) def test_cache_eviction(self): # tests we're evicting the oldest tx from cache txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for i in range(CACHE_SIZE): self.cache_storage.save_transaction(txs[i]) # next save should evict first tx self.cache_storage.save_transaction(txs[CACHE_SIZE]) self.assertNotIn(txs[0].hash, self.cache_storage.cache) self.assertIn(txs[CACHE_SIZE].hash, self.cache_storage.cache) self.assertEqual(CACHE_SIZE, len(self.cache_storage.cache)) def test_flush_thread(self): txs = [self._get_new_tx(nonce) for nonce in range(CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) for tx in txs: self.assertIn(tx.hash, self.cache_storage.dirty_txs) # Flush deferred is not None self.assertIsNotNone(self.cache_storage.flush_deferred) last_flush_deferred = self.cache_storage.flush_deferred self.cache_storage._start_flush_thread() self.assertEqual(last_flush_deferred, self.cache_storage.flush_deferred) # We flush the cache and flush_deferred becomes None self.cache_storage._cb_flush_thread( self.cache_storage.dirty_txs.copy()) self.assertIsNone(self.cache_storage.flush_deferred) # After the interval it becomes not None again self.clock.advance(10) self.assertIsNotNone(self.cache_storage.flush_deferred) # If an err occurs, it will become None again and then not None after the interval self.cache_storage._err_flush_thread('') self.assertIsNone(self.cache_storage.flush_deferred) self.clock.advance(5) self.assertIsNotNone(self.cache_storage.flush_deferred) # Remove element from cache to test a part of the code del self.cache_storage.cache[next(iter(self.cache_storage.dirty_txs))] self.cache_storage._flush_to_storage( self.cache_storage.dirty_txs.copy()) def test_topological_sort_dfs(self): _set_test_mode(TestMode.TEST_ALL_WEIGHT) add_new_blocks(self.manager, 11, advance_clock=1) tx = add_new_transactions(self.manager, 1, advance_clock=1)[0] total = 0 for tx in self.cache_storage._topological_sort_dfs(root=tx, visited=dict()): total += 1 self.assertEqual(total, 5)
class BasicTransaction(unittest.TestCase): def setUp(self): super().setUp() store = TransactionMemoryStorage() self.cache_storage = TransactionCacheStorage(store, self.clock, capacity=5) self.cache_storage._manually_initialize() self.cache_storage.start() self.genesis = self.cache_storage.get_all_genesis() self.genesis_blocks = [tx for tx in self.genesis if tx.is_block] self.genesis_txs = [tx for tx in self.genesis if not tx.is_block] # Save genesis metadata self.cache_storage.save_transaction_deferred(self.genesis_txs[0], only_metadata=True) self.manager = self.create_peer('testnet', tx_storage=self.cache_storage, unlock_wallet=True) def tearDown(self): super().tearDown() def _get_new_tx(self, nonce): tx = Transaction(nonce=nonce, storage=self.cache_storage) tx.update_hash() meta = TransactionMetadata(hash=tx.hash) tx._metadata = meta return tx def test_write_read(self): txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) txs2 = [self.cache_storage.get_transaction(tx.hash) for tx in txs] self.assertEqual(txs, txs2) def test_dirty_set(self): txs = [self._get_new_tx(nonce) for nonce in range(CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) for tx in txs: self.assertIn(tx.hash, self.cache_storage.dirty_txs) # should flush to disk and empty dirty set self.cache_storage._flush_to_storage( self.cache_storage.dirty_txs.copy()) self.assertEqual(0, len(self.cache_storage.dirty_txs)) def test_capacity(self): # cache should not grow over its capacity txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) self.assertEqual(CACHE_SIZE, len(self.cache_storage.cache)) def test_read_adds_to_cache(self): # make sure reading also adds to cache, not only writes txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) # by now, tx[0] will already have left the cache self.assertNotIn(txs[0].hash, self.cache_storage.cache) # read tx self.cache_storage.get_transaction(txs[0].hash) # now it should be in cache self.assertIn(txs[0].hash, self.cache_storage.cache) def test_read_moves_to_end(self): # when we read a tx from cache, it should be moved to the end of cache so it's evicted later txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for i in range(CACHE_SIZE): self.cache_storage.save_transaction(txs[i]) # first tx added would be the first to leave cache if we add one more tx # let's read it from cache so it goes to the end self.cache_storage.get_transaction(txs[0].hash) # add a new tx to cache, so it will evict a tx self.cache_storage.save_transaction(txs[-1]) # first tx should be in cache self.assertIn(txs[0].hash, self.cache_storage.cache) def test_cache_eviction(self): # tests we're evicting the oldest tx from cache txs = [self._get_new_tx(nonce) for nonce in range(2 * CACHE_SIZE)] for i in range(CACHE_SIZE): self.cache_storage.save_transaction(txs[i]) # next save should evict first tx self.cache_storage.save_transaction(txs[CACHE_SIZE]) self.assertNotIn(txs[0].hash, self.cache_storage.cache) self.assertIn(txs[CACHE_SIZE].hash, self.cache_storage.cache) self.assertEqual(CACHE_SIZE, len(self.cache_storage.cache)) def test_flush_thread(self): txs = [self._get_new_tx(nonce) for nonce in range(CACHE_SIZE)] for tx in txs: self.cache_storage.save_transaction(tx) for tx in txs: self.assertIn(tx.hash, self.cache_storage.dirty_txs) # Flush deferred is not None self.assertIsNotNone(self.cache_storage.flush_deferred) last_flush_deferred = self.cache_storage.flush_deferred self.cache_storage._start_flush_thread() self.assertEqual(last_flush_deferred, self.cache_storage.flush_deferred) # We flush the cache and flush_deferred becomes None self.cache_storage._cb_flush_thread( self.cache_storage.dirty_txs.copy()) self.assertIsNone(self.cache_storage.flush_deferred) # After the interval it becomes not None again self.clock.advance(10) self.assertIsNotNone(self.cache_storage.flush_deferred) # If an err occurs, it will become None again and then not None after the interval self.cache_storage._err_flush_thread('') self.assertIsNone(self.cache_storage.flush_deferred) self.clock.advance(5) self.assertIsNotNone(self.cache_storage.flush_deferred) # Remove element from cache to test a part of the code del self.cache_storage.cache[next(iter(self.cache_storage.dirty_txs))] self.cache_storage._flush_to_storage( self.cache_storage.dirty_txs.copy()) def test_deferred_methods(self): for _ in self._test_deferred_methods(): pass @inlineCallbacks def _test_deferred_methods(self): # Testing without cloning self.cache_storage._clone_if_needed = False block_parents = [tx.hash for tx in self.genesis] output = TxOutput( 200, bytes.fromhex('1e393a5ce2ff1c98d4ff6892f2175100f2dad049')) obj = Block(timestamp=MIN_TIMESTAMP, weight=12, outputs=[output], parents=block_parents, nonce=100781, storage=self.cache_storage) obj.resolve() self.cache_storage.save_transaction_deferred(obj) loaded_obj1 = yield self.cache_storage.get_transaction_deferred( obj.hash) metadata_obj1_def = yield self.cache_storage.get_metadata_deferred( obj.hash) metadata_obj1 = obj.get_metadata() self.assertEqual(metadata_obj1_def, metadata_obj1) metadata_error = yield self.cache_storage.get_metadata_deferred( bytes.fromhex( '0001569c85fffa5782c3979e7d68dce1d8d84772505a53ddd76d636585f3977e' )) self.assertIsNone(metadata_error) self.cache_storage._flush_to_storage( self.cache_storage.dirty_txs.copy()) self.cache_storage.cache = collections.OrderedDict() loaded_obj2 = yield self.cache_storage.get_transaction_deferred( obj.hash) self.assertEqual(loaded_obj1, loaded_obj2) self.assertTrue( (yield self.cache_storage.transaction_exists_deferred(obj.hash))) self.assertFalse((yield self.cache_storage.transaction_exists_deferred( '0001569c85fffa5782c3979e7d68dce1d8d84772505a53ddd76d636585f3977e') )) self.assertFalse( self.cache_storage.transaction_exists( '0001569c85fffa5782c3979e7d68dce1d8d84772505a53ddd76d636585f3977e' )) self.assertEqual(obj, loaded_obj1) self.assertEqual(obj.is_block, loaded_obj1.is_block) count = yield self.cache_storage.get_count_tx_blocks_deferred() self.assertEqual(count, 4) all_transactions = yield self.cache_storage.get_all_transactions_deferred( ) total = 0 for tx in all_transactions: total += 1 self.assertEqual(total, 4) def test_topological_sort_dfs(self): self.manager.test_mode = TestMode.TEST_ALL_WEIGHT add_new_blocks(self.manager, 11, advance_clock=1) tx = add_new_transactions(self.manager, 1, advance_clock=1)[0] total = 0 for tx in self.cache_storage._topological_sort_dfs(root=tx, visited=dict()): total += 1 self.assertEqual(total, 5)