def generate_and_send_txchains_n(self, conn, num_of_chains, chain_length, spend, locking_script, money_to_spend=2000000, vout_size=10, timeout=60): # Create and send txs. In this case there will be num_txs_to_create txs of chain length equal 1. txchains = self.get_txchains_n(num_of_chains, chain_length, spend, CScript(), locking_script, money_to_spend, vout_size) for tx in range(len(txchains)): conn.send_message(msg_tx(txchains[tx])) # Check if the validation queues are empty. wait_for_ptv_completion(conn, num_of_chains*chain_length, timeout=timeout) return txchains
def run_scenario1(self, conn, num_of_chains, chain_length, spend, timeout): # Create and send tx chains. txchains = self.get_txchains_n(num_of_chains, chain_length, spend) for tx in range(len(txchains)): conn.send_message(msg_tx(txchains[tx])) # Check if the validation queues are empty. wait_for_ptv_completion(conn, num_of_chains * chain_length, timeout=timeout) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, txchains, timeout)
def send_txs(self, rpcsend, conn, txs, exp_mempool_size, timeout=300, check_interval=0.1): conn = None if rpcsend is not None else conn if conn is not None: req_start_time = time.time() for tx in txs: conn.send_message(msg_tx(tx)) wait_for_ptv_completion(conn, exp_mempool_size, timeout=timeout, check_interval=check_interval) elapsed = time.time() - req_start_time elif rpcsend is not None: elapsed = self.rpc_send_txs(rpcsend, txs) else: raise Exception("Unspecified interface!") return elapsed
def run_scenario2(self, conn, num_of_chains, chain_length, spend, allowhighfees=False, dontcheckfee=False, timeout=60): # Create tx chains. txchains = self.run_scenario1(conn, num_of_chains, chain_length, spend, allowhighfees, dontcheckfee, timeout) wait_for_ptv_completion(conn, len(txchains), timeout=timeout) # Check if txchains txns are in the mempool. self.check_mempool(conn.rpc, set(txchains), timeout=60) # Check if there is only num_of_chains * chain_length txns in the mempool. assert_equal(conn.rpc.getmempoolinfo()['size'], len(txchains)) # Generate a single block. mined_block1 = conn.rpc.generate(1) # Mempool should be empty, all txns in the block. assert_equal(conn.rpc.getmempoolinfo()['size'], 0) # Use invalidateblock to re-org back; all transactions should # end up unconfirmed and back in the mempool. conn.rpc.invalidateblock(mined_block1[0]) # There should be exactly num_of_chains * chain_length txns in the mempool. assert_equal(conn.rpc.getmempoolinfo()['size'], len(txchains)) self.check_mempool(conn.rpc, set(txchains)) # Generate another block, they should all get mined. mined_block2 = conn.rpc.generate(1) # Mempool should be empty, all txns confirmed. assert_equal(conn.rpc.getmempoolinfo()['size'], 0) # Check if txchains txns are included in the block. mined_block2_details = conn.rpc.getblock(mined_block2[0]) assert_equal(mined_block2_details['num_tx'], len(txchains) + 1) # +1 for coinbase txn. assert_equal( len( set(mined_block2_details['tx']).intersection( t.hash for t in txchains)), len(txchains))
def get_tests(self): # Shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # Create a new block block(0, coinbase_pubkey=self.coinbase_pubkey) self.chain.save_spendable_output() yield self.accepted() # Now we need that block to mature so we can spend the coinbase. # Also, move block height on beyond Genesis activation. test = TestInstance(sync_every_block=False) for i in range(600): block(5000 + i, coinbase_pubkey=self.coinbase_pubkey) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # Collect spendable outputs now to avoid cluttering the code later on. out = [] for i in range(200): out.append(self.chain.get_spendable_output()) self.stop_node(0) # # Test Case 1 (TC1). # # - 10 standard txs used # - 1 peer connected to node0 # All txs emplaced initially in the standard validation queue are processed and accepted by the mempool. # - None txn is rejected with a reason 'too-long-validation-time' (not moved into the non-std queue). # # The number of txs used in the test case. tc1_txs_num = 10 # Select funding transactions to use: # - tc1_txs_num funding transactions are needed in this test case. spend_txs = out[0:tc1_txs_num] args = [ '-checkmempool=0', '-persistmempool=0', '-maxstdtxvalidationduration=500', # increasing max validation time ensures that timeout doesn't occur for standard txns, even on slower machines and on debug build '-maxnonstdtxnsperthreadratio=0' ] # setting it to zero ensures that non-standard txs won't be processed (if there are any queued). with self.run_node_with_connections( 'TC1: {} txs detected as std and then accepted.'.format( tc1_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. std_txs = self.run_scenario1(conn, spend_txs, tc1_txs_num, self.locking_script_1) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, std_txs, timeout=30) assert_equal(conn.rpc.getmempoolinfo()['size'], tc1_txs_num) # # Test Case 2 (TC2). # # - 10 non-standard txs (with a simple locking script) used. # - 1 peer connected to node0. # The test case creates rejected txns with a reason 'too-long-validation-time' for all txs initially emplaced into the standard queue. # - those rejects are not taken into account to create reject messages (see explanation - point 6) # All txns are then forwarded to the non-standard validation queue where the validation timeout is longer (sufficient). # # The number of txs used in the test case. tc2_txs_num = 10 # Select funding transactions to use: # - one funding transaction is needed in this test case. spend_txs = out[tc1_txs_num:tc1_txs_num + 1] args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC2: {} txs with small bignums detected as non-std txs and then finally accepted.' .format(tc2_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, rejected_txs = self.run_scenario2( conn, spend_txs, tc2_txs_num, self.locking_script_2) wait_for_ptv_completion(conn, len(nonstd_txs)) # No transactions should be rejected assert_equal(len(rejected_txs), 0) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, nonstd_txs, timeout=30) assert_equal(conn.rpc.getmempoolinfo()['size'], tc2_txs_num) # # Test Case 3 (TC3). # # - 10 non-standard txs (with a complex locking script) used. # - 1 peer connected to node0 # The test case creates rejected txns with a reason 'too-long-validation-time' for all txs initially emplaced into the standard queue. # - those rejects are not taken into account to create reject messages (see explanation - point 6) # All txns are then forwarded to the non-standard validation queue where the validation timeout is longer (sufficient). # # The number of txs used in the test case. tc3_txs_num = 10 # Select funding transactions to use: # - one funding transaction is needed in this test case. spend_txs = out[tc1_txs_num + 1:tc1_txs_num + 2] args = [ '-checkmempool=0', '-persistmempool=0', '-maxnonstdtxvalidationduration=100000', # On slow/busy machine txn validation times have to be high '-maxtxnvalidatorasynctasksrunduration=100001', # This needs to mehigher then maxnonstdtxvalidationduration '-maxscriptsizepolicy=0', '-maxscriptnumlengthpolicy=250000' ] with self.run_node_with_connections( 'TC3: {} txs with large bignums detected as non-std txs and then finally accepted.' .format(tc3_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, rejected_txs = self.run_scenario2( conn, spend_txs, tc3_txs_num, self.locking_script_3) wait_for_ptv_completion(conn, len(nonstd_txs)) # No transactions should be rejected assert_equal(len(rejected_txs), 0) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, nonstd_txs, timeout=30) assert_equal(conn.rpc.getmempoolinfo()['size'], tc3_txs_num) # # Test Case 4 (TC4). # # - 10 non-standard txs (with a complex locking script) used. # - 1 peer connected to node0 # The test case creates rejected txns with a reason 'too-long-validation-time' for all txs initially emplaced into the standard queue. # - those rejects are not taken into account to create reject messages (see explanation - point 6) # All txns are then forwarded to the non-standard validation queue. # - due to insufficient timeout config all txs are rejected again with 'too-long-validation-time' reject reason. # - reject messages are created for each and every txn. # # The number of txs used in the test case. tc4_txs_num = 10 # Select funding transactions to use: # - one funding transaction is needed in this test case. spend_txs = out[tc1_txs_num + 2:tc1_txs_num + 3] args = [ '-checkmempool=0', '-persistmempool=0', '-maxscriptsizepolicy=0', '-maxscriptnumlengthpolicy=250000' ] with self.run_node_with_connections( 'TC4: {} txs with large bignums detected as non-std txs and then finally rejected.' .format(tc4_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, rejected_txs = self.run_scenario2( conn, spend_txs, tc4_txs_num, self.locking_script_3) wait_for_ptv_completion(conn, 0) # Check rejected transactions. self.check_rejected(rejected_txs, nonstd_txs) assert_equal(len(rejected_txs), tc4_txs_num) # The mempool should be empty at this stage. assert_equal(conn.rpc.getmempoolinfo()['size'], 0) # # Test Case 5 (TC5). # # - 100 standard txs used. # - 10 non-standard (with a simple locking script) txs used. # - 1 peer connected to node0. # This test case is a combination of TC1 & TC2 # - the set of std and non-std txs is shuffled before sending it to the node. # # The number of txs used in the test case. tc5_1_txs_num = 100 tc5_2_txs_num = 10 # Select funding transactions to use: # - tc5_1_txs_num+1 funding transactions are needed in this test case. spend_txs = out[tc1_txs_num + 3:tc1_txs_num + 3 + tc5_1_txs_num] spend_txs2 = out[tc1_txs_num + 3 + tc5_1_txs_num:tc1_txs_num + 4 + tc5_1_txs_num] args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC5: The total of {} std and nonstd txs processed and accepted.' .format(tc5_1_txs_num + tc5_2_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. std_txs = self.get_txchains_n(tc5_1_txs_num, 1, spend_txs, CScript(), self.locking_script_1, 2000000, 10) std_and_nonstd_txs, rejected_txs = self.run_scenario2( conn, spend_txs2, tc5_2_txs_num, self.locking_script_2, std_txs, shuffle_txs=True) wait_for_ptv_completion(conn, len(std_and_nonstd_txs)) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, std_and_nonstd_txs, timeout=30) assert_equal(conn.rpc.getmempoolinfo()['size'], tc5_1_txs_num + tc5_2_txs_num)
def get_tests(self): # Shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # Create a new block block(0, coinbase_pubkey=self.coinbase_pubkey) self.chain.save_spendable_output() yield self.accepted() # Now we need that block to mature so we can spend the coinbase. # Also, move block height on beyond Genesis activation. test = TestInstance(sync_every_block=False) for i in range(600): block(5000 + i, coinbase_pubkey=self.coinbase_pubkey) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # Collect spendable outputs now to avoid cluttering the code later on. out = [] for i in range(200): out.append(self.chain.get_spendable_output()) self.stop_node(0) # # Test Case 1 (TC1). # # - 5000 standard txs used (100 txn chains, each of length 50) # - 1 peer connected to node0 # # The number of txs used in the test case. tc1_txchains_num = 100 tc1_tx_chain_length = 50 # Select funding transactions to use: # - tc1_txchains_num funding transactions are needed in this test case. spend_txs = self.get_front_slice(out, tc1_txchains_num) args = [ '-checkmempool=0', '-persistmempool=0', '-limitancestorcount=50', '-txnvalidationasynchrunfreq=100', '-numstdtxvalidationthreads=6', '-numnonstdtxvalidationthreads=2' ] with self.run_node_with_connections( 'TC1: {} std txn chains used, each of length {}.'.format( tc1_txchains_num, tc1_tx_chain_length), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. std_txs = self.run_scenario1(conn, spend_txs, tc1_txchains_num, tc1_tx_chain_length, self.locking_script_1, 5000000000, 1) wait_for_ptv_completion(conn, tc1_txchains_num * tc1_tx_chain_length) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, std_txs, timeout=30) assert_equal(conn.rpc.getmempoolinfo()['size'], tc1_txchains_num * tc1_tx_chain_length) # # Test Case 2 (TC2). # # - 2400 non-standard txs (with a simple locking script) used # - 1 peer connected to node0 # # The number of txs used in the test case. tc2_txs_num = 2400 # Select funding transactions to use: # - one funding transaction is needed in this test case. spend_txs = self.get_front_slice(out, 1) args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC2: {} non-std txs used.'.format(tc2_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, rejected_txs = self.run_scenario2( conn, spend_txs, tc2_txs_num, self.locking_script_2) wait_for_ptv_completion(conn, tc2_txs_num) # Check if required transactions are accepted by the mempool. self.check_mempool(conn.rpc, nonstd_txs, timeout=30) assert_equal(len(rejected_txs), 0) assert_equal(conn.rpc.getmempoolinfo()['size'], tc2_txs_num) # # Test Case 3 (TC3). # # - 2400 valid non-standard txs (with a simple locking script) used # - 100 double spend txs used # - 1 peer connected to node0 # From the double spends set only 1 txn is accepted by the mempool. # # The number of txs used in the test case. tc3_txs_num = 2400 ds_txs_num = 100 # Select funding transactions to use: # - one funding transaction is needed in this test case. spend_txs = self.get_front_slice(out, 1) args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC3: {} non-std txs ({} double spends) used.'.format( tc3_txs_num, ds_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, ds_txs, _ = self.run_scenario2(conn, spend_txs, tc3_txs_num, self.locking_script_2, ds_txs_num) wait_for_ptv_completion(conn, len(nonstd_txs) + 1) # All txs from the nonstd_txs result set should be accepted self.check_mempool_with_subset(conn.rpc, nonstd_txs, timeout=30) # There is one more transaction in the mempool, which is a random txn from the ds_txs set assert_equal(conn.rpc.getmempoolinfo()['size'], len(nonstd_txs) + 1) # Only one txn is allowed to be in the mempool from the given ds set. assert_equal( len(self.check_intersec_with_mempool(conn.rpc, ds_txs)), 1) # # Test Case 4 (TC4). # # - 10 standard txs used (as additional input set) # - 2400 non-standard (with a simple locking script) txs used # - 100 double spend txs used # - 1 peer connected to node0 # All input txs are randomly suffled before sending. # # The number of txs used in the test case. tc4_1_txs_num = 10 tc4_2_txs_num = 2400 ds_txs_num = 100 # Select funding transactions to use: # - tc4_1_txs_num+1 funding transactions are needed in this test case. spend_txs = self.get_front_slice(out, tc4_1_txs_num) spend_txs2 = self.get_front_slice(out, 1) args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC4: {} std, {} nonstd txs ({} double spends) used (shuffled set).' .format(tc4_1_txs_num, tc4_2_txs_num, ds_txs_num), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. # Create some additional std txs to use. std_txs = self.get_txchains_n(tc4_1_txs_num, 1, spend_txs, CScript(), self.locking_script_1, 2000000, 10) # Create and send generated txs. std_and_nonstd_txs, ds_txs, _ = self.run_scenario2( conn, spend_txs2, tc4_2_txs_num, self.locking_script_2, ds_txs_num, std_txs, shuffle_txs=True) wait_for_ptv_completion(conn, len(std_and_nonstd_txs) + 1) # All txs from the std_and_nonstd_txs result set should be accepted self.check_mempool_with_subset(conn.rpc, std_and_nonstd_txs, timeout=30) # There is one more transaction in the mempool. It is a random txn from the ds_txs set assert_equal(conn.rpc.getmempoolinfo()['size'], len(std_and_nonstd_txs) + 1) # Only one txn is allowed to be accepted by the mempool, from the given double spends txn set. assert_equal( len(self.check_intersec_with_mempool(conn.rpc, ds_txs)), 1) # # Test Case 5 (TC5). # # - 24K=10x2400 non-standard txs (with a simple locking script) used # - 1K=10x100 double spend txs used # - 1 peer connected to node0 # From each double spend set only 1 txn is accepted by the mempool. # - Valid non-standard txs are sent first, then double spend txs (this approach maximises a ratio of 'txn-double-spend-detected' reject msgs) # # The number of txs used in a single subset. tc5_txs_num = 2400 ds_txs_num = 100 # The number of subsets used in the test case. tc5_num_of_subsets = 10 # Select funding transactions to use: # - tc5_num_of_subsets funding transaction are needed in this test case. spend_txs = self.get_front_slice(out, tc5_num_of_subsets) args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC5: {} non-std txs ({} double spends) used.'.format( tc5_txs_num * tc5_num_of_subsets, ds_txs_num * tc5_num_of_subsets), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, ds_txs, rejected_txs = self.run_scenario3( conn, spend_txs, tc5_txs_num, self.locking_script_2, ds_txs_num) wait_for_ptv_completion(conn, len(nonstd_txs) + tc5_num_of_subsets, check_interval=0.5) # All txs from the nonstd_txs result set should be accepted self.check_mempool_with_subset(conn.rpc, nonstd_txs, timeout=60) # There are tc5_num_of_subsets more transaction in the mempool (random txns from the ds_txs set) assert_equal(conn.rpc.getmempoolinfo()['size'], len(nonstd_txs) + tc5_num_of_subsets) # Only tc5_num_of_subsets txns are allowed to be in the mempool from the given ds set. assert_equal( len(self.check_intersec_with_mempool(conn.rpc, ds_txs)), tc5_num_of_subsets) # # Test Case 6 (TC6). # # - 24K=10x2400 non-standard txs (with a simple locking script) used # - 1K=10x100 double spend txs used # - 1 peer connected to node0 # From each double spends set only 1 txn is accepted by the mempool. # All input txs are randomly suffled before sending. # - the txs set is shuffeled first so it significantly decreases 'txn-double-spend-detected' reject msgs comparing to TC5 # - in this case 'txn-mempool-conflict' reject reason will mostly occur # # The number of txs used in a single subset. tc6_txs_num = 2400 ds_txs_num = 100 # The number of subsets used in the test case. tc6_num_of_subsets = 10 # Select funding transactions to use: # - tc6_num_of_subsets funding transaction are needed in this test case. spend_txs = self.get_front_slice(out, tc6_num_of_subsets) args = ['-checkmempool=0', '-persistmempool=0'] with self.run_node_with_connections( 'TC6: {} non-std txs ({} double spends) used (shuffled set).'. format(tc6_txs_num * tc6_num_of_subsets, ds_txs_num * tc6_num_of_subsets), 0, args + self.default_args, number_of_connections=1) as (conn, ): # Run test case. nonstd_txs, ds_txs, rejected_txs = self.run_scenario3( conn, spend_txs, tc6_txs_num, self.locking_script_2, ds_txs_num, shuffle_txs=True) wait_for_ptv_completion(conn, len(nonstd_txs) + tc6_num_of_subsets, check_interval=0.5) # All txs from the nonstd_txs result set should be accepted self.check_mempool_with_subset(conn.rpc, nonstd_txs, timeout=60) # There are tc6_num_of_subsets more transaction in the mempool (random txns from the ds_txs set) assert_equal(conn.rpc.getmempoolinfo()['size'], len(nonstd_txs) + tc6_num_of_subsets) # Only tc6_num_of_subsets txns are allowed to be in the mempool from the given ds set. assert_equal( len(self.check_intersec_with_mempool(conn.rpc, ds_txs)), tc6_num_of_subsets)