def run_test(self): with self.run_node_with_connections( "Scenario 1", 0, [ '-banscore=100000', '-genesisactivationheight=110', '-maxstdtxvalidationduration=100' ], number_of_connections=1) as (conn, ): coinbase1 = make_new_block(conn) for _ in range(110): make_new_block(conn) tx_parent = create_transaction(coinbase1, 0, CScript(), coinbase1.vout[0].nValue - 1000, CScript([OP_TRUE])) tx_parent.rehash() tx_orphan = make_big_orphan( tx_parent, DEFAULT_MAX_TX_SIZE_POLICY_AFTER_GENESIS) assert_equal(len(tx_orphan.serialize()), DEFAULT_MAX_TX_SIZE_POLICY_AFTER_GENESIS) conn.send_message(msg_tx(tx_orphan)) # Making sure parent is not sent right away for bitcond to detect an orphan time.sleep(1) conn.send_message(msg_tx(tx_parent)) check_mempool_equals(conn.rpc, [tx_parent, tx_orphan])
def create_fund_txn(self, conn, noutput, tx_fee, locking_script, pubkey=None): # create a new block with coinbase last_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) coinbase = create_coinbase(height=last_block_info["height"] + 1, pubkey=pubkey) new_block = create_block(int(last_block_info["hash"], 16), coinbase=coinbase, nTime=last_block_info["time"] + 1) new_block.nVersion = last_block_info["version"] new_block.solve() conn.send_message(msg_block(new_block)) wait_until(lambda: conn.rpc.getbestblockhash() == new_block.hash, check_interval=0.3) # mature the coinbase conn.rpc.generate(100) # create and send a funding txn funding_tx = self.create_tx([(coinbase, 0)], 2, 1.5, locking_script) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) conn.rpc.generate(1) # create a new txn which pays the specified tx_fee new_tx = self.create_tx([(funding_tx, 0)], noutput, tx_fee, locking_script) last_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) new_block = create_block( int(last_block_info["hash"], 16), coinbase=create_coinbase(height=last_block_info["height"] + 1), nTime=last_block_info["time"] + 1) new_block.nVersion = last_block_info["version"] new_block.vtx.append(new_tx) new_block.hashMerkleRoot = new_block.calc_merkle_root() new_block.calc_sha256() new_block.solve() conn.send_message(msg_block(new_block)) wait_until(lambda: conn.rpc.getbestblockhash() == new_block.hash, check_interval=0.3) return new_tx
def _prepare_node(self): with self.run_node_with_connections( "Prepare utxos", 0, [], number_of_connections=1) as (conn, ): # create block with coinbase coinbase = create_coinbase(height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=0.3) # mature the coinbase conn.rpc.generate(150) funding_tx = self.create_tx([(coinbase, 0)], 4, 1.5) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) conn.rpc.generate(1) return funding_tx
def run_invalid_orphans_scenarios(self): with self.run_node_with_connections("Scenario 1: Valid orphans", 0, [], number_of_connections=1, ip=get_lan_ip()) as (conn, ): parent_tx1, parent_tx2, orphans, rejected_txs = self.prepare_parents_and_children( conn, parent_invalidity=None, children_invalidity=None) #sending orphans for tx in orphans: conn.send_message(msg_tx(tx)) assert conn.rpc.getmempoolinfo( )["size"] == 0, "No transactions should be in the mempool!" assert len( rejected_txs) == 0, "No transactions should be rejected!" # sending parents conn.send_message(msg_tx(parent_tx1)) conn.send_message(msg_tx(parent_tx2)) # all transactions should be accepted to mempool check_mempool_equals(conn.rpc, orphans + [parent_tx1, parent_tx2]) assert len( rejected_txs) == 0, "No transactions should be rejected!" with self.run_node_with_connections("Scenario 2: low fee orphans", 0, [], number_of_connections=1, ip=get_lan_ip()) as (conn, ): parent_tx1, parent_tx2, orphans, rejected_txs = self.prepare_parents_and_children( conn, parent_invalidity=None, children_invalidity="low_fee") # sending orphans for tx in orphans: conn.send_message(msg_tx(tx)) sleep(1) assert conn.rpc.getmempoolinfo( )["size"] == 0, "No transactions should be in the mempool!" assert len( rejected_txs) == 0, "No transactions should be rejected yet!" # sending first parent conn.send_message(msg_tx(parent_tx1)) check_mempool_equals(conn.rpc, [parent_tx1]) assert len( rejected_txs) == 0, "No transactions should be rejected yet!" # sending second parent conn.send_message(msg_tx(parent_tx2)) check_mempool_equals(conn.rpc, [parent_tx1, parent_tx2]) # check that all orphans are rejected, but we are not banned self.check_rejected(rejected_txs, orphans) assert conn.connected, "We should still be connected (not banned)." with self.run_node_with_connections( "Scenario 3: invalid signature orphans", 0, [], number_of_connections=1, ip=get_lan_ip()) as (conn, ): parent_tx1, parent_tx2, orphans, rejected_txs = self.prepare_parents_and_children( conn, parent_invalidity=None, children_invalidity="bad_signature") # sending orphans for tx in orphans: conn.send_message(msg_tx(tx)) sleep(1) assert conn.rpc.getmempoolinfo( )["size"] == 0, "No transactions should be in the mempool!" assert len( rejected_txs) == 0, "No transactions should be rejected yet!" conn.send_message(msg_tx(parent_tx1)) check_mempool_equals( conn.rpc, [parent_tx1]) # Only parent_tx1 should be in the mempool assert len( rejected_txs) == 0, "No transactions should be rejected yet!" conn.send_message(msg_tx(parent_tx2)) check_mempool_equals( conn.rpc, [parent_tx1, parent_tx2 ]) # Only parent_tx1 and parent_tx2 should be in the mempool # we must be banned conn.cb.wait_for_disconnect(timeout=20) # will be disconnected assert len(conn.rpc.listbanned()) == 1 # and banned conn.rpc.clearbanned() # the banscore is set to 101 because rejection of the tx with invalid signature brings 100 points, # we don't want to be banned as result of only one tx with self.run_node_with_connections("Scenario 4: bad signature parent", 0, ['-banscore=101'], number_of_connections=1, ip=get_lan_ip()) as (conn, ): valid_parent_tx, invalid_parent_tx, orphans, rejected_txs = self.prepare_parents_and_children( conn, parent_invalidity="bad_signature", children_invalidity=None) for tx in orphans: conn.send_message(msg_tx(tx)) sleep(1) assert conn.rpc.getmempoolinfo( )["size"] == 0, "No transactions should be in the mempool!" assert len( rejected_txs) == 0, "No transactions should be rejected yet!" conn.send_message(msg_tx(valid_parent_tx)) check_mempool_equals( conn.rpc, [valid_parent_tx ]) # Only valid_parent_tx should be in the mempool assert len( rejected_txs) == 0, "No transactions should be rejected yet!" conn.send_message(msg_tx(invalid_parent_tx)) check_mempool_equals( conn.rpc, [valid_parent_tx ]) # Still only valid_parent_tx should be in the mempool self.check_rejected( rejected_txs, [invalid_parent_tx]) # And only invalid parent is rejected sleep(1) assert len(conn.rpc.listbanned()) == 0 # not banned
def run_test(self): limitancestorcount = 20 limitcpfpgroupmemberscount = 10 with self.run_node_with_connections( "Tests for ancestors count limit, for primary and secondary mempool", 0, [ "-blockmintxfee=0.00001", f"-limitancestorcount={limitancestorcount}", f"-limitcpfpgroupmemberscount={limitcpfpgroupmemberscount}", "-checkmempool=1", ], number_of_connections=1) as (conn, ): mining_fee = 1.01 # in satoshi per byte relayfee = float(conn.rpc.getnetworkinfo()["relayfee"] * COIN / 1000) + 0.01 # in satoshi per byte # create block with coinbase coinbase = create_coinbase(height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=0.3) #mature the coinbase conn.rpc.generate(150) funding_tx = self.create_tx([(coinbase, 0)], 2, 1.5) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) conn.rpc.generate(1) rejected_txs = [] def on_reject(conn, msg): rejected_txs.append(msg) conn.cb.on_reject = on_reject # create oversized primary mempool chain, the last tx in the chain will be over the limit last_outpoint = (funding_tx, 0) primary_mempool_chain = [] for _ in range(limitancestorcount + 1): tx = self.create_tx([last_outpoint], 1, mining_fee) primary_mempool_chain.append(tx) last_outpoint = (tx, 0) # create oversized secondary mempool chain, the last tx in the chain will be over the limit last_outpoint = (funding_tx, 1) secondary_mempool_chain = [] for _ in range(limitcpfpgroupmemberscount + 1): tx = self.create_tx([last_outpoint], 1, relayfee) secondary_mempool_chain.append(tx) last_outpoint = (tx, 0) # send transactions to the node for tx in primary_mempool_chain[:-1]: conn.send_message(msg_tx(tx)) for tx in secondary_mempool_chain[:-1]: conn.send_message(msg_tx(tx)) # all transactions that are sent should en up in the mempool, chains are at the limit check_mempool_equals( conn.rpc, primary_mempool_chain[:-1] + secondary_mempool_chain[:-1]) # now send transactions that try to extend chain over the limit, should be rejected for tx_to_reject in [ primary_mempool_chain[-1], secondary_mempool_chain[-1] ]: conn.send_message(msg_tx(tx_to_reject)) wait_until(lambda: len(rejected_txs) == 1) assert_equal(rejected_txs[0].data, tx_to_reject.sha256) assert_equal(rejected_txs[0].reason, b'too-long-mempool-chain') rejected_txs.clear() # lets mine transactions from beggining of the chain, this will shorten the chains last_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) block = create_block( int(last_block_info["hash"], 16), coinbase=create_coinbase(height=last_block_info["height"] + 1), nTime=last_block_info["time"] + 1) block.vtx.append(primary_mempool_chain[0]) block.vtx.append(secondary_mempool_chain[0]) block.hashMerkleRoot = block.calc_merkle_root() block.calc_sha256() block.solve() conn.send_message(msg_block(block)) wait_until(lambda: conn.rpc.getbestblockhash() == block.hash, check_interval=0.3) # try to send transactions again, now chains are shorter and transactions will be accepted for tx_to_reject in [ primary_mempool_chain[-1], secondary_mempool_chain[-1] ]: conn.send_message(msg_tx(tx_to_reject)) check_mempool_equals( conn.rpc, primary_mempool_chain[1:] + secondary_mempool_chain[1:]) # invalidate the block, this will force mined transactions back to mempool # as we do not check chain lenght after reorg we will end up wit long chains in the mempool conn.rpc.invalidateblock(block.hash) check_mempool_equals( conn.rpc, primary_mempool_chain + secondary_mempool_chain)
def run_test(self): with self.run_node_with_connections( "Preparation", 0, [], number_of_connections=1) as (conn, ): self.start_node(1) self.start_node(2) connect_nodes_bi(self.nodes, 0, 1) connect_nodes_bi(self.nodes, 0, 2) # create block with coinbase coinbase = create_coinbase(height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=0.3) #mature the coinbase conn.rpc.generate(101) N_TX_IN_BLOCK = 30 funding_tx = self.create_tx([(coinbase, 0)], 3, 1000, 0) funding_tx_1 = self.create_tx([(funding_tx, 0)], N_TX_IN_BLOCK, 1000, 0) funding_tx_2 = self.create_tx([(funding_tx, 1)], N_TX_IN_BLOCK, 1000, 0) funding_tx_3 = self.create_tx([(funding_tx, 2)], N_TX_IN_BLOCK, 1000, 0) conn.send_message(msg_tx(funding_tx)) conn.send_message(msg_tx(funding_tx_1)) conn.send_message(msg_tx(funding_tx_2)) conn.send_message(msg_tx(funding_tx_3)) check_mempool_equals( conn.rpc, [funding_tx, funding_tx_1, funding_tx_2, funding_tx_3]) conn.rpc.generate(1) check_mempool_equals(conn.rpc, []) last_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) wait_until(lambda: self.nodes[1].getbestblockhash() == last_block_info["hash"]) wait_until(lambda: self.nodes[2].getbestblockhash() == last_block_info["hash"]) self.stop_node(1) self.stop_node(2) block = self.create_block(funding_tx=funding_tx_1, block_target_size=10 * ONE_MEGABYTE, prev_hash=int(last_block_info["hash"], 16), prev_height=last_block_info["height"], prev_time=last_block_info["time"]) block_bytes = block.serialize() block_size = len(block_bytes) block_bytes = None TARGET_SENDING_TIME_FAST = 50 TOTAL_BLOCK_DOWNLOAD_TIMEOUT = 60 TARGET_SENDING_TIME_SLOW = 70 target_send_rate_fast = block_size / TARGET_SENDING_TIME_FAST cmd_total_timeout = 100 * TOTAL_BLOCK_DOWNLOAD_TIMEOUT // ( 10 * 60) # 100% * (timeout) / 10min , target_send_rate_slow = block_size / TARGET_SENDING_TIME_SLOW """ CASE 1 """ self.test_send_block_to_node( label= "Single connection, sending slowly, setting only base timeout, timeout occures", node_index=0, block=block, send_rate=target_send_rate_slow, expected_time_to_send=TARGET_SENDING_TIME_SLOW, cmd_timeout_base=cmd_total_timeout, cmd_timeout_base_ibd= 1000000, # we are not in IBD so this param is not relevant cmd_timeout_per_peer= 1000000, # we are sending on only one connection so this param is not relevant expect_timeout=True, ) self.test_send_block_to_node( label= "Single connection, sending fast enough, setting only base timeout, timeout does not occure", node_index=0, block=block, send_rate=target_send_rate_fast, expected_time_to_send=TARGET_SENDING_TIME_FAST, cmd_timeout_base=cmd_total_timeout, cmd_timeout_base_ibd= 1, # we are not in IBD so this param is not relevant cmd_timeout_per_peer= 1, # we are sending on only one connection so this param is not relevant expect_timeout=False, ) """ CASE 2 """ self.test_send_block_to_node( label= "Single connection, node in the initial block download, setting only base IBD timeout, sending slowly, timeout occures", node_index=1, block=block, send_rate=target_send_rate_slow, expected_time_to_send=TARGET_SENDING_TIME_SLOW, cmd_timeout_base= 100000, # we are in IBD so this param is not relevant cmd_timeout_base_ibd=cmd_total_timeout, # we are in IBD cmd_timeout_per_peer= 100000, # we are sending on only one connection so this param is not relevant mocktime=int( time() + 48 * 60 * 60), # make node go 48 hours into future to put it in the IBD expect_timeout=True, ) self.test_send_block_to_node( label= "Single connection, node in the initial block download, setting only base IBD timeout, sending fast enough, timeout does not occure", node_index=1, block=block, send_rate=target_send_rate_fast, expected_time_to_send=TARGET_SENDING_TIME_FAST, cmd_timeout_base=1, # we are in IBD so this param is not relevant cmd_timeout_base_ibd=cmd_total_timeout, # we are in IBD cmd_timeout_per_peer= 1, # we are sending on only one connection so this param is not relevant mocktime=int( time() + 48 * 60 * 60), # make node go 48 hours into future to put it in the IBD expect_timeout=False, ) """ CASE 3 """ block_2 = self.create_block(funding_tx=funding_tx_2, block_target_size=10 * ONE_MEGABYTE, prev_hash=int(last_block_info["hash"], 16), prev_height=last_block_info["height"], prev_time=last_block_info["time"]) block_3 = self.create_block(funding_tx=funding_tx_3, block_target_size=10 * ONE_MEGABYTE, prev_hash=int(block_2.hash, 16), prev_height=last_block_info["height"] + 1, prev_time=last_block_info["time"] + 1) self.test_send_block_to_node( label= "Two connections, on both connections we are sending blocks, sending slowly, timeout occures", node_index=2, block=block, send_rate=target_send_rate_slow, expected_time_to_send=TARGET_SENDING_TIME_SLOW, cmd_timeout_base=cmd_total_timeout // 2, # half of the timeout is contributed by the base cmd_timeout_base_ibd= 100000, # we are not in IBD so this param is not relevant cmd_timeout_per_peer=cmd_total_timeout // 2, # another half of the timeout is contributed by the additional (single) per peer additional_conn_blocks=[ block_2, block_3 ], # blocks to send on through additional connection additional_conn_send_rate=target_send_rate_slow, expect_timeout=True, ) self.test_send_block_to_node( label= "Two connections, on both connections we are sending blocks, sending slowly, sending fast enough, timeout does not occure", node_index=2, block=block, send_rate=target_send_rate_fast, expected_time_to_send=TARGET_SENDING_TIME_FAST, cmd_timeout_base=cmd_total_timeout // 2, # half of the timeout is contributed by the base cmd_timeout_base_ibd= 1, # we are not in IBD so this param is not relevant cmd_timeout_per_peer=cmd_total_timeout // 2, # another half of the timeout is contributed by the additional (single) per peer additional_conn_blocks=[ block_2, block_3 ], # blocks to send on through additional connection additional_conn_send_rate=target_send_rate_slow, expect_timeout=False, )
def run_test(self): with self.run_node_with_connections( "Scenario 1: Low fee, non-whitelisted peer", 0, ["-blockmintxfee=0.00001"], number_of_connections=1) as (conn, ): mining_fee = 1.01 # in satoshi per byte relayfee = float(conn.rpc.getnetworkinfo()["relayfee"] * COIN / 1000) + 0.01 # in satoshi per byte # create block with coinbase coinbase = create_coinbase(height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=0.3) #mature the coinbase conn.rpc.generate(150) funding_tx = self.create_tx([(coinbase, 0)], 10, 1.5) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) conn.rpc.generate(1) last_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) block = create_block( int(last_block_info["hash"], 16), coinbase=create_coinbase(height=last_block_info["height"] + 1), nTime=last_block_info["time"] + 1) low_fee_tx = self.create_tx([(funding_tx, 0)], 2, relayfee) block.vtx.append(low_fee_tx) block.hashMerkleRoot = block.calc_merkle_root() block.calc_sha256() block.solve() conn.send_message(msg_block(block)) wait_until(lambda: conn.rpc.getbestblockhash() == block.hash, check_interval=0.3) tx_pays_relay1 = self.create_tx([(low_fee_tx, 0)], 2, relayfee) tx_pays_relay2 = self.create_tx([(tx_pays_relay1, 0)], 1, relayfee) tx_pays_enough_for_itself = self.create_tx([(tx_pays_relay1, 1)], 1, mining_fee) tx_pays_for_ancestors = self.create_tx([(tx_pays_relay2, 0)], 1, 3.5 * mining_fee) tx_pays_relay3 = self.create_tx([(tx_pays_for_ancestors, 0)], 1, relayfee) conn.send_message(msg_tx(tx_pays_relay1)) check_mempool_equals(conn.rpc, [tx_pays_relay1]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1 ) #"should be coinbase only" conn.send_message(msg_tx(tx_pays_relay2)) check_mempool_equals(conn.rpc, [tx_pays_relay1, tx_pays_relay2]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1 ) #"should be coinbase only" conn.send_message(msg_tx(tx_pays_enough_for_itself)) check_mempool_equals( conn.rpc, [tx_pays_relay1, tx_pays_relay2, tx_pays_enough_for_itself]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1 ) #"should be coinbase only" #try to rebuild journal, mining candidate should stay the same conn.rpc.rebuildjournal() wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1) # this will trigger cpfp for two unpaying antcestors (tx_pays_relay1 and tx_pays_relay1) then # after that tx_pays_enough_for_itself will be free of ancestor debt and it will be accepted also conn.send_message(msg_tx(tx_pays_for_ancestors)) check_mempool_equals(conn.rpc, [ tx_pays_relay1, tx_pays_relay2, tx_pays_enough_for_itself, tx_pays_for_ancestors ]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 5 ), #"all tx plus coinbase" # still, non paying child of the tx_pays_for_ancestors will not be accepted conn.send_message(msg_tx(tx_pays_relay3)) check_mempool_equals(conn.rpc, [ tx_pays_relay1, tx_pays_relay2, tx_pays_enough_for_itself, tx_pays_for_ancestors, tx_pays_relay3 ]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 5 ), #"all tx, except tx_pays_relay3 plus coinbase" #try to rebuild journal, mining candidate should stay the same conn.rpc.rebuildjournal() wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 5) # we will mine a new block, all transactions from the journal will end up in new block, mempool will contain # only tx_pays_relay3 conn.rpc.generate(1) check_mempool_equals(conn.rpc, [tx_pays_relay3]) # now we invalidate block two blocks, at this point tx_pays_for_ancestors does not pay enough for # all non-paying children, nothing will end up in the journal. # non paying children are: low_fee_tx, tx_pays_relay1, tx_pays_relay2 conn.rpc.invalidateblock(block.hash) check_mempool_equals(conn.rpc, [ low_fee_tx, tx_pays_relay1, tx_pays_relay2, tx_pays_enough_for_itself, tx_pays_for_ancestors, tx_pays_relay3 ]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1 ) #"should be coinbase only" # when we reconsider invalidate block, everything should be the same conn.rpc.reconsiderblock(block.hash) check_mempool_equals(conn.rpc, [tx_pays_relay3]) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1 ) #"should be coinbase only"
def run_test(self): with self.run_node_with_connections( "Preparation", 0, [ "-blockmintxfee=0.00001", "-relayfee=0.000005", "-checkmempool=0", ], number_of_connections=1) as (conn, ): mining_fee = 1.1 # create block with coinbase coinbase = create_coinbase(pubkey=self.public_key, height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=0.3) # mature the coinbase conn.rpc.generate(150) funding_tx = self.create_tx([(coinbase, 0)], 2006, mining_fee) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) # generates a root block with our funding transaction conn.rpc.generate(1) # create 2000 standard p2pk transactions a1_txs = [] for m in range(2000): a1_txs.append(self.create_tx([(funding_tx, m)], 1, mining_fee)) a1_spends = [] for a1_tx in a1_txs: a1_spends.append(self.create_tx([(a1_tx, 0)], 1, mining_fee)) # create 2000 standard p2pk transactions which are spending the same outputs as a1_txs double_spend_txs = [] for m in range(2000): double_spend_txs.append( self.create_tx([(funding_tx, m)], 1, mining_fee)) TX_COUNT = 8 # create for pairs of long-evaluating transactions for blocks b1, b2, c1, and c2 long_eval_txs = [] for m in range(2000, 2006): long_eval_txs.append( self.create_tx([(funding_tx, m)], 1, 0.0001, make_long_eval_script=True)) for _ in range(TX_COUNT - 1): long_eval_txs.append( self.create_tx([(long_eval_txs[-1], 0)], 1, 0.0001, make_long_eval_script=True)) root_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) root_hash = root_block_info["hash"] root_height = root_block_info["height"] root_time = root_block_info["time"] # create all blocks needed for this test block_a1 = self.make_block(a1_txs, root_hash, root_height, root_time) block_b1 = self.make_block( long_eval_txs[0 * TX_COUNT:1 * TX_COUNT], root_hash, root_height, root_time) block_b2 = self.make_block( long_eval_txs[1 * TX_COUNT:2 * TX_COUNT], block_b1.hash, root_height + 1, root_time + 100) block_c1 = self.make_block( long_eval_txs[2 * TX_COUNT:3 * TX_COUNT], root_hash, root_height, root_time) block_c2 = self.make_block( long_eval_txs[3 * TX_COUNT:4 * TX_COUNT], block_c1.hash, root_height + 1, root_time + 101) block_d1 = self.make_block( long_eval_txs[4 * TX_COUNT:5 * TX_COUNT], root_hash, root_height, root_time) block_d2 = self.make_block( long_eval_txs[5 * TX_COUNT:6 * TX_COUNT], block_d1.hash, root_height + 1, root_time + 102) conn.send_message(msg_block(block_a1)) wait_until(lambda: conn.rpc.getbestblockhash() == block_a1.hash, check_interval=0.3) with self.run_node_with_connections( "1. Try sending the same transaction that are in the disconnected block during the reorg", 0, [ "-blockmintxfee=0.00001", "-relayfee=0.000005", "-maxtxsizepolicy=0", "-maxstdtxnsperthreadratio=1", "-maxnonstdtxnsperthreadratio=1", '-maxnonstdtxvalidationduration=100000', '-maxtxnvalidatorasynctasksrunduration=100001', '-genesisactivationheight=1', '-maxstackmemoryusageconsensus=2GB', "-maxscriptsizepolicy=2GB", "-acceptnonstdoutputs=1", ], number_of_connections=1) as (conn, ): # send all transactions form block a1 at once and flood the PTV for tx in a1_txs: conn.send_message(msg_tx(tx)) # announce blocks b1, and b2 and send them triggering the reorg headers = msg_headers() headers.headers.append(block_b1) headers.headers.append(block_b2) conn.send_message(headers) conn.send_message(msg_block(block_b1)) conn.send_message(msg_block(block_b2)) # here we are having the PTV and PBV working at the same time, filling the mempool while # the a1 is disconnected # check if everything is as expected wait_until(lambda: conn.rpc.getbestblockhash() == block_b2.hash, timeout=60, check_interval=1) check_mempool_equals(conn.rpc, a1_txs) # now prepare for next scenario conn.rpc.invalidateblock(block_b1.hash) wait_until(lambda: conn.rpc.getbestblockhash() == block_a1.hash, check_interval=1) # transactions from the disconnected blocks b1 and b2 will not be added to mempool because of # the insufficient priority (zero fee) check_mempool_equals(conn.rpc, [], timeout=60, check_interval=1) with self.run_node_with_connections( "2. Try sending transaction that are spending same inputs as transactions in the disconnected block during the reorg", 0, [ "-blockmintxfee=0.00001", "-relayfee=0.000005", "-maxtxsizepolicy=0", "-maxstdtxnsperthreadratio=1", "-maxnonstdtxnsperthreadratio=1", '-maxnonstdtxvalidationduration=100000', '-maxtxnvalidatorasynctasksrunduration=100001', '-genesisactivationheight=1', '-maxstackmemoryusageconsensus=2GB', "-maxscriptsizepolicy=2GB", "-acceptnonstdoutputs=1", ], number_of_connections=1) as (conn, ): # see if everything is still as expected wait_until(lambda: conn.rpc.getbestblockhash() == block_a1.hash, check_interval=1) check_mempool_equals(conn.rpc, [], timeout=60, check_interval=1) # send all transactions that are the double-spends of txs form block a1 for double_spend_tx in double_spend_txs: conn.send_message(msg_tx(double_spend_tx)) # announce and send c1, and c2 headers = msg_headers() headers.headers.append(block_c1) headers.headers.append(block_c2) conn.send_message(headers) conn.send_message(msg_block(block_c1)) conn.send_message(msg_block(block_c2)) # here we are having the PTV and PBV working at the same time, filling the mempool with double-spends # while the a1 is disconnected # see if everything is as expected wait_until(lambda: conn.rpc.getbestblockhash() == block_c2.hash, timeout=60, check_interval=1) # in the mempool we want all transactions for blocks a1 # while no double_spend_txs should be present check_mempool_equals(conn.rpc, a1_txs, timeout=60, check_interval=1) # now prepare for next scenario conn.rpc.invalidateblock(block_c1.hash) wait_until(lambda: conn.rpc.getbestblockhash() == block_a1.hash, check_interval=1) # transactions from the disconnected blocks c1 and c2 will not be added to mempool because of # the insufficient priority (zero fee) check_mempool_equals(conn.rpc, [], timeout=60, check_interval=1) with self.run_node_with_connections( "3. Submit transactions that are spending ouputs from disconnecting block and try to mine a block during the reorg", 0, [ "-blockmintxfee=0.00001", "-relayfee=0.000005", "-maxtxsizepolicy=0", '-maxnonstdtxvalidationduration=100000', '-maxtxnvalidatorasynctasksrunduration=100001', '-genesisactivationheight=1', '-maxstackmemoryusageconsensus=2GB', "-maxscriptsizepolicy=2GB", "-acceptnonstdoutputs=1", ], number_of_connections=1) as (conn, ): # see if everything is still as expected wait_until(lambda: conn.rpc.getbestblockhash() == block_a1.hash, check_interval=1) check_mempool_equals(conn.rpc, [], timeout=60, check_interval=1) for tx in a1_spends: conn.send_message(msg_tx(tx)) # send transactions that are spending outputs from the soon-to-be-disconnected block (a1) check_mempool_equals(conn.rpc, a1_spends, timeout=100) # announce blocks d1, and d2 and send them triggering the reorg headers = msg_headers() headers.headers.append(block_d1) headers.headers.append(block_d2) conn.send_message(headers) conn.send_message(msg_block(block_d1)) conn.send_message(msg_block(block_d2)) # lets give a chance for reorg to start sleep(0.5) # we are in the middle of the reorg, let try to mine a block # if we are in inconsistent state this call would fail conn.rpc.generate(1)
def _test_chain(self, outpoint1, outpoint2): limitancestorcount = 20 limitcpfpgroupmemberscount = 10 with self.run_node_with_connections( "Tests for ancestors count limit for primary and secondary mempool. " "Transactions arranged in chain.", 0, [ "-blockmintxfee=0.00001", "-relayfee=0.000005", f"-limitancestorcount={limitancestorcount}", f"-limitcpfpgroupmemberscount={limitcpfpgroupmemberscount}", "-checkmempool=1", ], number_of_connections=1) as (conn, ): mining_fee = 1.001 # in satoshi per byte relayfee = 0.501 # in satoshi per byte rejected_txs = [] def on_reject(conn, msg): rejected_txs.append(msg) conn.cb.on_reject = on_reject # create oversized primary mempool chain, the last tx in the chain will be over the limit last_outpoint = outpoint1 primary_mempool_chain = [] for _ in range(limitancestorcount + 1): tx = self.create_tx([last_outpoint], 1, mining_fee) primary_mempool_chain.append(tx) last_outpoint = (tx, 0) # create oversized secondary mempool chain, the last tx in the chain will be over the limit last_outpoint = outpoint2 secondary_mempool_chain = [] for _ in range(limitcpfpgroupmemberscount + 1): tx = self.create_tx([last_outpoint], 1, relayfee) secondary_mempool_chain.append(tx) last_outpoint = (tx, 0) # send transactions to the node for tx in primary_mempool_chain[:-1]: conn.send_message(msg_tx(tx)) for tx in secondary_mempool_chain[:-1]: conn.send_message(msg_tx(tx)) # all transactions that are sent should en up in the mempool, chains are at the limit check_mempool_equals( conn.rpc, primary_mempool_chain[:-1] + secondary_mempool_chain[:-1]) # now send transactions that try to extend chain over the limit, should be rejected for tx_to_reject in [ primary_mempool_chain[-1], secondary_mempool_chain[-1] ]: conn.send_message(msg_tx(tx_to_reject)) wait_until(lambda: len(rejected_txs) == 1) assert_equal(rejected_txs[0].data, tx_to_reject.sha256) assert_equal(rejected_txs[0].reason, b'too-long-mempool-chain') rejected_txs.clear() # lets mine transactions from beggining of the chain, this will shorten the chains block = self.mine_transactions( conn, [primary_mempool_chain[0], secondary_mempool_chain[0]]) # try to send transactions again, now chains are shorter and transactions will be accepted for tx_to_reject in [ primary_mempool_chain[-1], secondary_mempool_chain[-1] ]: conn.send_message(msg_tx(tx_to_reject)) check_mempool_equals( conn.rpc, primary_mempool_chain[1:] + secondary_mempool_chain[1:]) # invalidate the block, this will force mined transactions back to mempool # as we do not check chain length after reorg we will end up with long chains in the mempool conn.rpc.invalidateblock(block.hash) check_mempool_equals( conn.rpc, primary_mempool_chain + secondary_mempool_chain) # mine all txs from mempool to ensure empty mempool for the next test case self.mine_transactions( conn, primary_mempool_chain + secondary_mempool_chain)
def _test_graph(self, outpoint, mempool_type): if mempool_type == "primary": limitancestorcount = 3 limitcpfpgroupmemberscount = 1000 elif mempool_type == "secondary": limitancestorcount = 1000 limitcpfpgroupmemberscount = 7 else: raise Exception("Unsupported mempool type") with self.run_node_with_connections( f"Tests for ancestors count limit in {mempool_type} mempool." "Transactions arranged in graph.", 0, [ "-blockmintxfee=0.00001", "-relayfee=0.000005", f"-limitancestorcount={limitancestorcount}", f"-limitcpfpgroupmemberscount={limitcpfpgroupmemberscount}", "-checkmempool=1", ], number_of_connections=1) as (conn, ): # ensure that the mempool is empty check_mempool_equals(conn.rpc, []) rejected_txs = [] def on_reject(conn, msg): rejected_txs.append(msg) conn.cb.on_reject = on_reject mining_fee = 1.001 # in satoshi per byte relayfee = 0.501 # in satoshi per byte # create trasactions # <transaction_name> (<prim_count>, <sec_count>) # # prim_count is ancestors count in primary mempool, algorithm == max # sec_count is ancestors count in secondary mempool, algorithm == sum # # # tx1 (0, 0) # +------------+ | +------------+ # | | | # tx2 (1, 1) tx3 (1, 1) tx4 (1, 1) # | | | # +------------+ | +------------+ # tx5 (2, 6) # | # | # tx6 (3, 7) fee = mining_fee if mempool_type == "primary" else relayfee tx1 = self.create_tx([outpoint], 3, fee) tx2 = self.create_tx([(tx1, 0)], 1, fee) tx3 = self.create_tx([(tx1, 1)], 1, fee) tx4 = self.create_tx([(tx1, 2)], 1, fee) tx5 = self.create_tx([(tx2, 0), (tx3, 0), (tx4, 0)], 1, fee) tx6 = self.create_tx([(tx5, 0)], 1, fee) conn.send_message(msg_tx(tx1)) conn.send_message(msg_tx(tx2)) conn.send_message(msg_tx(tx3)) conn.send_message(msg_tx(tx4)) conn.send_message(msg_tx(tx5)) # up to now all txs are accepted check_mempool_equals(conn.rpc, [tx1, tx2, tx3, tx4, tx5]) # tx6 will be rejected because it is limit of ancestors count conn.send_message(msg_tx(tx6)) wait_until(lambda: len(rejected_txs) == 1) assert_equal(rejected_txs[0].data, tx6.sha256) assert_equal(rejected_txs[0].reason, b'too-long-mempool-chain') # now mine tx1 to shorten the chain block = self.mine_transactions(conn, [tx1]) # now we can add tx6 to mempool conn.send_message(msg_tx(tx6)) check_mempool_equals(conn.rpc, [tx2, tx3, tx4, tx5, tx6]) # invalidate the block, this will force mined transactions back to mempool # as we do not check chain length after reorg we will end up with long chains in the mempool conn.rpc.invalidateblock(block.hash) check_mempool_equals(conn.rpc, [tx1, tx2, tx3, tx4, tx5, tx6]) # mine all txs from mempool to ensure empty mempool for the next test case self.mine_transactions(conn, [tx1, tx2, tx3, tx4, tx5, tx6])
def run_test(self): with self.run_node_with_connections( "Eviction order test; fill the memppol over its size and see what txs will be evicted.", 0, [ "-blockmintxfee=0.00001", # 1 satoshi/byte "-minrelaytxfee=0", "-maxmempool=300MB", "-maxmempoolsizedisk=0", "-genesisactivationheight=1", '-maxstdtxvalidationduration=5000', '-maxnonstdtxvalidationduration=5001', '-maxstackmemoryusageconsensus=5MB', '-maxstackmemoryusagepolicy=5MB', '-maxscriptsizepolicy=5MB', '-checkmempool=0', ], number_of_connections=1) as (conn, ): mining_fee = 1.01 # in satoshi per byte # create block with coinbase coinbase1 = create_coinbase(height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase1) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=1) coinbase2 = create_coinbase(height=2) second_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase2) second_block.solve() conn.send_message(msg_block(second_block)) wait_until( lambda: conn.rpc.getbestblockhash() == second_block.hash, check_interval=1) #mature the coinbase conn.rpc.generate(100) funding_tx = self.create_tx([(coinbase1, 0), (coinbase2, 0)], 16, mining_fee, 0) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) conn.rpc.generate(1) # (funding_tx, 0) (funding_tx, 1) (funding_tx, 2) (funding_tx, 3) (funding_tx, 4-14) (funding_tx, 15) # --------------------------------------------------------------------------------------------------------------------- # group1tx1 lowPaying3 tx1 tx3 (long chain of (chain of high # | | | | high paying txs) paying txs used to # group1tx2 lowPaying4 tx2 lowPaying5 to fill the mempool) push low paying txs # / \ out of mempools) # group1paying group2tx1 # | | # lowPaying1 group2tx2 # | # group2paying # | # lowPaying2 group1tx1 = self.create_tx([(funding_tx, 0)], noutput=1, feerate=0, totalSize=ONE_MEGABYTE) group1tx2 = self.create_tx([(group1tx1, 0)], noutput=2, feerate=0, totalSize=ONE_MEGABYTE) group1paying = self.create_tx( [(group1tx2, 0)], noutput=1, feerate=1.4, totalSize=ONE_MEGABYTE, size_of_nonpayin_txs=self.tx_size(group1tx1) + self.tx_size(group1tx2)) group2tx1 = self.create_tx([(group1tx2, 1)], noutput=1, feerate=0, totalSize=ONE_MEGABYTE) group2tx2 = self.create_tx([(group2tx1, 0)], noutput=1, feerate=0, totalSize=ONE_MEGABYTE) group2paying = self.create_tx( [(group2tx2, 0)], noutput=1, feerate=1.6, totalSize=ONE_MEGABYTE, size_of_nonpayin_txs=self.tx_size(group2tx1) + self.tx_size(group2tx2)) tx1 = self.create_tx([(funding_tx, 2)], noutput=1, feerate=1.1, totalSize=ONE_MEGABYTE) tx2 = self.create_tx([(tx1, 0)], noutput=1, feerate=1.8, totalSize=ONE_MEGABYTE) tx3 = self.create_tx([(funding_tx, 3)], noutput=1, feerate=1.1, totalSize=ONE_MEGABYTE) lowPaying1 = self.create_tx([(group1paying, 0)], noutput=1, feerate=0.1, totalSize=ONE_MEGABYTE) lowPaying2 = self.create_tx([(group2paying, 0)], noutput=1, feerate=0.2, totalSize=ONE_MEGABYTE) lowPaying3 = self.create_tx([(funding_tx, 1)], noutput=1, feerate=0.3, totalSize=ONE_MEGABYTE) lowPaying4 = self.create_tx([(lowPaying3, 0)], noutput=1, feerate=0.4, totalSize=ONE_MEGABYTE) lowPaying5 = self.create_tx([(tx3, 0)], noutput=1, feerate=0.5, totalSize=ONE_MEGABYTE) primaryMempoolTxs = [ group1tx1, group1tx2, group1paying, group2tx1, group2tx2, group2paying, tx1, tx2, tx3 ] secondaryMempoolTxs = [ lowPaying1, lowPaying2, lowPaying3, lowPaying4, lowPaying5 ] for tx in primaryMempoolTxs + secondaryMempoolTxs: conn.send_message(msg_tx(tx)) check_mempool_equals(conn.rpc, primaryMempoolTxs + secondaryMempoolTxs) wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == len( primaryMempoolTxs) + 1) txs_in_mempool = set(primaryMempoolTxs + secondaryMempoolTxs) outpoints_to_spend = [(funding_tx, n) for n in range(4, 15)] while len(txs_in_mempool) < 299: tx = self.create_tx([ outpoints_to_spend.pop(0), ], noutput=2, feerate=5, totalSize=ONE_MEGABYTE) outpoints_to_spend.append((tx, 0)) outpoints_to_spend.append((tx, 1)) conn.send_message(msg_tx(tx)) txs_in_mempool.add(tx) check_mempool_equals(conn.rpc, txs_in_mempool, timeout=600, check_interval=2) eviction_order = [ lowPaying1, lowPaying2, lowPaying4, lowPaying3, lowPaying5, tx3, group2paying, group2tx2, group2tx1, group1paying, group1tx2, group1tx1, tx2, tx1 ] conn.rpc.log.info(f"lowPaying1 = {lowPaying1.hash}") conn.rpc.log.info(f"lowPaying2 = {lowPaying2.hash}") conn.rpc.log.info(f"lowPaying3 = {lowPaying3.hash}") conn.rpc.log.info(f"lowPaying4 = {lowPaying4.hash}") conn.rpc.log.info(f"lowPaying5 = {lowPaying5.hash}") conn.rpc.log.info(f"tx1 = {tx1.hash}") conn.rpc.log.info(f"tx2 = {tx2.hash}") conn.rpc.log.info(f"tx3 = {tx3.hash}") conn.rpc.log.info(f"group2paying = {group2paying.hash}") conn.rpc.log.info(f"group2tx2 = {group2tx2.hash}") conn.rpc.log.info(f"group2tx1 = {group2tx1.hash}") conn.rpc.log.info(f"group1paying = {group1paying.hash}") conn.rpc.log.info(f"group1tx2 = {group1tx2.hash}") conn.rpc.log.info(f"group1tx1 = {group1tx1.hash}") outpoint_to_spend = (funding_tx, 15) for evicting in eviction_order: tx = self.create_tx([ outpoint_to_spend, ], noutput=1, feerate=30, totalSize=ONE_MEGABYTE) outpoint_to_spend = (tx, 0) conn.send_message(msg_tx(tx)) txs_in_mempool.add(tx) txs_in_mempool.remove(evicting) check_mempool_equals(conn.rpc, txs_in_mempool, check_interval=0.5, timeout=60) # when there are still some secondary mempool transaction in the mempool if len(txs_in_mempool & set(secondaryMempoolTxs)) != 0: # the mempoolminfee should not exceed blockmintxfee assert conn.rpc.getmempoolinfo( )['mempoolminfee'] <= conn.rpc.getsettings( )['blockmintxfee'] with self.run_node_with_connections( "Restart the node with using the disk for storing transactions.", 0, [ "-blockmintxfee=0.00001", # 1 satoshi/byte "-minrelaytxfee=0", "-maxmempool=300MB", "-maxmempoolsizedisk=10MB", "-genesisactivationheight=1", '-maxstdtxvalidationduration=5000', '-maxnonstdtxvalidationduration=5001', '-maxstackmemoryusageconsensus=5MB', '-maxstackmemoryusagepolicy=5MB', '-maxscriptsizepolicy=5MB', '-checkmempool=0', ], number_of_connections=1) as (conn, ): # check that we have all txs in the mempool check_mempool_equals(conn.rpc, txs_in_mempool, check_interval=1, timeout=60) # check that we are not using the tx database assert conn.rpc.getmempoolinfo()['usagedisk'] == 0 #now we have room for some more txs for _ in range(3): tx = self.create_tx([ outpoint_to_spend, ], noutput=1, feerate=1, totalSize=ONE_MEGABYTE) outpoint_to_spend = (tx, 0) conn.send_message(msg_tx(tx)) txs_in_mempool.add(tx) check_mempool_equals(conn.rpc, txs_in_mempool, check_interval=0.5, timeout=60) # make sure that we are using the tx database now assert conn.rpc.getmempoolinfo()['usagedisk'] != 0 with self.run_node_with_connections( "Restart the node once again to see if transaction were stored in the db.", 0, [ "-blockmintxfee=0.00001", # 1 satoshi/byte "-minrelaytxfee=0", "-maxmempool=300MB", "-maxmempoolsizedisk=10MB", "-genesisactivationheight=1", '-maxstdtxvalidationduration=5000', '-maxnonstdtxvalidationduration=5001', '-maxstackmemoryusageconsensus=5MB', '-maxstackmemoryusagepolicy=5MB', '-maxscriptsizepolicy=5MB', '-checkmempool=0', ], number_of_connections=1) as (conn, ): # check that we have all txs in the mempool check_mempool_equals(conn.rpc, txs_in_mempool, check_interval=1, timeout=60) # make sure that we are using the tx database assert conn.rpc.getmempoolinfo()['usagedisk'] != 0
def run_cpfp_scenario1(self, conn, txchains, last_descendant_from_each_txchain, chain_length, num_of_chains, mining_fee, locking_script, rpcsend=None, timeout=240): # # Send low fee (paying relay_fee) txs to the node. # exp_mempool_size = conn.rpc.getmempoolinfo()['size'] + len(txchains) elapsed1 = self.send_txs(rpcsend, conn, txchains, exp_mempool_size, timeout) # Check if mempool contains all low fee txs. check_mempool_equals(conn.rpc, txchains, timeout) # # Check getminingcandidate result: There should be no cpfp txs in the block template, due to low fees. # wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == 1 ) # there should be coinbase tx only # # Create and send cpfp txs (paying mining_fee). # cpfp_txs_pay_for_ancestors = [] for tx in last_descendant_from_each_txchain: cpfp_txs_pay_for_ancestors.append( self.create_tx([(tx, 0)], 2, (chain_length + 1) * (mining_fee), locking_script)) # Send cpfp txs. exp_mempool_size = conn.rpc.getmempoolinfo()['size'] + len( cpfp_txs_pay_for_ancestors) elapsed2 = self.send_txs(rpcsend, conn, cpfp_txs_pay_for_ancestors, exp_mempool_size, timeout) # Check if there is a required number of txs in the mempool. check_mempool_equals(conn.rpc, cpfp_txs_pay_for_ancestors + txchains, timeout) # # Check getminingcandidate result: There should be all cpfp txs (+ ancestor txs) in the block template. # wait_until(lambda: conn.rpc.getminingcandidate()["num_tx"] == len( cpfp_txs_pay_for_ancestors + txchains) + 1) # +1 a coinbase tx # # Collect stats. # interface_name = "p2p" if rpcsend is None else rpcsend._service_name self.log.info( "[%s]: Submit and process %d txchains of length %d (%d relay_fee std txs) [time duration: %.6f sec]", interface_name, num_of_chains, chain_length, num_of_chains * chain_length, elapsed1) self.log.info( "[%s]: Submit and process %d cpfp std txs (each pays mining_fee) [time duration: %.6f sec]", interface_name, len(cpfp_txs_pay_for_ancestors), elapsed2) self.log.info( "[%s]: Total time to submit and process %d std txs took %.6f sec", interface_name, num_of_chains * chain_length + len(cpfp_txs_pay_for_ancestors), elapsed1 + elapsed2) return elapsed1 + elapsed2
def run_test(self): with self.run_node_with_connections( "Scenario 1: Create complex graph of txs, doublespend some fo them and check mempool after", 0, ["-minrelaytxfee=0"], number_of_connections=1) as (conn, ): # create block with coinbase coinbase = create_coinbase(height=1) first_block = create_block(int(conn.rpc.getbestblockhash(), 16), coinbase=coinbase) first_block.solve() conn.send_message(msg_block(first_block)) wait_until(lambda: conn.rpc.getbestblockhash() == first_block.hash, check_interval=0.3) #mature the coinbase conn.rpc.generate(150) funding_tx = self.create_tx([(coinbase, 0)], 2) conn.send_message(msg_tx(funding_tx)) check_mempool_equals(conn.rpc, [funding_tx]) conn.rpc.generate(1) last_block_info = conn.rpc.getblock(conn.rpc.getbestblockhash()) block = create_block( int(last_block_info["hash"], 16), coinbase=create_coinbase(height=last_block_info["height"] + 1), nTime=last_block_info["time"] + 1) low_fee_tx = self.create_tx([(funding_tx, 0)], 2) block.vtx.append(low_fee_tx) block.hashMerkleRoot = block.calc_merkle_root() block.calc_sha256() block.solve() conn.send_message(msg_block(block)) wait_until(lambda: conn.rpc.getbestblockhash() == block.hash, check_interval=0.3) # tx_double_spend_mempool tx_to_be_mined # | | | | # ------------------------------------------------------- # | | | | # | +-------+ +--------+ | # | | | | # | tx_descedant_of_conflict_1 tx_stay_in_mempool_1 # | | | | # | | | | # | tx_descedant_of_conflict_2 | tx_stay_in_mempool_2 # | | | | # | | | | # | tx_descedant_of_conflict_3 | # | | # +---------+ +--------------------------------+ # | | # tx_descedant_of_conflict_4 tx_double_spend_mempool = self.create_tx([(low_fee_tx, 0)], 2) tx_double_spend_block = self.create_tx([(low_fee_tx, 0)], 1) tx_to_be_mined = self.create_tx([(low_fee_tx, 1)], 2) tx_descedant_of_conflict_1 = self.create_tx( [(tx_double_spend_mempool, 0), (tx_to_be_mined, 0)], 1) tx_descedant_of_conflict_2 = self.create_tx( [(tx_descedant_of_conflict_1, 0)], 1) tx_stay_in_mempool_1 = self.create_tx([(tx_to_be_mined, 1)], 2) tx_descedant_of_conflict_3 = self.create_tx( [(tx_descedant_of_conflict_2, 0), (tx_stay_in_mempool_1, 0)], 2) tx_stay_in_mempool_2 = self.create_tx([(tx_stay_in_mempool_1, 1)], 1) tx_descedant_of_conflict_4 = self.create_tx( [(tx_double_spend_mempool, 1), (tx_stay_in_mempool_2, 0)], 1) conn.send_message(msg_tx(tx_double_spend_mempool)) conn.send_message(msg_tx(tx_to_be_mined)) conn.send_message(msg_tx(tx_descedant_of_conflict_1)) conn.send_message(msg_tx(tx_descedant_of_conflict_2)) conn.send_message(msg_tx(tx_stay_in_mempool_1)) conn.send_message(msg_tx(tx_descedant_of_conflict_3)) conn.send_message(msg_tx(tx_stay_in_mempool_2)) conn.send_message(msg_tx(tx_descedant_of_conflict_4)) check_mempool_equals(conn.rpc, [ tx_double_spend_mempool, tx_to_be_mined, tx_descedant_of_conflict_1, tx_descedant_of_conflict_2, tx_stay_in_mempool_1, tx_descedant_of_conflict_3, tx_stay_in_mempool_2, tx_descedant_of_conflict_4 ]) block2 = create_block( block.sha256, coinbase=create_coinbase(height=last_block_info["height"] + 2), nTime=last_block_info["time"] + 2) block2.vtx.append(tx_double_spend_block) block2.vtx.append(tx_to_be_mined) block2.hashMerkleRoot = block2.calc_merkle_root() block2.calc_sha256() block2.solve() conn.send_message(msg_block(block2)) wait_until(lambda: conn.rpc.getbestblockhash() == block2.hash, check_interval=0.3) check_mempool_equals(conn.rpc, [tx_stay_in_mempool_1, tx_stay_in_mempool_2])