def test_reorg_finney_attack(): ###an unconfirmed tx being broadcast, another conflicting tx being ### confirmed, the first tx gets conflicted status dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx( confirmations=0) dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx( confirmations=0, input_txid=dummy_tx1["vin"][0]) #two unconfirmed txes spending the same input, so they are in conflict rpc = DummyJsonRpc([dummy_tx1], [dummy_tx1["vin"][0]], {dummy_tx1["blockhash"]: containing_block_height1, dummy_tx2["blockhash"]: containing_block_height2}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1, dummy_spk2]) assert len(txmonitor.address_history) == 2 sh1 = script_to_scripthash(dummy_spk1) sh2 = script_to_scripthash(dummy_spk2) assert len(txmonitor.get_electrum_history(sh1)) == 1 assert len(txmonitor.get_electrum_history(sh2)) == 0 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1, height=0, txid=dummy_tx1["txid"], subscribed=False) # a conflicting transaction confirms rpc.add_transaction(dummy_tx2) dummy_tx1["confirmations"] = -1 dummy_tx2["confirmations"] = 1 assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history(sh1)) == 0 assert len(txmonitor.get_electrum_history(sh2)) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2, height=containing_block_height2, txid=dummy_tx2["txid"], subscribed=False)
def test_overrun_gap_limit(): ###overrun gap limit so import address is needed dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx() dummy_spk_imported = create_dummy_spk() class DummyImportDeterministicWallet(DeterministicWallet): def __init__(self): pass def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys): return {0: 1} #overrun by one def get_new_scriptpubkeys(self, change, count): return [dummy_spk_imported] rpc = DummyJsonRpc([], [], {dummy_tx["blockhash"]: containing_block_height}) txmonitor = TransactionMonitor(rpc, [DummyImportDeterministicWallet()], debugf, logf) assert txmonitor.build_address_history([dummy_spk]) assert len(txmonitor.address_history) == 1 assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history( script_to_scripthash(dummy_spk))) == 0 rpc.add_transaction(dummy_tx) assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history( script_to_scripthash(dummy_spk))) == 1 assert len( txmonitor.get_electrum_history( script_to_scripthash(dummy_spk_imported))) == 0 assert len(rpc.get_imported_addresses()) == 1 assert rpc.get_imported_addresses()[0] == dummy_spk_to_address( dummy_spk_imported)
def test_reorg_race_attack(): #a tx is confirmed, a chain reorganization happens and that tx is replaced # by another tx spending the same input, the original tx is now conflicted dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx() dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx( input_txid=dummy_tx1["vin"][0]) rpc = DummyJsonRpc([dummy_tx1], [], {dummy_tx1["blockhash"]: containing_block_height1, dummy_tx2["blockhash"]: containing_block_height2}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1, dummy_spk2]) assert len(txmonitor.address_history) == 2 sh1 = script_to_scripthash(dummy_spk1) sh2 = script_to_scripthash(dummy_spk2) assert len(txmonitor.get_electrum_history(sh1)) == 1 assert len(txmonitor.get_electrum_history(sh2)) == 0 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1, height=containing_block_height1, txid=dummy_tx1["txid"], subscribed=False) #race attack happens #dummy_tx1 goes to -1 confirmations, dummy_tx2 gets confirmed rpc.add_transaction(dummy_tx2) dummy_tx1["confirmations"] = -1 dummy_tx2["confirmations"] = 1 assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history(sh1)) == 0 assert len(txmonitor.get_electrum_history(sh2)) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2, height=containing_block_height2, txid=dummy_tx2["txid"], subscribed=False)
def test_conflicted_tx(): ###conflicted transaction should get rejected dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx( confirmations=-1) rpc = DummyJsonRpc([dummy_tx], [], {}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk]) assert len(txmonitor.address_history) == 1 assert len(txmonitor.get_electrum_history(script_to_scripthash( dummy_spk))) == 0 #shouldnt show up after build history b/c conflicted rpc.add_transaction(dummy_tx) assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history( script_to_scripthash(dummy_spk))) == 0 #incoming tx is not added too
def test_tx_within_wallet(): ###transaction from one address to the other, both addresses in wallet dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx() dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx( input_txid=dummy_tx1["txid"]) rpc = DummyJsonRpc([dummy_tx1, dummy_tx2], [], {dummy_tx1["blockhash"]: containing_block_height1, dummy_tx2["blockhash"]: containing_block_height2}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1, dummy_spk2]) assert len(txmonitor.get_electrum_history(script_to_scripthash( dummy_spk1))) == 2 assert len(txmonitor.get_electrum_history(script_to_scripthash( dummy_spk2))) == 1
def test_tx_arrival_then_confirmation(): ###build empty address history, subscribe one address ### an unconfirmed tx appears, then confirms dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx( confirmations=0) rpc = DummyJsonRpc([], [dummy_tx["vin"][0]], {dummy_tx["blockhash"]: containing_block_height}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk]) assert len(txmonitor.address_history) == 1 sh = script_to_scripthash(dummy_spk) assert len(txmonitor.get_electrum_history(sh)) == 0 txmonitor.subscribe_address(sh) # unconfirm transaction appears assert len(list(txmonitor.check_for_updated_txes())) == 0 rpc.add_transaction(dummy_tx) assert len(list(txmonitor.check_for_updated_txes())) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk, height=0, txid=dummy_tx["txid"], subscribed=True) # transaction confirms dummy_tx["confirmations"] = 1 assert len(list(txmonitor.check_for_updated_txes())) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk, height=containing_block_height, txid=dummy_tx["txid"], subscribed=True)
def test_address_reuse(): ###transaction which arrives to an address which already has a tx on it dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx() dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx( output_spk=dummy_spk1) rpc = DummyJsonRpc([dummy_tx1], [], {dummy_tx1["blockhash"]: containing_block_height1, dummy_tx2["blockhash"]: containing_block_height2}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1]) sh = script_to_scripthash(dummy_spk1) assert len(txmonitor.get_electrum_history(sh)) == 1 rpc.add_transaction(dummy_tx2) assert len(txmonitor.get_electrum_history(sh)) == 1 txmonitor.check_for_updated_txes() assert len(txmonitor.get_electrum_history(sh)) == 2
def test_reorg_censor_tx(): #confirmed tx gets reorgd out and becomes unconfirmed dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx() rpc = DummyJsonRpc([dummy_tx1], [dummy_tx1["vin"][0]], {dummy_tx1["blockhash"]: containing_block_height1}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1]) assert len(txmonitor.address_history) == 1 sh = script_to_scripthash(dummy_spk1) assert len(txmonitor.get_electrum_history(sh)) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1, height=containing_block_height1, txid=dummy_tx1["txid"], subscribed=False) #blocks appear which reorg out the tx, making it unconfirmed dummy_tx1["confirmations"] = 0 assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history(sh)) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1, height=0, txid=dummy_tx1["txid"], subscribed=False)
def test_duplicate_txid(): ###two txes with the same txid, built history dummy_spk, containing_block_height1, dummy_tx1 = create_dummy_funding_tx() dummy_spk, containing_block_height2, dummy_tx2 = create_dummy_funding_tx( output_spk=dummy_spk) dummy_spk, containing_block_height3, dummy_tx3 = create_dummy_funding_tx( output_spk=dummy_spk) dummy_tx2["txid"] = dummy_tx1["txid"] dummy_tx3["txid"] = dummy_tx1["txid"] sh = script_to_scripthash(dummy_spk) rpc = DummyJsonRpc([dummy_tx1, dummy_tx2], [], {dummy_tx1["blockhash"]: containing_block_height1, dummy_tx2["blockhash"]: containing_block_height2, dummy_tx3["blockhash"]: containing_block_height3}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk]) assert len(txmonitor.get_electrum_history(sh)) == 1 txmonitor.subscribe_address(sh) assert txmonitor.get_electrum_history(sh)[0]["tx_hash"] == dummy_tx1["txid"] rpc.add_transaction(dummy_tx3) assert len(list(txmonitor.check_for_updated_txes())) == 1 assert len(txmonitor.get_electrum_history(sh)) == 1 assert txmonitor.get_electrum_history(sh)[0]["tx_hash"] == dummy_tx1["txid"]
def test_from_address(): ###transaction spending FROM one of our addresses dummy_spk1, containing_block_height1, input_tx = create_dummy_funding_tx() dummy_spk2, containing_block_height2, spending_tx = create_dummy_funding_tx( input_txid=input_tx["txid"]) rpc = DummyJsonRpc([input_tx, spending_tx], [], {input_tx["blockhash"]: containing_block_height1, spending_tx["blockhash"]: containing_block_height2}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1]) sh = script_to_scripthash(dummy_spk1) assert len(txmonitor.get_electrum_history(sh)) == 2
def test_unrelated_tx(): ###transaction that has nothing to do with our wallet dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx( confirmations=0) our_dummy_spk = create_dummy_spk() rpc = DummyJsonRpc([dummy_tx], [], {dummy_tx["blockhash"]: containing_block_height}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([our_dummy_spk]) assert len(txmonitor.address_history) == 1 assert len(txmonitor.get_electrum_history(script_to_scripthash( our_dummy_spk))) == 0
def test_reorg_different_block(): #confirmed tx gets reorged into another block with a different height dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx() dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx() rpc = DummyJsonRpc([dummy_tx1], [], {dummy_tx1["blockhash"]: containing_block_height1, dummy_tx2["blockhash"]: containing_block_height2}) txmonitor = TransactionMonitor(rpc, deterministic_wallets, debugf, logf) assert txmonitor.build_address_history([dummy_spk1]) assert len(txmonitor.address_history) == 1 sh = script_to_scripthash(dummy_spk1) assert len(txmonitor.get_electrum_history(sh)) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1, height=containing_block_height1, txid=dummy_tx1["txid"], subscribed=False) #tx gets reorged into another block (so still confirmed) dummy_tx1["blockhash"] = dummy_tx2["blockhash"] assert len(list(txmonitor.check_for_updated_txes())) == 0 assert len(txmonitor.get_electrum_history(sh)) == 1 assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1, height=containing_block_height2, txid=dummy_tx1["txid"], subscribed=False)