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))
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #8
0
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)