def test_add_then_update(self): cache = TransactionCache(self.store) bytedata_1 = bytes.fromhex(tx_hex_1) tx_hash_1 = bitcoinx.double_sha256(bytedata_1) metadata_1 = TxData(position=11) with SynchronousWriter() as writer: cache.add( [(tx_hash_1, metadata_1, bytedata_1, TxFlags.StateDispatched)], completion_callback=writer.get_callback()) assert writer.succeeded() assert cache.is_cached(tx_hash_1) entry = cache.get_entry(tx_hash_1) assert TxFlags.HasByteData | TxFlags.HasPosition | TxFlags.StateDispatched == entry.flags assert entry.bytedata is not None metadata_2 = TxData(fee=10, height=88) propagate_flags = TxFlags.HasFee | TxFlags.HasHeight with SynchronousWriter() as writer: cache.update([(tx_hash_1, metadata_2, None, propagate_flags | TxFlags.HasPosition)], completion_callback=writer.get_callback()) assert writer.succeeded() entry = cache.get_entry(tx_hash_1) expected_flags = propagate_flags | TxFlags.StateDispatched | TxFlags.HasByteData assert expected_flags == entry.flags, \ f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(entry.flags)}" assert entry.bytedata is not None
def test_get_metadata(self): # Full entry caching for non-settled transactions, otherwise only metadata. bytedata_set_1 = bytes.fromhex(tx_hex_1) tx_hash_1 = bitcoinx.double_sha256(bytedata_set_1) metadata_set_1 = TxData(height=None, fee=2, position=None, date_added=1, date_updated=1) bytedata_set_2 = bytes.fromhex(tx_hex_2) tx_hash_2 = bitcoinx.double_sha256(bytedata_set_2) metadata_set_2 = TxData(height=1, fee=2, position=10, date_added=1, date_updated=1) with SynchronousWriter() as writer: self.store.create([ (tx_hash_1, metadata_set_1, bytedata_set_1, TxFlags.Unset, None), (tx_hash_2, metadata_set_2, bytedata_set_2, TxFlags.StateSettled, None), ], completion_callback=writer.get_callback()) assert writer.succeeded() cache = TransactionCache(self.store) metadata_get = cache.get_metadata(tx_hash_1) assert metadata_set_1.height == metadata_get.height assert metadata_set_1.fee == metadata_get.fee assert metadata_set_1.position == metadata_get.position metadata_get = cache.get_metadata(tx_hash_2) assert metadata_set_2.height == metadata_get.height assert metadata_set_2.fee == metadata_get.fee assert metadata_set_2.position == metadata_get.position entry = cache._cache[tx_hash_1] assert cache.have_transaction_data_cached(tx_hash_1) entry = cache._cache[tx_hash_2] assert not cache.have_transaction_data_cached(tx_hash_2)
def test_update_many(self): to_add = [] for i in range(10): tx_bytes = os.urandom(10) tx_hash_bytes = bitcoinx.double_sha256(tx_bytes) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) tx_data = TxData(height=None, fee=2, position=None, timestamp=None) to_add.append((tx_id, tx_data, tx_bytes, TxFlags.Unset)) self.store.add_many(to_add) to_update = [] for tx_id, metadata, tx_bytes, flags in to_add: tx_metadata = TxData(height=1, fee=2, position=None, timestamp=None) to_update.append((tx_id, tx_metadata, tx_bytes, flags)) self.store.update_many(to_update) for tx_id_get, metadata_get, bytedata_get, flags_get in self.store.get_many( ): for update_tx_id, update_metadata, update_tx_bytes, update_flags in to_update: if update_tx_id == tx_id_get: self.assertEqual(metadata_get, update_metadata) self.assertEqual(bytedata_get, update_tx_bytes) continue
def test_add_then_update(self): cache = TxCache(self.store) bytedata_1 = bytes.fromhex(tx_hex_1) tx_id_1 = bitcoinx.hash_to_hex_str(bitcoinx.double_sha256(bytedata_1)) metadata_1 = TxData(position=11) cache.add([(tx_id_1, metadata_1, bytedata_1, TxFlags.StateDispatched)]) self.assertTrue(cache.is_cached(tx_id_1)) entry = cache.get_entry(tx_id_1) self.assertEqual( TxFlags.HasByteData | TxFlags.HasPosition | TxFlags.StateDispatched, entry.flags) self.assertIsNotNone(entry.bytedata) metadata_2 = TxData(fee=10, height=88) propagate_flags = TxFlags.HasFee | TxFlags.HasHeight cache.update([(tx_id_1, metadata_2, None, propagate_flags | TxFlags.HasPosition)]) entry = cache.get_entry(tx_id_1) expected_flags = propagate_flags | TxFlags.StateDispatched | TxFlags.HasByteData self.assertEqual( expected_flags, entry.flags, f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(entry.flags)}" ) self.assertIsNotNone(entry.bytedata)
def test_update_or_add(self): cache = TxCache(self.store) # Add. bytedata_1 = bytes.fromhex(tx_hex_1) tx_hash_bytes_1 = bitcoinx.double_sha256(bytedata_1) tx_id_1 = bitcoinx.hash_to_hex_str(tx_hash_bytes_1) metadata_1 = TxData() cache.update_or_add([(tx_id_1, metadata_1, bytedata_1, TxFlags.StateCleared)]) self.assertTrue(cache.is_cached(tx_id_1)) entry = cache.get_entry(tx_id_1) self.assertEqual(TxFlags.HasByteData | TxFlags.StateCleared, entry.flags) self.assertIsNotNone(entry.bytedata) # Update. metadata_2 = TxData(position=22) cache.update_or_add([(tx_id_1, metadata_2, None, TxFlags.HasPosition | TxFlags.StateDispatched)]) entry = cache.get_entry(tx_id_1) store_flags = self.store.get_flags(tx_id_1) # State flags if present get set in an update otherwise they remain the same. expected_flags = TxFlags.HasPosition | TxFlags.HasByteData | TxFlags.StateDispatched self.assertEqual( expected_flags, store_flags, f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(store_flags)}" ) self.assertEqual( expected_flags, entry.flags, f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(entry.flags)}" ) self.assertEqual(bytedata_1, entry.bytedata) self.assertEqual(metadata_2.position, entry.metadata.position)
def test_update(self): to_add = [] for i in range(10): tx_bytes = os.urandom(10) tx_hash = bitcoinx.double_sha256(tx_bytes) tx_data = TxData(height=None, fee=2, position=None, date_added=1, date_updated=1) if i % 2: to_add.append((tx_hash, tx_data, tx_bytes, TxFlags.HasByteData, None)) else: to_add.append((tx_hash, tx_data, None, TxFlags.Unset, None)) with SynchronousWriter() as writer: self.store.create(to_add, completion_callback=writer.get_callback()) assert writer.succeeded() to_update = [] for tx_hash, metadata, tx_bytes, flags, description in to_add: tx_metadata = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) to_update.append((tx_hash, tx_metadata, tx_bytes, flags)) with SynchronousWriter() as writer: self.store.update(to_update, completion_callback=writer.get_callback()) assert writer.succeeded() for get_tx_hash, bytedata_get, flags_get, metadata_get in self.store.read(): for update_tx_hash, update_metadata, update_tx_bytes, update_flags in to_update: if update_tx_hash == get_tx_hash: assert metadata_get == update_metadata assert bytedata_get == update_tx_bytes continue
def test_add_transaction_update(self): cache = TransactionCache(self.store) tx = Transaction.from_hex(tx_hex_1) data = [ tx.hash(), TxData(height=1295924, position=4, fee=None, date_added=1, date_updated=1), None, TxFlags.Unset, None ] with SynchronousWriter() as writer: cache.add([data], completion_callback=writer.get_callback()) assert writer.succeeded() entry = cache.get_entry(tx.hash()) assert entry is not None assert TxFlags.Unset == entry.flags & TxFlags.STATE_MASK with SynchronousWriter() as writer: cache.add_transaction(tx, TxFlags.StateCleared, completion_callback=writer.get_callback()) assert writer.succeeded() tx_hash = tx.hash() entry = cache.get_entry(tx_hash) assert entry is not None assert cache.have_transaction_data_cached(tx_hash) assert TxFlags.StateCleared == entry.flags & TxFlags.StateCleared
def test_proof(self): bytedata = os.urandom(10) tx_hash = bitcoinx.double_sha256(bytedata) metadata = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) with SynchronousWriter() as writer: self.store.create([(tx_hash, metadata, bytedata, 0, None)], completion_callback=writer.get_callback()) assert writer.succeeded() position1 = 10 merkle_branch1 = [os.urandom(32) for i in range(10)] proof = TxProof(position1, merkle_branch1) date_updated = 1 with SynchronousWriter() as writer: self.store.update_proof([(tx_hash, proof, date_updated)], completion_callback=writer.get_callback()) assert writer.succeeded() rows = self.store.read_proof([self.tx_hash]) assert len(rows) == 0 db_tx_hash, (tx_position2, merkle_branch2) = self.store.read_proof([tx_hash])[0] assert db_tx_hash == tx_hash assert position1 == tx_position2 assert merkle_branch1 == merkle_branch2
def test_read_metadata(self) -> None: # We're going to add five matches and look for two of them, checking that we do not match # unwanted rows. all_tx_hashes = [] datas = [] for i in range(5): bytedata = os.urandom(10) tx_hash = bitcoinx.double_sha256(bytedata) metadata = TxData(height=i * 100, fee=i * 1000, position=None, date_added=1, date_updated=1) datas.append((tx_hash, metadata, bytedata, TxFlags.Unset, None)) all_tx_hashes.append(tx_hash) with SynchronousWriter() as writer: self.store.create(datas, completion_callback=writer.get_callback()) assert writer.succeeded() # We also ask for a dud tx_hash that won't get matched. select_tx_hashes = [all_tx_hashes[0], all_tx_hashes[3], b"12121212"] rowdatas = self.store.read_metadata(tx_hashes=select_tx_hashes) # Check that the two valid matches are there and their values match the projected values. assert len(rowdatas) == 2 for rowdata in rowdatas: tx_hash = rowdata[0] tx_flags = rowdata[1] metadata = rowdata[2] rowidx = all_tx_hashes.index(tx_hash) assert metadata.height == rowidx * 100 assert metadata.fee == rowidx * 1000 assert metadata.position is None
def test_delete(self) -> None: to_add = [] for i in range(10): bytedata = os.urandom(10) tx_hash = bitcoinx.double_sha256(bytedata) metadata = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) to_add.append((tx_hash, metadata, bytedata, TxFlags.Unset, None)) with SynchronousWriter() as writer: self.store.create(to_add, completion_callback=writer.get_callback()) assert writer.succeeded() add_hashes = set(t[0] for t in to_add) get_hashes = set(self._get_store_hashes()) assert add_hashes == get_hashes with SynchronousWriter() as writer: self.store.delete(add_hashes, completion_callback=writer.get_callback()) assert writer.succeeded() get_hashes = self._get_store_hashes() assert 0 == len(get_hashes)
def test_update__with_valid_magic_bytedata(self): tx_bytes = os.urandom(10) tx_hash = bitcoinx.double_sha256(tx_bytes) tx_data = TxData(height=None, fee=2, position=None, date_added=1, date_updated=1) row = (tx_hash, tx_data, tx_bytes, TxFlags.HasByteData, None) with SynchronousWriter() as writer: self.store.create([row], completion_callback=writer.get_callback()) assert writer.succeeded() # Ensure that with SynchronousWriter() as writer: self.store.update([(tx_hash, tx_data, MAGIC_UNTOUCHED_BYTEDATA, TxFlags.HasByteData)], completion_callback=writer.get_callback()) assert writer.succeeded() rows = self.store.read() assert 1 == len(rows) get_tx_hash, bytedata_get, flags_get, metadata_get = rows[0] assert tx_bytes == bytedata_get assert flags_get & TxFlags.HasByteData != 0
def test_read(self): to_add = [] for i in range(10): tx_bytes = os.urandom(10) tx_hash = bitcoinx.double_sha256(tx_bytes) tx_data = TxData(height=None, fee=2, position=None, date_added=1, date_updated=1) to_add.append((tx_hash, tx_data, tx_bytes, TxFlags.HasFee, None)) with SynchronousWriter() as writer: self.store.create(to_add, completion_callback=writer.get_callback()) assert writer.succeeded() # Test the first "add" hash is matched. matches = self.store.read(tx_hashes=[to_add[0][0]]) assert to_add[0][0] == matches[0][0] # Test no id is matched. matches = self.store.read(tx_hashes=[b"aaaa"]) assert 0 == len(matches) # Test flag and mask combinations. matches = self.store.read(flags=TxFlags.HasFee) assert 10 == len(matches) matches = self.store.read(flags=TxFlags.Unset, mask=TxFlags.HasHeight) assert 10 == len(matches) matches = self.store.read(flags=TxFlags.HasFee, mask=TxFlags.HasFee) assert 10 == len(matches) matches = self.store.read(flags=TxFlags.Unset, mask=TxFlags.HasFee) assert 0 == len(matches)
def test_get_entry_cached_already(self) -> None: metadata = TxData(position=11, date_added=1, date_updated=1) flags = TxFlags.HasPosition def _read(*args, **kwargs) -> Tuple[bytes, Optional[bytes], TxFlags, TxData]: nonlocal metadata, flags return [(b"tx_hash", None, flags, metadata)] def _read_metadata(*args, **kwargs) -> Tuple[bytes, TxFlags, TxData]: nonlocal metadata, flags return [(b"tx_hash", flags, metadata)] mock_store = MockTransactionStore() mock_store.read = _read mock_store.read_metadata = _read_metadata cache = TransactionCache(mock_store, 0) # Verify that we do not hit the store for our cached entry. our_entry = TransactionCacheEntry(metadata, TxFlags.HasPosition) cache._cache[b"tx_hash"] = our_entry their_entry = cache.get_entry(b"tx_hash") assert our_entry.metadata == their_entry.metadata assert our_entry.flags == their_entry.flags
def test_get_transaction_after_metadata(self): # Getting an entry for a settled transaction should update from metadata-only to full. bytedata_set = bytes.fromhex(tx_hex_1) tx_hash = bitcoinx.double_sha256(bytedata_set) metadata_set = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) with SynchronousWriter() as writer: self.store.create([ (tx_hash, metadata_set, bytedata_set, TxFlags.StateSettled, None) ], completion_callback=writer.get_callback()) assert writer.succeeded() cache = TransactionCache(self.store) metadata_get = cache.get_metadata(tx_hash) assert metadata_get is not None # Initial priming of cache will be only metadata. cached_entry_1 = cache._cache[tx_hash] assert not cache.have_transaction_data_cached(tx_hash) # Entry request will hit the database. entry = cache.get_entry(tx_hash) assert cache.have_transaction_data_cached(tx_hash) cached_entry_2 = cache._cache[tx_hash] assert entry.metadata == cached_entry_2.metadata assert entry.flags == cached_entry_2.flags
def test_get_many(self): to_add = [] for i in range(10): tx_bytes = os.urandom(10) tx_hash_bytes = bitcoinx.double_sha256(tx_bytes) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) tx_data = TxData(height=None, fee=2, position=None, timestamp=None) to_add.append((tx_id, tx_data, tx_bytes, TxFlags.HasFee)) self.store.add_many(to_add) # Test the first "add" id is matched. matches = self.store.get_many(tx_ids=[to_add[0][0]]) self.assertEqual(to_add[0][0], matches[0][0]) # Test no id is matched. matches = self.store.get_many(tx_ids=["aaaa"]) self.assertEqual(0, len(matches)) # Test flag and mask combinations. matches = self.store.get_many(flags=TxFlags.HasFee) self.assertEqual(10, len(matches)) matches = self.store.get_many(flags=TxFlags.Unset, mask=TxFlags.HasHeight) self.assertEqual(10, len(matches)) matches = self.store.get_many(flags=TxFlags.HasFee, mask=TxFlags.HasFee) self.assertEqual(10, len(matches)) matches = self.store.get_many(flags=TxFlags.Unset, mask=TxFlags.HasFee) self.assertEqual(0, len(matches))
def test_update_flags(self): cache = TxCache(self.store) tx_bytes_1 = bytes.fromhex(tx_hex_1) tx_hash_bytes_1 = bitcoinx.double_sha256(tx_bytes_1) tx_id_1 = bitcoinx.hash_to_hex_str(tx_hash_bytes_1) data = TxData(position=11) cache.add([(tx_id_1, data, tx_bytes_1, TxFlags.StateDispatched)]) self.assertTrue(cache.is_cached(tx_id_1)) entry = cache.get_entry(tx_id_1) self.assertEqual( TxFlags.HasByteData | TxFlags.HasPosition | TxFlags.StateDispatched, entry.flags) self.assertIsNotNone(entry.bytedata) cache.update_flags(tx_id_1, TxFlags.StateCleared, TxFlags.HasByteData | TxFlags.HasProofData) entry = cache.get_entry(tx_id_1) store_flags = self.store.get_flags(tx_id_1) expected_flags = TxFlags.HasByteData | TxFlags.HasPosition | TxFlags.StateCleared self.assertEqual( expected_flags, store_flags, f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(store_flags)}" ) self.assertEqual( expected_flags, entry.flags, f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(entry.flags)}" ) self.assertIsNotNone(entry.bytedata)
def test_update_flags(self): bytedata = os.urandom(10) tx_hash_bytes = bitcoinx.double_sha256(bytedata) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) metadata = TxData(height=1, fee=2, position=None, timestamp=None) self.store.add(tx_id, metadata, bytedata) # Verify the field flags are assigned correctly on the add. expected_flags = TxFlags.HasFee | TxFlags.HasHeight | TxFlags.HasByteData flags = self.store.get_flags(tx_id) self.assertEqual(expected_flags, flags) flags = TxFlags.StateReceived mask = TxFlags.METADATA_FIELD_MASK | TxFlags.HasByteData | TxFlags.HasProofData self.store.update_flags(tx_id, flags, mask) # Verify the state flag is correctly added via the mask. flags_get = self.store.get_flags(tx_id) expected_flags |= TxFlags.StateReceived self.assertEqual( expected_flags, flags_get, f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(flags_get)}" ) flags = TxFlags.StateReceived mask = TxFlags.Unset self.store.update_flags(tx_id, flags, mask) # Verify the state flag is correctly set via the mask. flags = self.store.get_flags(tx_id) self.assertEqual(TxFlags.StateReceived, flags)
def test_get_unsynced_ids(self): cache = TxCache(self.store) bytedata_1 = bytes.fromhex(tx_hex_1) tx_hash_bytes_1 = bitcoinx.double_sha256(bytedata_1) tx_id_1 = bitcoinx.hash_to_hex_str(tx_hash_bytes_1) metadata_1 = TxData(height=11) cache.add([(tx_id_1, metadata_1, None, TxFlags.StateCleared)]) results = cache.get_unsynced_ids() self.assertEqual(1, len(results)) metadata_2 = TxData() cache.update([(tx_id_1, metadata_2, bytedata_1, TxFlags.HasByteData)]) results = cache.get_unsynced_ids() self.assertEqual(0, len(results))
def test_labels(self): bytedata_1 = os.urandom(10) tx_hash_1 = bitcoinx.double_sha256(bytedata_1) metadata_1 = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) bytedata_2 = os.urandom(10) tx_hash_2 = bitcoinx.double_sha256(bytedata_2) metadata_2 = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) with SynchronousWriter() as writer: self.store.create([(tx_hash_1, metadata_1, bytedata_1, 0, None), (tx_hash_2, metadata_2, bytedata_2, 0, None)], completion_callback=writer.get_callback()) assert writer.succeeded() with SynchronousWriter() as writer: self.store.update_descriptions( [("tx 1", tx_hash_1)], completion_callback=writer.get_callback()) assert writer.succeeded() rows = self.store.read_descriptions() assert len(rows) == 1 assert len([r[1] == "tx 1" for r in rows if r[0] == tx_hash_1]) == 1 with SynchronousWriter() as writer: self.store.update_descriptions( [(None, tx_hash_1), ("tx 2", tx_hash_2)], completion_callback=writer.get_callback()) assert writer.succeeded() rows = self.store.read_descriptions([tx_hash_2]) assert len(rows) == 1 assert rows[0][0] == tx_hash_2 and rows[0][1] == "tx 2" # Reading entries for a non-existent ... rows = self.store.read_descriptions([self.tx_hash]) assert len(rows) == 0
def test_delete(self): tx_bytes = os.urandom(10) tx_hash_bytes = bitcoinx.double_sha256(tx_bytes) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) data = TxData(height=1, fee=2, position=None, timestamp=None) self.store.add(tx_id, data, tx_bytes) self.assertTrue(self.store.has(tx_id)) self.store.delete(tx_id) self.assertFalse(self.store.has(tx_id))
def test_update(self): bytedata = os.urandom(10) tx_hash_bytes = bitcoinx.double_sha256(bytedata) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) metadata_a = TxData(height=None, fee=None, position=None, timestamp=None) self.store.add(tx_id, metadata_a, bytedata) metadata_update = TxData(height=None, fee=100, position=None, timestamp=None) self.store.update(tx_id, metadata_update, bytedata) metadata_get, bytedata_get, flags = self.store.get(tx_id) self.assertEqual(metadata_update, metadata_get) self.assertEqual(bytedata, bytedata_get)
def test_data_unpack_version_1(self): for hex, data, flags in [ [ "0101020000", TxData(height=1, fee=2), TxFlags.HasFee | TxFlags.HasHeight ], [ "0200026efd4d04", TxData(height=-1, fee=2, position=110, timestamp=1101), TxFlags.HasFee | TxFlags.HasHeight | TxFlags.HasPosition | TxFlags.HasTimestamp ], ]: raw = bytes.fromhex(hex) unpacked_data = self.store._unpack_data(raw, flags) self.assertEqual(data.height, unpacked_data.height) self.assertEqual(data.fee, unpacked_data.fee) self.assertEqual(data.position, unpacked_data.position) self.assertEqual(data.timestamp, unpacked_data.timestamp)
def test_get_unverified_entries_too_high(self): cache = TxCache(self.store) tx_bytes_1 = bytes.fromhex(tx_hex_1) tx_hash_bytes_1 = bitcoinx.double_sha256(tx_bytes_1) tx_id_1 = bitcoinx.hash_to_hex_str(tx_hash_bytes_1) data = TxData(height=11, position=22) cache.add([(tx_id_1, data, tx_bytes_1, TxFlags.StateCleared)]) results = cache.get_unverified_entries(100) self.assertEqual(0, len(results))
def test_get_transaction(self): bytedata = bytes.fromhex(tx_hex_1) tx_hash_bytes = bitcoinx.double_sha256(bytedata) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) metadata = TxData(height=1, fee=2, position=None, timestamp=None) self.store.add(tx_id, metadata, bytedata) cache = TxCache(self.store) tx = cache.get_transaction(tx_id) self.assertIsNotNone(tx) self.assertEqual(tx_id, tx.txid())
def test_get_all_pending(self): get_tx_ids = set([]) for tx_hex in (tx_hex_1, tx_hex_2): bytedata = bytes.fromhex(tx_hex) tx_hash_bytes = bitcoinx.double_sha256(bytedata) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) metadata = TxData(height=1, fee=2, position=None, timestamp=None) self.store.add(tx_id, metadata, bytedata) get_tx_ids.add(tx_id) result_tx_ids = self.store.get_ids() self.assertEqual(get_tx_ids, result_tx_ids)
def test_update__entry_with_magic_bytedata_and_set_flag(self): tx_bytes = os.urandom(10) tx_hash = bitcoinx.double_sha256(tx_bytes) tx_data = TxData(height=None, fee=2, position=None, date_added=1, date_updated=1) row = (tx_hash, tx_data, tx_bytes, TxFlags.HasByteData, None) with SynchronousWriter() as writer: self.store.create([ row ], completion_callback=writer.get_callback()) assert writer.succeeded() # Ensure that the magic bytedata requires a set bytedata flag. with pytest.raises(AssertionError): self.store.update([(tx_hash, tx_data, MAGIC_UNTOUCHED_BYTEDATA, TxFlags.Unset)])
def test_get(self): bytedata = os.urandom(10) tx_hash = bitcoinx.double_sha256(bytedata) metadata = TxData(height=1, fee=2, position=None, date_added=1, date_updated=1) with SynchronousWriter() as writer: self.store.create([ (tx_hash, metadata, bytedata, TxFlags.Unset, None) ], completion_callback=writer.get_callback()) assert writer.succeeded() assert tx_hash in self._get_store_hashes() assert self.store.read(tx_hashes=[tx_hash]) assert self.store.read(TxFlags.HasByteData, TxFlags.HasByteData, [tx_hash])
def test_add_many(self): to_add = [] for i in range(10): tx_bytes = os.urandom(10) tx_hash_bytes = bitcoinx.double_sha256(tx_bytes) tx_id = bitcoinx.hash_to_hex_str(tx_hash_bytes) tx_data = TxData(height=1, fee=2, position=None, timestamp=None) to_add.append((tx_id, tx_data, tx_bytes, TxFlags.Unset)) self.store.add_many(to_add) existing_tx_ids = self.store.get_ids() added_tx_ids = set(t[0] for t in to_add) self.assertEqual(added_tx_ids, existing_tx_ids)
def test_update__entry_with_set_bytedata_flag(self): tx_bytes = os.urandom(10) tx_hash = bitcoinx.double_sha256(tx_bytes) tx_data = TxData(height=None, fee=2, position=None, date_added=1, date_updated=1) row = (tx_hash, tx_data, tx_bytes, TxFlags.HasByteData, None) with SynchronousWriter() as writer: self.store.create([ row ], completion_callback=writer.get_callback()) assert writer.succeeded() # Ensure that a set bytedata flag requires bytedata to be included. with pytest.raises(AssertionError): self.store.update([(tx_hash, tx_data, None, TxFlags.HasByteData)])
def test_add_then_update(self): cache = TransactionCache(self.store) bytedata_1 = bytes.fromhex(tx_hex_1) tx_hash_1 = bitcoinx.double_sha256(bytedata_1) metadata_1 = TxData(position=11) with SynchronousWriter() as writer: cache.add([(tx_hash_1, metadata_1, bytedata_1, TxFlags.StateDispatched, None)], completion_callback=writer.get_callback()) assert writer.succeeded() assert cache.is_cached(tx_hash_1) entry = cache.get_entry(tx_hash_1) assert TxFlags.HasByteData | TxFlags.HasPosition | TxFlags.StateDispatched == entry.flags assert cache.have_transaction_data_cached(tx_hash_1) # NOTE: We are not updating bytedata, and it should remain the same. The flags we pass # into update are treated specially to achieve this. metadata_2 = TxData(fee=10, height=88) propagate_flags = TxFlags.HasFee | TxFlags.HasHeight with SynchronousWriter() as writer: cache.update([(tx_hash_1, metadata_2, None, propagate_flags | TxFlags.HasPosition)], completion_callback=writer.get_callback()) assert writer.succeeded() # Check the cache to see that the flags are correct and that bytedata is cached. entry = cache.get_entry(tx_hash_1) expected_flags = propagate_flags | TxFlags.StateDispatched | TxFlags.HasByteData assert expected_flags == entry.flags, \ f"{TxFlags.to_repr(expected_flags)} != {TxFlags.to_repr(entry.flags)}" assert cache.have_transaction_data_cached(tx_hash_1) # Check the store to see that the flags are correct and the bytedata is retained. rows = self.store.read(tx_hashes=[tx_hash_1]) assert 1 == len(rows) get_tx_hash, bytedata_get, flags_get, metadata_get = rows[0] assert bytedata_1 == bytedata_get assert flags_get & TxFlags.HasByteData != 0