electrum-personal-server

Maximally lightweight electrum server for a single user
git clone https://git.parazyd.org/electrum-personal-server
Log | Files | Refs | README

test_transactionmonitor.py (29287B)


      1 
      2 import pytest
      3 import logging
      4 
      5 from electrumpersonalserver.server import (
      6     DeterministicWallet,
      7     TransactionMonitor,
      8     JsonRpcError,
      9     script_to_scripthash
     10 )
     11 
     12 logger = logging.getLogger('ELECTRUMPERSONALSERVER-TEST')
     13 logger.setLevel(logging.DEBUG)
     14 
     15 class DummyJsonRpc(object):
     16     """
     17     Electrum Personal Server gets all its information about the bitcoin network
     18     from the json-rpc interface. This dummy interface is used for simulating
     19     events in bitcoin
     20     """
     21     def __init__(self, txlist, utxoset, block_heights):
     22         self.txlist = txlist
     23         self.utxoset = utxoset
     24         self.block_heights = block_heights
     25         self.imported_addresses = []
     26 
     27     def call(self, method, params):
     28         if method == "listtransactions":
     29             count = int(params[1])
     30             skip = int(params[2])
     31             return self.txlist[skip:skip + count][::-1]
     32         elif method == "gettransaction":
     33             for t in self.txlist:
     34                 if t["txid"] == params[0]:
     35                     return t
     36             raise JsonRpcError()
     37         elif method == "decoderawtransaction":
     38             for t in self.txlist:
     39                 if t["hex"] == params[0]:
     40                     return t
     41             logger.debug(params[0])
     42             assert 0
     43         elif method == "gettxout":
     44             for u in self.utxoset:
     45                 if u["txid"] == params[0] and u["vout"] == params[1]:
     46                     return u
     47             logger.debug("txid = " + params[0] + " vout = " + str(params[1]))
     48             assert 0
     49         elif method == "getblockheader":
     50             if params[0] in self.block_heights:
     51                 return {"height": self.block_heights[params[0]]}
     52             logger.debug(params[0])
     53             assert 0
     54         elif method == "decodescript":
     55             return {"addresses": [dummy_spk_to_address(params[0])]}
     56         elif method == "importaddress":
     57             self.imported_addresses.append(params[0])
     58         elif method == "getmempoolentry":
     59             for t in self.txlist:
     60                 if t["txid"] == params[0]:
     61                     return {"fees": {"base": 0},
     62                         "ancestorcount":
     63                             1 if t["vin"][0]["confirmations"] > 0 else 2}
     64             logger.debug(params[0])
     65             assert 0
     66         else:
     67             raise ValueError("unknown method in dummy jsonrpc " + method)
     68 
     69     def add_transaction(self, tx):
     70         self.txlist = [tx] + self.txlist
     71 
     72     def get_imported_addresses(self):
     73         return self.imported_addresses
     74 
     75 
     76 class DummyDeterministicWallet(DeterministicWallet):
     77     """Empty deterministic wallets"""
     78     def __init__(self):
     79         pass
     80 
     81     def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
     82         return None #not overrun
     83 
     84     def get_new_scriptpubkeys(self, change, count):
     85         pass
     86 
     87 
     88 def dummy_spk_to_address(spk):
     89     ##spk is short for scriptPubKey
     90     return spk + "-address"
     91 
     92 deterministic_wallets = [DummyDeterministicWallet()]
     93 dummy_id_g = [1000]
     94 
     95 def create_dummy_spk(): #script pub key
     96     dummy_id = dummy_id_g[0]
     97     dummy_id_g[0] += 1
     98     return "deadbeef" + str(dummy_id)
     99 
    100 def create_dummy_funding_tx(confirmations=1, output_spk=None,
    101         input_txid="placeholder-unknown-input-txid", coinbase=False,
    102         input_confirmations=1):
    103     dummy_id = dummy_id_g[0]
    104     dummy_id_g[0] += 1
    105 
    106     if output_spk == None:
    107         dummy_spk = "deadbeef" + str(dummy_id) #scriptpubkey
    108     else:
    109         dummy_spk = output_spk
    110     dummy_containing_block = "blockhash-placeholder" + str(dummy_id)
    111     containing_block_height = dummy_id
    112     category = "receive"
    113     vin = [{"txid": input_txid, "vout": 0, "value": 1,
    114             "confirmations": input_confirmations}]
    115     if coinbase:
    116         vin = [{"coinbase": "nonce"}]
    117         if confirmations < 1:
    118             category = "orphan"
    119         elif confirmations <= 100:
    120             category = "immature"
    121         else:
    122             category = "generate"
    123     dummy_tx = {
    124         "txid": "placeholder-test-txid" + str(dummy_id),
    125         "vin": vin,
    126         "vout": [{"value": 1, "scriptPubKey": {"hex": dummy_spk}}],
    127         "address": dummy_spk_to_address(dummy_spk),
    128         "category": category,
    129         "confirmations": confirmations,
    130         "blockhash": dummy_containing_block,
    131         "hex": "placeholder-test-txhex" + str(dummy_id)
    132     }
    133     logger.debug("created dummy tx: " + str(dummy_tx))
    134     return dummy_spk, containing_block_height, dummy_tx
    135 
    136 def assert_address_history_tx(address_history, spk, height, txid, subscribed):
    137     history_element = address_history[script_to_scripthash(spk)]
    138     assert history_element["history"][0]["height"] == height
    139     assert history_element["history"][0]["tx_hash"] == txid
    140     #fee always zero, its easier to test because otherwise you have
    141     # to use Decimal to stop float weirdness
    142     if height == 0:
    143         assert history_element["history"][0]["fee"] == 0
    144     assert history_element["subscribed"] == subscribed
    145 
    146 def test_single_tx():
    147     ###single confirmed tx in wallet belonging to us, address history built
    148     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx()
    149 
    150     rpc = DummyJsonRpc([dummy_tx], [],
    151         {dummy_tx["blockhash"]: containing_block_height})
    152     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    153     assert txmonitor.build_address_history([dummy_spk])
    154     assert len(txmonitor.address_history) == 1
    155     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk,
    156         height=containing_block_height, txid=dummy_tx["txid"], subscribed=False)
    157 
    158 def test_two_txes():
    159     ###two confirmed txes in wallet belonging to us, addr history built
    160     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    161     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx()
    162 
    163     rpc = DummyJsonRpc([dummy_tx1, dummy_tx2], [],
    164         {dummy_tx1["blockhash"]: containing_block_height1,
    165         dummy_tx2["blockhash"]: containing_block_height2})
    166     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    167     assert txmonitor.build_address_history([dummy_spk1, dummy_spk2])
    168     assert len(txmonitor.address_history) == 2
    169     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    170         height=containing_block_height1, txid=dummy_tx1["txid"],
    171         subscribed=False)
    172     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2,
    173         height=containing_block_height2, txid=dummy_tx2["txid"],
    174         subscribed=False)
    175 
    176 def test_coinbase_txs():
    177     ###two coinbase txs (mature and immature) in wallet, addr history built
    178     ## two more coinbase txs added, addr history updated
    179     ## orphaned coinbase txs not added to addr history
    180     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx(
    181         coinbase=True, confirmations=1)
    182     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    183         coinbase=True, confirmations=101)
    184     dummy_spk3, containing_block_height3, dummy_tx3 = create_dummy_funding_tx(
    185         coinbase=True, confirmations=0)
    186     dummy_spk4, containing_block_height4, dummy_tx4 = create_dummy_funding_tx(
    187         coinbase=True, confirmations=1)
    188     dummy_spk5, containing_block_height5, dummy_tx5 = create_dummy_funding_tx(
    189         coinbase=True, confirmations=101)
    190     dummy_spk6, containing_block_height6, dummy_tx6 = create_dummy_funding_tx(
    191         coinbase=True, confirmations=0)
    192 
    193     rpc = DummyJsonRpc([dummy_tx1, dummy_tx2, dummy_tx3], [],
    194                        {dummy_tx1["blockhash"]: containing_block_height1,
    195                         dummy_tx2["blockhash"]: containing_block_height2,
    196                         dummy_tx3["blockhash"]: containing_block_height3,
    197                         dummy_tx4["blockhash"]: containing_block_height4,
    198                         dummy_tx5["blockhash"]: containing_block_height5,
    199                         dummy_tx6["blockhash"]: containing_block_height6})
    200     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    201     assert txmonitor.build_address_history([dummy_spk1, dummy_spk2, dummy_spk3,
    202                                             dummy_spk4, dummy_spk5, dummy_spk6])
    203     assert len(txmonitor.address_history) == 6
    204     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    205         height=containing_block_height1, txid=dummy_tx1["txid"],
    206         subscribed=False)
    207     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2,
    208         height=containing_block_height2, txid=dummy_tx2["txid"],
    209         subscribed=False)
    210     sh3 = script_to_scripthash(dummy_spk3)
    211     assert len(txmonitor.get_electrum_history(sh3)) == 0
    212 
    213     rpc.add_transaction(dummy_tx4)
    214     rpc.add_transaction(dummy_tx5)
    215     rpc.add_transaction(dummy_tx6)
    216     assert len(list(txmonitor.check_for_updated_txes())) == 0
    217     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk4,
    218         height=containing_block_height4, txid=dummy_tx4["txid"],
    219         subscribed=False)
    220     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk5,
    221         height=containing_block_height5, txid=dummy_tx5["txid"],
    222         subscribed=False)
    223     sh6 = script_to_scripthash(dummy_spk6)
    224     assert len(txmonitor.get_electrum_history(sh6)) == 0
    225 
    226     #test orphan tx is removed from history
    227     dummy_tx1["confirmations"] = 0
    228     dummy_tx1["category"] = "orphan"
    229     assert len(list(txmonitor.check_for_updated_txes())) == 0
    230     sh1 = script_to_scripthash(dummy_spk1)
    231     assert len(txmonitor.get_electrum_history(sh1)) == 0
    232 
    233 def test_many_txes():
    234     ##many txes in wallet and many more added,, intended to test the loop
    235     ## in build_addr_history and check_for_new_txes()
    236     input_spk, input_block_height1, input_tx = create_dummy_funding_tx()
    237     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx(
    238         confirmations=0, input_txid=input_tx["vin"][0])
    239     sh = script_to_scripthash(dummy_spk)
    240 
    241     #batch size is 1000
    242     INITIAL_TX_COUNT = 1100
    243     txes = [dummy_tx]
    244     #0confirm to avoid having to obtain block hash
    245     txes.extend( (create_dummy_funding_tx(output_spk=dummy_spk,
    246         input_txid=input_tx["vin"][0], confirmations=0)[2]
    247         for i in range(INITIAL_TX_COUNT-1)) )
    248     assert len(txes) == INITIAL_TX_COUNT
    249 
    250     rpc = DummyJsonRpc(txes, [dummy_tx["vin"][0]], {})
    251     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    252     assert txmonitor.build_address_history([dummy_spk])
    253     assert len(txmonitor.address_history) == 1
    254     assert len(list(txmonitor.check_for_updated_txes())) == 0
    255     assert len(txmonitor.address_history[sh]["history"]) == INITIAL_TX_COUNT
    256 
    257     ADDED_TX_COUNT = 130
    258     new_txes = []
    259     new_txes.extend( (create_dummy_funding_tx(output_spk=dummy_spk,
    260         input_txid=input_tx["vin"][0], confirmations=0)[2]
    261         for i in range(ADDED_TX_COUNT)) )
    262 
    263     for tx in new_txes:
    264         rpc.add_transaction(tx)
    265     assert len(list(txmonitor.check_for_updated_txes())) == 0
    266     assert len(txmonitor.address_history[sh]["history"]) == (INITIAL_TX_COUNT
    267         + ADDED_TX_COUNT)
    268 
    269 def test_non_subscribed_confirmation():
    270     ###one unconfirmed tx in wallet belonging to us, with confirmed inputs,
    271     ### addr history built, then tx confirms, not subscribed to address
    272     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx(
    273         confirmations=0)
    274 
    275     rpc = DummyJsonRpc([dummy_tx], [dummy_tx["vin"][0]],
    276         {dummy_tx["blockhash"]: containing_block_height})
    277     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    278     assert txmonitor.build_address_history([dummy_spk])
    279     assert len(txmonitor.address_history) == 1
    280     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk,
    281         height=0, txid=dummy_tx["txid"], subscribed=False)
    282     assert len(list(txmonitor.check_for_updated_txes())) == 0
    283     dummy_tx["confirmations"] = 1 #tx confirms
    284     #not subscribed so still only returns an empty list
    285     assert len(list(txmonitor.check_for_updated_txes())) == 0
    286     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk,
    287         height=containing_block_height, txid=dummy_tx["txid"], subscribed=False)
    288 
    289 def test_tx_arrival_then_confirmation():
    290     ###build empty address history, subscribe one address
    291     ### an unconfirmed tx appears, then confirms
    292     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx(
    293         confirmations=0)
    294 
    295     rpc = DummyJsonRpc([], [dummy_tx["vin"][0]], {dummy_tx["blockhash"]:
    296         containing_block_height})
    297     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    298     assert txmonitor.build_address_history([dummy_spk])
    299     assert len(txmonitor.address_history) == 1
    300     sh = script_to_scripthash(dummy_spk)
    301     assert len(txmonitor.get_electrum_history(sh)) == 0
    302     txmonitor.subscribe_address(sh)
    303     # unconfirm transaction appears
    304     assert len(list(txmonitor.check_for_updated_txes())) == 0
    305     rpc.add_transaction(dummy_tx)
    306     assert len(list(txmonitor.check_for_updated_txes())) == 1
    307     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk,
    308         height=0, txid=dummy_tx["txid"], subscribed=True)
    309     # transaction confirms
    310     dummy_tx["confirmations"] = 1
    311     assert len(list(txmonitor.check_for_updated_txes())) == 1
    312     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk,
    313         height=containing_block_height, txid=dummy_tx["txid"], subscribed=True)
    314 
    315 def test_unrelated_tx():
    316     ###transaction that has nothing to do with our wallet
    317     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx(
    318         confirmations=0)
    319     our_dummy_spk = create_dummy_spk()
    320 
    321     rpc = DummyJsonRpc([dummy_tx], [], {dummy_tx["blockhash"]:
    322         containing_block_height})
    323     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    324     assert txmonitor.build_address_history([our_dummy_spk])
    325     assert len(txmonitor.address_history) == 1
    326     assert len(txmonitor.get_electrum_history(script_to_scripthash(
    327         our_dummy_spk))) == 0
    328 
    329 def test_duplicate_txid():
    330     ###two txes with the same txid, built history
    331     dummy_spk, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    332     dummy_spk, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    333         output_spk=dummy_spk)
    334     dummy_spk, containing_block_height3, dummy_tx3 = create_dummy_funding_tx(
    335         output_spk=dummy_spk)
    336     dummy_tx2["txid"] = dummy_tx1["txid"]
    337     dummy_tx3["txid"] = dummy_tx1["txid"]
    338     sh = script_to_scripthash(dummy_spk)
    339     rpc = DummyJsonRpc([dummy_tx1, dummy_tx2], [], {dummy_tx1["blockhash"]:
    340         containing_block_height1, dummy_tx2["blockhash"]:
    341         containing_block_height2, dummy_tx3["blockhash"]:
    342         containing_block_height3})
    343     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    344     assert txmonitor.build_address_history([dummy_spk])
    345     assert len(txmonitor.get_electrum_history(sh)) == 1
    346     txmonitor.subscribe_address(sh)
    347     assert txmonitor.get_electrum_history(sh)[0]["tx_hash"] == dummy_tx1["txid"]
    348     rpc.add_transaction(dummy_tx3)
    349     assert len(list(txmonitor.check_for_updated_txes())) == 1
    350     assert len(txmonitor.get_electrum_history(sh)) == 1
    351     assert txmonitor.get_electrum_history(sh)[0]["tx_hash"] == dummy_tx1["txid"]
    352 
    353 def test_address_reuse():
    354     ###transaction which arrives to an address which already has a tx on it
    355     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    356     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    357         output_spk=dummy_spk1)
    358 
    359     rpc = DummyJsonRpc([dummy_tx1], [], {dummy_tx1["blockhash"]:
    360         containing_block_height1, dummy_tx2["blockhash"]:
    361         containing_block_height2})
    362     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    363     assert txmonitor.build_address_history([dummy_spk1])
    364     sh = script_to_scripthash(dummy_spk1)
    365     assert len(txmonitor.get_electrum_history(sh)) == 1
    366     rpc.add_transaction(dummy_tx2)
    367     assert len(txmonitor.get_electrum_history(sh)) == 1
    368     txmonitor.check_for_updated_txes()
    369     assert len(txmonitor.get_electrum_history(sh)) == 2
    370 
    371 def test_from_address():
    372     ###transaction spending FROM one of our addresses
    373     dummy_spk1, containing_block_height1, input_tx = create_dummy_funding_tx()
    374     dummy_spk2, containing_block_height2, spending_tx = create_dummy_funding_tx(
    375         input_txid=input_tx["txid"])
    376 
    377     rpc = DummyJsonRpc([input_tx, spending_tx], [],
    378         {input_tx["blockhash"]: containing_block_height1,
    379         spending_tx["blockhash"]: containing_block_height2})
    380     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    381     assert txmonitor.build_address_history([dummy_spk1])
    382     sh = script_to_scripthash(dummy_spk1)
    383     assert len(txmonitor.get_electrum_history(sh)) == 2
    384 
    385 def test_tx_within_wallet():
    386     ###transaction from one address to the other, both addresses in wallet
    387     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    388     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    389         input_txid=dummy_tx1["txid"])
    390 
    391     rpc = DummyJsonRpc([dummy_tx1, dummy_tx2], [],
    392         {dummy_tx1["blockhash"]: containing_block_height1,
    393         dummy_tx2["blockhash"]: containing_block_height2})
    394     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    395     assert txmonitor.build_address_history([dummy_spk1, dummy_spk2])
    396     assert len(txmonitor.get_electrum_history(script_to_scripthash(
    397         dummy_spk1))) == 2
    398     assert len(txmonitor.get_electrum_history(script_to_scripthash(
    399         dummy_spk2))) == 1
    400 
    401 def test_tx_with_unconfirmed_input():
    402     ###unconfirmed tx arrives with unconfirmed input, which then both confirm
    403 
    404     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx(
    405         confirmations=0)
    406     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    407         confirmations=0, input_txid=dummy_tx1["txid"], input_confirmations=0)
    408 
    409     rpc = DummyJsonRpc([], [dummy_tx1["vin"][0], dummy_tx2["vin"][0]],
    410         {dummy_tx1["blockhash"]: containing_block_height1,
    411         dummy_tx2["blockhash"]: containing_block_height2})
    412     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    413 
    414     assert txmonitor.build_address_history([dummy_spk1, dummy_spk2])
    415     assert len(txmonitor.address_history) == 2
    416 
    417     sh1 = script_to_scripthash(dummy_spk1)
    418     sh2 = script_to_scripthash(dummy_spk2)
    419     assert len(txmonitor.get_electrum_history(sh1)) == 0
    420     assert len(txmonitor.get_electrum_history(sh2)) == 0
    421     txmonitor.subscribe_address(sh1)
    422     txmonitor.subscribe_address(sh2)
    423 
    424     #the unconfirmed transactions appear
    425     assert len(list(txmonitor.check_for_updated_txes())) == 0
    426     rpc.add_transaction(dummy_tx1)
    427     rpc.add_transaction(dummy_tx2)
    428     assert len(list(txmonitor.check_for_updated_txes())) == 2
    429     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    430         height=0, txid=dummy_tx1["txid"], subscribed=True)
    431     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2,
    432         height=-1, txid=dummy_tx2["txid"], subscribed=True)
    433 
    434     #the transactions confirm
    435     dummy_tx1["confirmations"] = 1
    436     dummy_tx2["confirmations"] = 1
    437 
    438     assert len(list(txmonitor.check_for_updated_txes())) == 2
    439     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    440         height=containing_block_height1, txid=dummy_tx1["txid"],
    441         subscribed=True)
    442     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2,
    443         height=containing_block_height2, txid=dummy_tx2["txid"],
    444         subscribed=True)
    445 
    446 def test_overrun_gap_limit():
    447     ###overrun gap limit so import address is needed
    448     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx()
    449     dummy_spk_imported = create_dummy_spk()
    450 
    451     class DummyImportDeterministicWallet(DeterministicWallet):
    452         def __init__(self):
    453             pass
    454 
    455         def have_scriptpubkeys_overrun_gaplimit(self, scriptpubkeys):
    456             return {0: 1} #overrun by one
    457 
    458         def get_new_scriptpubkeys(self, change, count):
    459             return [dummy_spk_imported]
    460 
    461     rpc = DummyJsonRpc([], [], {dummy_tx["blockhash"]: containing_block_height})
    462     txmonitor = TransactionMonitor(rpc, [DummyImportDeterministicWallet()],
    463                                    logger)
    464     assert txmonitor.build_address_history([dummy_spk])
    465     assert len(txmonitor.address_history) == 1
    466     assert len(list(txmonitor.check_for_updated_txes())) == 0
    467     assert len(txmonitor.get_electrum_history(script_to_scripthash(
    468         dummy_spk))) == 0
    469     rpc.add_transaction(dummy_tx)
    470     assert len(list(txmonitor.check_for_updated_txes())) == 0
    471     assert len(txmonitor.get_electrum_history(script_to_scripthash(
    472         dummy_spk))) == 1
    473     assert len(txmonitor.get_electrum_history(script_to_scripthash(
    474         dummy_spk_imported))) == 0
    475     assert len(rpc.get_imported_addresses()) == 1
    476     assert rpc.get_imported_addresses()[0] == dummy_spk_to_address(
    477         dummy_spk_imported)
    478 
    479 def test_conflicted_tx():
    480     ###conflicted transaction should get rejected
    481     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx(
    482         confirmations=-1)
    483     rpc = DummyJsonRpc([dummy_tx], [], {})
    484     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    485     sh = script_to_scripthash(dummy_spk)
    486 
    487     assert txmonitor.build_address_history([dummy_spk])
    488     assert len(txmonitor.address_history) == 1
    489     #shouldnt show up after build history because conflicted
    490     assert len(txmonitor.get_electrum_history(sh)) == 0
    491 
    492     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx(
    493         confirmations=-1, output_spk=dummy_spk)
    494     rpc.add_transaction(dummy_tx)
    495     assert len(list(txmonitor.check_for_updated_txes())) == 0
    496     #incoming tx is not added either
    497     assert len(txmonitor.get_electrum_history(sh)) == 0
    498 
    499 def test_reorg_finney_attack():
    500     ###an unconfirmed tx being broadcast, another conflicting tx being
    501     ### confirmed, the first tx gets conflicted status
    502 
    503     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx(
    504         confirmations=0)
    505     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    506         confirmations=0, input_txid=dummy_tx1["vin"][0])
    507     #two unconfirmed txes spending the same input, so they are in conflict
    508 
    509     rpc = DummyJsonRpc([dummy_tx1], [dummy_tx1["vin"][0]],
    510         {dummy_tx1["blockhash"]: containing_block_height1,
    511         dummy_tx2["blockhash"]: containing_block_height2})
    512     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    513     assert txmonitor.build_address_history([dummy_spk1, dummy_spk2])
    514     assert len(txmonitor.address_history) == 2
    515     sh1 = script_to_scripthash(dummy_spk1)
    516     sh2 = script_to_scripthash(dummy_spk2)
    517     assert len(txmonitor.get_electrum_history(sh1)) == 1
    518     assert len(txmonitor.get_electrum_history(sh2)) == 0
    519     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    520         height=0, txid=dummy_tx1["txid"], subscribed=False)
    521     # a conflicting transaction confirms
    522     rpc.add_transaction(dummy_tx2)
    523     dummy_tx1["confirmations"] = -1
    524     dummy_tx2["confirmations"] = 1
    525     assert len(list(txmonitor.check_for_updated_txes())) == 0
    526     assert len(txmonitor.get_electrum_history(sh1)) == 0
    527     assert len(txmonitor.get_electrum_history(sh2)) == 1
    528     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2,
    529         height=containing_block_height2, txid=dummy_tx2["txid"],
    530         subscribed=False)
    531 
    532 def test_reorg_race_attack():
    533     #a tx is confirmed, a chain reorganization happens and that tx is replaced
    534     # by another tx spending the same input, the original tx is now conflicted
    535     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    536     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx(
    537         input_txid=dummy_tx1["vin"][0])
    538 
    539     rpc = DummyJsonRpc([dummy_tx1], [],
    540         {dummy_tx1["blockhash"]: containing_block_height1,
    541         dummy_tx2["blockhash"]: containing_block_height2})
    542     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    543     assert txmonitor.build_address_history([dummy_spk1, dummy_spk2])
    544     assert len(txmonitor.address_history) == 2
    545     sh1 = script_to_scripthash(dummy_spk1)
    546     sh2 = script_to_scripthash(dummy_spk2)
    547     assert len(txmonitor.get_electrum_history(sh1)) == 1
    548     assert len(txmonitor.get_electrum_history(sh2)) == 0
    549     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    550         height=containing_block_height1, txid=dummy_tx1["txid"],
    551         subscribed=False)
    552     #race attack happens
    553     #dummy_tx1 goes to -1 confirmations, dummy_tx2 gets confirmed
    554     rpc.add_transaction(dummy_tx2)
    555     dummy_tx1["confirmations"] = -1
    556     dummy_tx2["confirmations"] = 1
    557     assert len(list(txmonitor.check_for_updated_txes())) == 0
    558     assert len(txmonitor.get_electrum_history(sh1)) == 0
    559     assert len(txmonitor.get_electrum_history(sh2)) == 1
    560     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk2,
    561         height=containing_block_height2, txid=dummy_tx2["txid"],
    562         subscribed=False)
    563 
    564 def test_reorg_censor_tx():
    565     #confirmed tx gets reorgd out and becomes unconfirmed
    566     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    567 
    568     rpc = DummyJsonRpc([dummy_tx1], [dummy_tx1["vin"][0]],
    569         {dummy_tx1["blockhash"]: containing_block_height1})
    570     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    571     assert txmonitor.build_address_history([dummy_spk1])
    572     assert len(txmonitor.address_history) == 1
    573     sh = script_to_scripthash(dummy_spk1)
    574     assert len(txmonitor.get_electrum_history(sh)) == 1
    575     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    576         height=containing_block_height1, txid=dummy_tx1["txid"],
    577         subscribed=False)
    578     #blocks appear which reorg out the tx, making it unconfirmed
    579     dummy_tx1["confirmations"] = 0
    580     assert len(list(txmonitor.check_for_updated_txes())) == 0
    581     assert len(txmonitor.get_electrum_history(sh)) == 1
    582     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    583         height=0, txid=dummy_tx1["txid"], subscribed=False)
    584 
    585 def test_reorg_different_block():
    586     #confirmed tx gets reorged into another block with a different height
    587     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    588     dummy_spk2, containing_block_height2, dummy_tx2 = create_dummy_funding_tx()
    589 
    590     rpc = DummyJsonRpc([dummy_tx1], [],
    591         {dummy_tx1["blockhash"]: containing_block_height1,
    592         dummy_tx2["blockhash"]: containing_block_height2})
    593     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    594     assert txmonitor.build_address_history([dummy_spk1])
    595     assert len(txmonitor.address_history) == 1
    596     sh = script_to_scripthash(dummy_spk1)
    597     assert len(txmonitor.get_electrum_history(sh)) == 1
    598     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    599         height=containing_block_height1, txid=dummy_tx1["txid"],
    600         subscribed=False)
    601 
    602     #tx gets reorged into another block (so still confirmed)
    603     dummy_tx1["blockhash"] = dummy_tx2["blockhash"]
    604     assert len(list(txmonitor.check_for_updated_txes())) == 0
    605     assert len(txmonitor.get_electrum_history(sh)) == 1
    606     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk1,
    607         height=containing_block_height2, txid=dummy_tx1["txid"],
    608         subscribed=False)
    609 
    610 def test_tx_safe_from_reorg():
    611     ##tx confirmed with 1 confirmation, then confirmations goes to 100
    612     ## test that the reorganizable_txes list length goes down
    613     dummy_spk1, containing_block_height1, dummy_tx1 = create_dummy_funding_tx()
    614     rpc = DummyJsonRpc([dummy_tx1], [],
    615         {dummy_tx1["blockhash"]: containing_block_height1})
    616     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    617     assert txmonitor.build_address_history([dummy_spk1])
    618     assert len(list(txmonitor.check_for_updated_txes())) == 0
    619     assert len(txmonitor.reorganizable_txes) == 1
    620     dummy_tx1["confirmations"] = 2000
    621     assert len(list(txmonitor.check_for_updated_txes())) == 0
    622     assert len(txmonitor.reorganizable_txes) == 0
    623 
    624 #other possible stuff to test:
    625 #finding confirmed and unconfirmed tx, in that order, then both confirm
    626 #finding unconfirmed and confirmed tx, in that order, then both confirm
    627 
    628 def test_single_tx_no_address_key():
    629     ### same as test_single_tx() but the result of listtransactions has no
    630     ### address field, see the github issue #31
    631     dummy_spk, containing_block_height, dummy_tx = create_dummy_funding_tx()
    632     del dummy_tx["address"]
    633     logger.info("dummy_tx with no address = " + str(dummy_tx))
    634     print("pdummy_tx with no address = " + str(dummy_tx))
    635 
    636     rpc = DummyJsonRpc([dummy_tx], [],
    637         {dummy_tx["blockhash"]: containing_block_height})
    638     txmonitor = TransactionMonitor(rpc, deterministic_wallets, logger)
    639     assert txmonitor.build_address_history([dummy_spk])
    640     assert len(txmonitor.address_history) == 1
    641     assert_address_history_tx(txmonitor.address_history, spk=dummy_spk,
    642         height=containing_block_height, txid=dummy_tx["txid"], subscribed=False)
    643