def run_test(self): node0_address = self.nodes[0].getnewaddress() # Spend block 1/2/3's coinbase transactions # Mine a block. # Create three more transactions, spending the spends # Mine another block. # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block # ... make sure all the transactions are confirmed again. b = [self.nodes[0].getblockhash(n) for n in range(1, 4)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spends1_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.99) for txid in coinbase_txids] spends1_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends1_raw] blocks = [] blocks.extend(self.nodes[0].generate(1)) spends2_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.98) for txid in spends1_id] spends2_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw] blocks.extend(self.nodes[0].generate(1)) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) for txid in spends1_id+spends2_id: tx = self.nodes[0].gettransaction(txid) assert(tx["confirmations"] > 0) # Use invalidateblock to re-org back; all transactions should # end up unconfirmed and back in the mempool for node in self.nodes: node.invalidateblock(blocks[0]) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set(spends1_id+spends2_id)) for txid in spends1_id+spends2_id: tx = self.nodes[0].gettransaction(txid) assert(tx["confirmations"] == 0) # Generate another block, they should all get mined self.nodes[0].generate(1) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) for txid in spends1_id+spends2_id: tx = self.nodes[0].gettransaction(txid) assert(tx["confirmations"] > 0)
def run_test(self): chain_height = self.nodes[0].getblockcount() assert_equal(chain_height, 200) node0_address = self.nodes[0].getnewaddress() # Coinbase at height chain_height-100+1 ok in mempool, should # get mined. Coinbase at height chain_height-100+2 is # is too immature to spend. b = [self.nodes[0].getblockhash(n) for n in range(101, 103)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spends_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.99) for txid in coinbase_txids] spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0]) # coinbase at height 102 should be too immature to spend assert_raises_rpc_error(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1]) # mempool should have just spend_101: assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ]) # mine a block, spend_101 should get confirmed self.nodes[0].generate(1) assert_equal(set(self.nodes[0].getrawmempool()), set()) # ... and now height 102 can be spent: spend_102_id = self.nodes[0].sendrawtransaction(spends_raw[1]) assert_equal(self.nodes[0].getrawmempool(), [ spend_102_id ])
def run_test(self): # Start with a 200 block chain assert_equal(self.nodes[0].getblockcount(), 200) # Mine four blocks. After this, nodes[0] blocks # 101, 102, and 103 are spend-able. new_blocks = self.nodes[1].generate(4) self.sync_all() node0_address = self.nodes[0].getnewaddress() node1_address = self.nodes[1].getnewaddress() # Three scenarios for re-orging coinbase spends in the memory pool: # 1. Direct coinbase spend : spend_101 # 2. Indirect (coinbase spend in chain, child in mempool) : spend_102 and spend_102_1 # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. b = [ self.nodes[0].getblockhash(n) for n in range(101, 105) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] spend_101_raw = create_raw_transaction(self.nodes[0], coinbase_txids[1], node1_address, amount=49.99, fee=0.01) spend_102_raw = create_raw_transaction(self.nodes[0], coinbase_txids[2], node0_address, amount=49.99, fee=0.01) spend_103_raw = create_raw_transaction(self.nodes[0], coinbase_txids[3], node0_address, amount=49.99, fee=0.01) # Create a transaction which is time-locked to two blocks in the future timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 49.99, "fee": 0.01}) # Set the time lock timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1) timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransactionwithwallet(timelock_tx)["hex"] # This will raise an exception because the timelock transaction is too immature to spend assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) # Time-locked transaction is still too immature to spend assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = create_raw_transaction(self.nodes[0], spend_102_id, node1_address, amount=49.98, fee=0.01) spend_103_1_raw = create_raw_transaction(self.nodes[0], spend_103_id, node1_address, amount=49.98, fee=0.01) # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) last_block = self.nodes[0].generate(1) # Time-locked transaction can now be spent timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw) self.sync_all() assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id}) for node in self.nodes: node.invalidateblock(last_block[0]) # Time-locked transaction is now too immature and has been removed from the mempool # spend_103_1 has been re-orged out of the chain and is back in the mempool assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, spend_103_1_id}) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: for node in self.nodes: node.invalidateblock(new_blocks[0]) self.sync_all() # mempool should be empty. assert_equal(set(self.nodes[0].getrawmempool()), set())
def run_test(self): # Start with a 200 block chain assert_equal(self.nodes[0].getblockcount(), 200) # Mine four blocks. After this, nodes[0] blocks # 101, 102, and 103 are spend-able. new_blocks = self.nodes[1].generate(4) self.sync_all() node0_address = self.nodes[0].getnewaddress() node1_address = self.nodes[1].getnewaddress() # Three scenarios for re-orging coinbase spends in the memory pool: # 1. Direct coinbase spend : spend_101 # 2. Indirect (coinbase spend in chain, child in mempool) : spend_102 and spend_102_1 # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. b = [self.nodes[0].getblockhash(n) for n in range(101, 105)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spend_101_raw = create_raw_transaction(self.nodes[0], coinbase_txids[1], node1_address, 49.99) spend_102_raw = create_raw_transaction(self.nodes[0], coinbase_txids[2], node0_address, 49.99) spend_103_raw = create_raw_transaction(self.nodes[0], coinbase_txids[3], node0_address, 49.99) # Create a transaction which is time-locked to two blocks in the future timelock_tx = self.nodes[0].createrawtransaction( [{ "txid": coinbase_txids[0], "vout": 0 }], {node0_address: 49.99}) # Set the time lock timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1) timelock_tx = timelock_tx[:-8] + \ hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransactionwithwallet( timelock_tx)["hex"] # This will raise an exception because the timelock transaction is too # immature to spend assert_raises_rpc_error(-26, "bad-txns-nonfinal", self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) # Time-locked transaction is still too immature to spend assert_raises_rpc_error(-26, 'bad-txns-nonfinal', self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = create_raw_transaction(self.nodes[0], spend_102_id, node1_address, 49.98) spend_103_1_raw = create_raw_transaction(self.nodes[0], spend_103_id, node1_address, 49.98) # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) last_block = self.nodes[0].generate(1) # Sync blocks, so that peer 1 gets the block before timelock_tx # Otherwise, peer 1 would put the timelock_tx in recentRejects self.sync_all() # Time-locked transaction can now be spent timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw) self.sync_all() assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id}) for node in self.nodes: node.invalidateblock(last_block[0]) # Time-locked transaction is now too immature and has been removed from the mempool # spend_103_1 has been re-orged out of the chain and is back in the # mempool assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, spend_103_1_id}) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: for node in self.nodes: node.invalidateblock(new_blocks[0]) self.sync_all() # mempool should be empty. assert_equal(set(self.nodes[0].getrawmempool()), set())
def run_test(self): node0_address = self.nodes[0].getnewaddress() # Spend block 1/2/3's coinbase transactions # Mine a block. # Create three more transactions, spending the spends # Mine another block. # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block # ... make sure all the transactions are confirmed again. b = [self.nodes[0].getblockhash(n) for n in range(1, 4)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spends1_raw = [ create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.99) for txid in coinbase_txids ] spends1_id = [ self.nodes[0].sendrawtransaction(tx) for tx in spends1_raw ] blocks = [] blocks.extend(self.nodes[0].generate(1)) spends2_raw = [ create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.98) for txid in spends1_id ] spends2_id = [ self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw ] blocks.extend(self.nodes[0].generate(1)) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) for txid in spends1_id + spends2_id: tx = self.nodes[0].gettransaction(txid) assert tx["confirmations"] > 0 # Use invalidateblock to re-org back for node in self.nodes: node.invalidateblock(blocks[0]) # All txns should be back in mempool with 0 confirmations assert_equal(set(self.nodes[0].getrawmempool()), set(spends1_id + spends2_id)) for txid in spends1_id + spends2_id: tx = self.nodes[0].gettransaction(txid) assert tx["confirmations"] == 0 # Generate another block, they should all get mined self.nodes[0].generate(1) # mempool should be empty, all txns confirmed assert_equal(set(self.nodes[0].getrawmempool()), set()) for txid in spends1_id + spends2_id: tx = self.nodes[0].gettransaction(txid) assert tx["confirmations"] > 0
def run_test(self): for node in self.nodes: node.generate(25) self.sync_all() self.nodes[0].generate(COINBASE_MATURITY) self.sync_all() start_count = self.nodes[0].getblockcount() # Mine three blocks. After this, nodes[0] blocks # 101, 102, and 103 are spend-able. new_blocks = self.nodes[1].generate(4) self.sync_all() assert_equal(self.nodes[0].getblockcount(), self.nodes[1].getblockcount()) node0_address = self.nodes[0].getnewaddress() node1_address = self.nodes[1].getnewaddress() # Three scenarios for re-orging coinbase spends in the memory pool: # 1. Direct coinbase spend : spend_101 # 2. Indirect (coinbase spend in chain, child in mempool) : spend_102 and spend_102_1 # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. b = [self.nodes[0].getblockhash(n) for n in range(51, 55)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spend_101_raw = create_raw_transaction(self.nodes[0], coinbase_txids[1], node1_address, amount=INITIAL_BLOCK_REWARD - 0.01) spend_102_raw = create_raw_transaction(self.nodes[0], coinbase_txids[2], node0_address, amount=INITIAL_BLOCK_REWARD - 0.01) spend_103_raw = create_raw_transaction(self.nodes[0], coinbase_txids[3], node0_address, amount=INITIAL_BLOCK_REWARD - 0.01) # Create a block-height-locked transaction which will be invalid after reorg timelock_tx = self.nodes[0].createrawtransaction( [{ "txid": coinbase_txids[0], "vout": 0 }], {node0_address: INITIAL_BLOCK_REWARD - 0.01}) # Set the time lock timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1) timelock_tx = timelock_tx[:-8] + hex( self.nodes[0].getblockcount() + 2)[3:] + "0" + hex(self.nodes[0].getblockcount() + 2)[2:3] + "0000" timelock_tx = self.nodes[0].signrawtransactionwithwallet( timelock_tx)["hex"] # This will raise an exception because the timelock transaction is too immature to spend assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) # Time-locked transaction is still too immature to spend assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = create_raw_transaction(self.nodes[0], spend_102_id, node1_address, amount=INITIAL_BLOCK_REWARD - Decimal('0.02')) spend_103_1_raw = create_raw_transaction(self.nodes[0], spend_103_id, node1_address, amount=INITIAL_BLOCK_REWARD - Decimal('0.02')) # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) last_block = self.nodes[0].generate(1) # Time-locked transaction can now be spent timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw) self.sync_all() assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id}) for node in self.nodes: node.invalidateblock(last_block[0]) # Time-locked transaction is now too immature and has been removed from the mempool # spend_103_1 has been re-orged out of the chain and is back in the mempool assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, spend_103_1_id}) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: for node in self.nodes: node.invalidateblock(new_blocks[0]) self.sync_all() # mempool should be empty. assert_equal(set(self.nodes[0].getrawmempool()), set())
def basic_check(self): """Tests basic getdsproof/getdsprooflist functionality""" self.generate(self.nodes[0], 1) self.sync_all() # Create and mine a regular non-coinbase transaction for spending funding_txid = self.nodes[0].getblock( self.nodes[0].getblockhash(1))['tx'][0] # Create three conflicting transactions. They are only signed, but not yet submitted to the mempool first_ds_tx = create_raw_transaction(self.nodes[0], funding_txid, self.nodes[0].getnewaddress(), 49.95) second_ds_tx = create_raw_transaction(self.nodes[0], funding_txid, self.nodes[0].getnewaddress(), 49.95) first_ds_tx_id = self.nodes[0].sendrawtransaction(first_ds_tx) assert_equal(self.nodes[0].getdsproofscore(first_ds_tx_id), 1.0) # score is 1.0 until we see a dsproof second_ds_tx_id = self.nodes[1].call_rpc( 'sendrawtransaction', second_ds_tx, ignore_error='txn-mempool-conflict') vout = find_output(self.nodes[0], first_ds_tx_id, Decimal('49.95')) child = create_raw_transaction(self.nodes[0], first_ds_tx_id, self.nodes[0].getnewaddress(), 49.90, vout) child_id = self.nodes[0].sendrawtransaction(child) vout = find_output(self.nodes[0], child_id, Decimal('49.90')) grandchild = create_raw_transaction(self.nodes[0], child_id, self.nodes[0].getnewaddress(), 49.85, vout) grandchild_id = self.nodes[0].sendrawtransaction(grandchild) # Wait until both nodes see the same dsproof wait_until(lambda: len(self.nodes[0].getdsprooflist()) == 1 and len( self.nodes[1].getdsprooflist()) == 1, timeout=10) assert_equal(self.nodes[0].getdsproofscore(first_ds_tx_id), 0.0) # score is 0 after we see a dsproof dsplist = self.nodes[0].getdsprooflist() assert_equal(len(dsplist), 1) assert isinstance(dsplist[0], str) assert isinstance(self.nodes[0].getdsprooflist(1)[0], dict) # Get a DSP by DspId dsp = self.nodes[0].getdsproof(dsplist[0]) dsp_node1 = self.nodes[1].getdsproof(dsplist[0]) # each node has the same dsproof, but associated with the txid it sees in mempool assert_equal(dsp["txid"], first_ds_tx_id) if second_ds_tx_id is not None: # common case where node1 saw the RPC tx2 before the p2p tx1 assert_equal(dsp_node1["txid"], second_ds_tx_id) else: # node1 happened to see the first tx via p2p first assert_equal(dsp_node1["txid"], first_ds_tx_id) # we expect this dsp tx to invalidate 3 tx's total (parent, child, and grandchild) assert_equal(len(dsp["descendants"]), 3) # Check that the dsp has the "outpoint" key and it is what we expect ds_outpoint = {"txid": funding_txid, "vout": 0} assert_equal(dsp["outpoint"], ds_outpoint) # Get a DSP by double spending txid assert "txid" in self.nodes[0].getdsproof(first_ds_tx_id) # Get a DSP by txid of a transaction in a double spent chain assert_equal(len(self.nodes[0].getdsproof(child_id)["path"]), 2) assert_equal(len(self.nodes[0].getdsproof(grandchild_id)["path"]), 3) # A non-recursive call for the double spent transaction will result in a DSP assert self.nodes[0].getdsproof(first_ds_tx_id, 0, False) is not None # A non-recursive call for a child transaction in the double spent chain # will not result in a DSP assert self.nodes[0].getdsproof(child_id, 0, False) is None # Check that the list and the get calls outputs match for the same verbosity level, # and that they contain the keys we expect for each verbosity level verb_keys = { 1: {"txid", "hex"}, 2: {"dspid", "txid", "outpoint"}, 3: {"dspid", "txid", "outpoint", "spenders"}, } spenders_keys = { "txversion", "sequence", "locktime", "hashprevoutputs", "hashsequence", "hashoutputs", "pushdata" } for verbosity in (1, 2, 3): dspid = dsplist[0] dsp = self.nodes[0].getdsproof(dspid, verbosity) dsp.pop("descendants", None) # Remove key that is missing from the list mode dlist = self.nodes[0].getdsprooflist(verbosity) for dsp2 in dlist: if dsp == dsp2: break else: assert False, "Could not find the dsp we expected in the dsplist" assert_equal(dsp["txid"], first_ds_tx_id) # ensure txid always what we expect expected_keys = verb_keys[verbosity] assert_equal(expected_keys & set(dsp.keys()), expected_keys) if "dspid" in expected_keys: assert_equal(dsp["dspid"], dspid) if "outpoint" in expected_keys: assert_equal(dsp["outpoint"], ds_outpoint) if "spenders" in expected_keys: for spender in dsp["spenders"]: assert_equal(spenders_keys & set(spender.keys()), spenders_keys) if "hex" in expected_keys: # ensure that the dsproof hex data decodes ok data = bytes.fromhex(dsp["hex"]) # dsproof serialized data cannot be smaller than 216 bytes assert_greater_than(len(data), 216) # If any of the competing transactions is mined, the DPSs are put in the orphan list self.generate(self.nodes[0], 1) self.sync_all() assert_equal(len(self.nodes[0].getdsprooflist()), 0) # no non-orphan results dsps_all_orphans = self.nodes[0].getdsprooflist(0, True) assert_equal(dsps_all_orphans, dsplist) # all of the previous proofs are orphans for dspid in dsps_all_orphans: # make sure they are all orphans by checking they have no 'txid' key assert self.nodes[0].getdsproof(dspid).get('txid') is None
def paths_check(self): """Check that: - the 'paths' key in a lookup by child txid works as expected, - the 'descendants' key in the lookup of a dsp works as expected. """ self.generate(self.nodes[0], 1) self.sync_all() # Create a transaction on a double spent chain with multiple paths back to the DSP # The following scenario of txos will occur: # A -> B -> C -> D -> E # \ / # -> Z ---->/ # Where there is a known DSP for A # and D contains inputs from C and Z # exactly one path from D to A will be found # (either [Z->D, A->Z] or [C->D, B->C, A->B]) paths = [[], []] # Here we spend from A to B and Z funding_txid = self.nodes[0].getblock( self.nodes[0].getblockhash(2))['tx'][0] inputs = [{"txid": funding_txid, "vout": 0}] b_address = self.nodes[0].getnewaddress() z_address = self.nodes[0].getnewaddress() outputs = {b_address: 24.99, z_address: 24.99} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) signresult = self.nodes[0].signrawtransactionwithwallet(rawtx) assert_equal(signresult["complete"], True) transaction_2_outputs = signresult['hex'] doublespendingtransaction = create_raw_transaction( self.nodes[0], funding_txid, self.nodes[0].getnewaddress(), 49.97, 0) transaction_2outputs_id = self.nodes[0].sendrawtransaction( transaction_2_outputs) self.nodes[1].call_rpc('sendrawtransaction', doublespendingtransaction, ignore_error='txn-mempool-conflict') paths[0].insert(0, transaction_2outputs_id) # root of both possible paths paths[1].insert(0, transaction_2outputs_id) # Here we spend from B to C child = create_raw_transaction(self.nodes[0], transaction_2outputs_id, self.nodes[0].getnewaddress(), 24.96, 0) child_id = self.nodes[0].sendrawtransaction(child) paths[1].insert(0, child_id) # exists only on longer path d_address = self.nodes[0].getnewaddress() vout = find_output(self.nodes[0], child_id, Decimal('24.96')) # Here we spend from Z and C to D inputs = [{ "txid": transaction_2outputs_id, "vout": 1 }, { "txid": child_id, "vout": vout }] outputs = {d_address: 49.94} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) signresult = self.nodes[0].signrawtransactionwithwallet(rawtx) assert_equal(signresult["complete"], True) transaction_2inputs = signresult['hex'] transaction_2inputs_id = self.nodes[0].sendrawtransaction( transaction_2inputs) paths[0].insert( 0, transaction_2inputs_id) # exists of both possible paths paths[1].insert(0, transaction_2inputs_id) # add 1 more grandchild tx for good measure e_tx = create_raw_transaction(self.nodes[0], transaction_2inputs_id, self.nodes[0].getnewaddress(), 24.95 * 2, 0) e_txid = self.nodes[0].sendrawtransaction(e_tx) paths_shorter = deepcopy(paths) paths[0].insert(0, e_txid) # leaf tx of both possible paths paths[1].insert(0, e_txid) wait_until(lambda: len(self.nodes[0].getdsprooflist()) == 1, timeout=10) dsplist = self.nodes[0].getdsprooflist() self.log.info(f"dsplist: {dsplist}") dsp = self.nodes[0].getdsproof(dsplist[0]) descendants_expected = {txid for txid in paths[0] + paths[1]} self.log.info( f"dsp: {dsp} descendants_expected: {descendants_expected}") assert_equal(set(dsp["descendants"]), descendants_expected) dsp = self.nodes[0].getdsproof(e_txid) self.log.info(f"dsp: {dsp}, paths: {paths}") assert dsp is not None assert "path" in dsp # make sure the path from query txid to double-spend txid is one of the two possible paths assert dsp["path"] in paths self.log.info(f"dsp: {dsp}") self.log.info(f"dsp path len: {len(dsp['path'])}") assert_equal(dsp["txid"], transaction_2outputs_id) # now go up one, query by previous txid, we should get a shorter path dsp2 = self.nodes[0].getdsproof(transaction_2inputs_id) assert dsp2 != dsp assert dsp2["path"] in paths_shorter assert_equal(dsp2["descendants"], dsp["descendants"]) assert_equal(dsp2["dspid"], dsp["dspid"]) assert_equal(dsp2["txid"], dsp["txid"])
def run_test(self): node = self.nodes[0] self.setup_stake_coins(node) first_3_blocks = node.generate(3) # Let's lock the first 3 coinbase txs so we can used them later for block_id in first_3_blocks: node.lockunspent(False, [{"txid": node.getblock(block_id)['tx'][0], "vout": 0}]) # Make the first 3 coinbase mature now node.generate(102) assert_equal(node.getblockcount(), 105) assert_finalizationstate(node, {'currentDynasty': 18, 'currentEpoch': 21, 'lastJustifiedEpoch': 20, 'lastFinalizedEpoch': 19}) node0_address = node.getnewaddress("", "bech32") # Spend block 1/2/3's coinbase transactions # Mine a block. # Create three more transactions, spending the spends # Mine another block. # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block # ... make sure all the transactions are confirmed again. b = [self.nodes[0].getblockhash(n) for n in range(1, 4)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] spends1_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=PROPOSER_REWARD - Decimal('0.01')) for txid in coinbase_txids] spends1_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends1_raw] blocks = [] blocks.extend(node.generate(1)) spends2_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=PROPOSER_REWARD - Decimal('0.02')) for txid in spends1_id] spends2_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw] blocks.extend(node.generate(1)) # mempool should be empty, all txns confirmed assert_equal(set(node.getrawmempool()), set()) for txid in spends1_id+spends2_id: tx = node.gettransaction(txid) assert tx["confirmations"] > 0 # Use invalidateblock to re-org back for node in self.nodes: node.invalidateblock(blocks[0]) # All txns should be back in mempool with 0 confirmations assert_equal(set(node.getrawmempool()), set(spends1_id+spends2_id)) for txid in spends1_id+spends2_id: tx = node.gettransaction(txid) assert tx["confirmations"] == 0 # Generate another block, they should all get mined node.generate(1) # mempool should be empty, all txns confirmed assert_equal(set(node.getrawmempool()), set()) for txid in spends1_id+spends2_id: tx = node.gettransaction(txid) assert tx["confirmations"] > 0