def test_big_bitmap(self, xt_node, test_node, num_utxos): assert (num_utxos % 8 == 0) bitmap_bytes = int(num_utxos / 8) num_spent = int(num_utxos / 2) self.log.info( "bip64: creating a getutxos request for %d outpoints (%d bitmap bytes)", num_utxos, bitmap_bytes) create_confirmed_utxos(xt_node.getnetworkinfo()["relayfee"], xt_node, num_spent) spent = [] unspent = [] unspent_values = [] # spend utxos for _ in range(0, num_spent): amount = round(random.random() + 0.01, 2) txid = xt_node.sendtoaddress(xt_node.getnewaddress(), amount) tx = FromHex(CTransaction(), xt_node.getrawtransaction(txid)) spent.append(tx.vin[0].prevout) # find equal amount of unspent utxos = create_confirmed_utxos(xt_node.getnetworkinfo()["relayfee"], xt_node, num_spent) assert (len(utxos) >= num_spent) for _ in range(0, num_spent): u = utxos.pop() unspent.append(COutPoint(int(u["txid"], 16), u["vout"])) unspent_values.append(u["amount"] * COIN) assert (len(spent) + len(unspent) == num_utxos) while (xt_node.getmempoolinfo()['size'] > 0): xt_node.generate(1) outpoints = [] for b in range(0, bitmap_bytes): o = b * 4 # offset: 4 utxos per byte from each of spent, unspent outpoints.extend([ unspent[o], unspent[o + 1], unspent[o + 2], unspent[o + 3], spent[o], spent[o + 1], spent[o + 2], spent[o + 3] ]) self.get_utxos(outpoints, False) assert_equal(len(test_node.utxos.bitmap), bitmap_bytes) assert_equal(len(test_node.utxos.result), len(unspent)) # check that returned results are in the same order as requested for i in range(0, len(unspent_values)): assert_equal(unspent_values[i], test_node.utxos.result[i].out.nValue) for b in range(0, bitmap_bytes): assert_equal(test_node.utxos.bitmap[b], int('00001111', 2))
def run_test(self): txouts = gen_return_txouts() relayfee = self.nodes[0].getnetworkinfo()['relayfee'] self.log.info('Check that mempoolminfee is minrelytxfee') assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_equal(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) txids = [] utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91) self.log.info('Create a mempool tx that will be evicted') us0 = utxos.pop() inputs = [{"txid": us0["txid"], "vout": us0["vout"]}] outputs = { self.nodes[0].getnewaddress(): 0.0001, "fee": us0["amount"] - Decimal('0.0001') } tx = self.nodes[0].createrawtransaction(inputs, outputs) self.nodes[0].settxfee( relayfee) # specifically fund this tx with low fee txF = self.nodes[0].fundrawtransaction(tx) self.nodes[0].settxfee(0) # return to automatic fee selection txFS = self.nodes[0].signrawtransactionwithwallet(txF['hex']) txid = self.nodes[0].sendrawtransaction(txFS['hex']) relayfee = self.nodes[0].getnetworkinfo()['relayfee'] base_fee = relayfee * 100 for i in range(3): txids.append([]) txids[i] = create_lots_of_big_transactions( self.nodes[0], txouts, utxos[30 * i:30 * i + 30], 30, (i + 1) * base_fee) self.log.info('The tx should be evicted by now') assert txid not in self.nodes[0].getrawmempool() txdata = self.nodes[0].gettransaction(txid) assert txdata['confirmations'] == 0 #confirmation should still be 0 self.log.info('Check that mempoolminfee is larger than minrelytxfee') assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) self.log.info('Create a mempool tx that will not pass mempoolminfee') us0 = utxos.pop() inputs = [{"txid": us0["txid"], "vout": us0["vout"]}] outputs = { self.nodes[0].getnewaddress(): 0.0001, "fee": us0["amount"] - Decimal('0.0001') } tx = self.nodes[0].createrawtransaction(inputs, outputs) # specifically fund this tx with a fee < mempoolminfee, >= than minrelaytxfee txF = self.nodes[0].fundrawtransaction(tx, {'feeRate': relayfee}) txFS = self.nodes[0].signrawtransactionwithwallet(txF['hex']) assert_raises_rpc_error(-26, "mempool min fee not met", self.nodes[0].sendrawtransaction, txFS['hex'])
def _send_transactions_to_node(self, node, num_trasactions): # Create UTXOs to build a bunch of transactions from self.relayfee = node.getnetworkinfo()['relayfee'] utxos = create_confirmed_utxos(self.relayfee, node, 100) self.sync_all() # Create a lot of transactions from the UTXOs newutxos = split_utxos(self.relayfee, node, num_trasactions, utxos) fill_mempool(self.relayfee, node, newutxos)
def test_broadcast(self): self.log.info( "Test that mempool reattempts delivery of locally submitted transaction" ) node = self.nodes[0] min_relay_fee = node.getnetworkinfo()["relayfee"] utxos = create_confirmed_utxos(min_relay_fee, node, 10) disconnect_nodes(node, 1) self.log.info("Generate transactions that only node 0 knows about") # generate a wallet txn addr = node.getnewaddress() wallet_tx_hsh = node.sendtoaddress(addr, 0.0001) # generate a txn using sendrawtransaction us0 = utxos.pop() inputs = [{"txid": us0["txid"], "vout": us0["vout"]}] outputs = {addr: 0.0001} tx = node.createrawtransaction(inputs, outputs) node.settxfee(min_relay_fee) txF = node.fundrawtransaction(tx) txFS = node.signrawtransactionwithwallet(txF["hex"]) rpc_tx_hsh = node.sendrawtransaction(txFS["hex"]) # check that second node doesn't have these two txns mempool = self.nodes[1].getrawmempool() assert rpc_tx_hsh not in mempool assert wallet_tx_hsh not in mempool # ensure that unbroadcast txs are persisted to mempool.dat self.restart_node(0) self.log.info("Reconnect nodes & check if they are sent to node 1") connect_nodes(node, 1) # fast forward into the future & ensure that the second node has the txns node.mockscheduler(15 * 60) # 15 min in seconds self.sync_mempools(timeout=30) mempool = self.nodes[1].getrawmempool() assert rpc_tx_hsh in mempool assert wallet_tx_hsh in mempool self.log.info( "Add another connection & ensure transactions aren't broadcast again" ) conn = node.add_p2p_connection(P2PTxInvStore()) node.mockscheduler(15 * 60) time.sleep(5) assert_equal(len(conn.get_invs()), 0)
def run_test(self): relayfee = self.nodes[0].getnetworkinfo()['relayfee'] utxos = create_confirmed_utxos(relayfee, self.nodes[0], 40) total_number_of_transactions = 30 # create a mempool transaction that will be evicted (smaller fee rate) # size: 5000211B, fee: 2000000 satoshi (0.02 BSV) --> fee rate: 0.399 sat/byte which is 0.00000399 BSV/kB small_fee = decimal.Decimal('0.02') small_data_size = 5000000 firstTxId = send_tx_with_data(self.nodes[0], utxos.pop(), small_fee, small_data_size) assert (firstTxId in self.nodes[0].getrawmempool()) self.log.info("First transaction %s successfully accepted to mempool.", firstTxId) # transactions with higher fee rate # size: 10000211B, fee: 10000000 satoshi (0.1 BSV) --> fee rate: 0.999 sat/byte which is 0.00000999 BSV/kB big_fee = decimal.Decimal('0.1') big_data_size = 10000000 for i in range(total_number_of_transactions - 1): send_tx_with_data(self.nodes[0], utxos.pop(), big_fee, big_data_size) assert_equal(len(self.nodes[0].getrawmempool()), total_number_of_transactions) self.log.info("%d big transactions successfully arrived to mempool.", total_number_of_transactions - 1) # At this point, mempool size is something more than 295 000 000 bytes. # If we send another transaction with size more than 5 MB and the highest fee rate, it should be replaced with the first one. # transaction with the highest fee rate, the same size as the first one # size: 5000211B, fee: 10000000 satoshi (0.1 BSV) --> fee rate: 1.999 sat/byte which is 0.00001999 BSV/kB lastTxId = send_tx_with_data(self.nodes[0], utxos.pop(), big_fee, small_data_size) # by now, the first transaction should be evicted, check confirmation state assert (firstTxId not in self.nodes[0].getrawmempool()) # last transaction should be in mempool because it has the highest fee assert (lastTxId in self.nodes[0].getrawmempool()) self.log.info( "First transaction %s evicted from mempool. Last sent transaction %s successfully accepted to mempool.", firstTxId, lastTxId) assert_equal(len(self.nodes[0].getrawmempool()), total_number_of_transactions) txdata = self.nodes[0].gettransaction(firstTxId) assert (txdata['confirmations'] == 0) # confirmation should still be 0
def run_test(self): txouts = gen_return_txouts() relayfee = self.nodes[0].getnetworkinfo()['relayfee'] txids = [] utxos = create_confirmed_utxos(relayfee, self.nodes[0], self.thirtyTransactions * 30) # create a mempool tx that will be evicted us0 = utxos.pop() inputs = [{"txid": us0["txid"], "vout": us0["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.1} tx = self.nodes[0].createrawtransaction(inputs, outputs) # Any fee calc method should work as longs as base_fee is set proportionally... # 1 tx_f = self.nodes[0].fundrawtransaction(tx) base_fee = satoshi_round( 0.01025 * 100 ) # DEFAULT_FALLBACK_FEE (settxfee(0) is default and falls through to this) # 2 # self.nodes[0].settxfee(relayfee) # specifically fund this tx with low fee (this is too low and will be bumped to MINFEE) # tx_f = self.nodes[0].fundrawtransaction(tx) # base_fee = satoshi_round(0.0005*100) # DEFAULT_TRANSACTION_MINFEE # self.nodes[0].settxfee(0) # return to automatic fee selection # 3 # tx_f = self.nodes[0].fundrawtransaction(tx, {"feeRate": relayfee}) # relayfee = self.nodes[0].getnetworkinfo()['relayfee'] # base_fee = relayfee*100 tx_fs = self.nodes[0].signrawtransaction(tx_f['hex']) txid = self.nodes[0].sendrawtransaction(tx_fs['hex']) for i in range(self.thirtyTransactions): txids.append([]) txids[i] = create_lots_of_big_transactions( self.nodes[0], txouts, utxos[30 * i:30 * i + 30], 30, (i + 1) * base_fee) # by now, the tx should be evicted, check confirmation state assert (txid not in self.nodes[0].getrawmempool()) txdata = self.nodes[0].gettransaction(txid) assert (txdata['confirmations'] == 0) # confirmation should still be 0
def run_test(self): txouts = gen_return_txouts() relayfee = self.nodes[0].getnetworkinfo()['relayfee'] self.log.info('Check that mempoolminfee is minrelytxfee') assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_equal(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) txids = [] utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91) self.log.info('Create a mempool tx that will be evicted') us0 = utxos.pop() inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}] outputs = {self.nodes[0].getnewaddress() : 0.0001} tx = self.nodes[0].createrawtransaction(inputs, outputs) self.nodes[0].settxfee(relayfee) # specifically fund this tx with low fee txF = self.nodes[0].fundrawtransaction(tx) self.nodes[0].settxfee(0) # return to automatic fee selection txFS = self.nodes[0].signrawtransactionwithwallet(txF['hex']) txid = self.nodes[0].sendrawtransaction(txFS['hex']) relayfee = self.nodes[0].getnetworkinfo()['relayfee'] base_fee = relayfee*100 for i in range (3): txids.append([]) txids[i] = create_lots_of_big_transactions(self.nodes[0], txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee) self.log.info('The tx should be evicted by now') assert(txid not in self.nodes[0].getrawmempool()) txdata = self.nodes[0].gettransaction(txid) assert(txdata['confirmations'] == 0) #confirmation should still be 0 self.log.info('Check that mempoolminfee is larger than minrelytxfee') assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) self.log.info('Create a mempool tx that will not pass mempoolminfee') us0 = utxos.pop() inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}] outputs = {self.nodes[0].getnewaddress() : 0.0001} tx = self.nodes[0].createrawtransaction(inputs, outputs) # specifically fund this tx with a fee < mempoolminfee, >= than minrelaytxfee txF = self.nodes[0].fundrawtransaction(tx, {'feeRate': relayfee}) txFS = self.nodes[0].signrawtransactionwithwallet(txF['hex']) assert_raises_rpc_error(-26, "mempool min fee not met", self.nodes[0].sendrawtransaction, txFS['hex'])
def test_broadcast(self): self.log.info( "Test that mempool reattempts delivery of locally submitted transaction" ) node = self.nodes[0] min_relay_fee = node.getnetworkinfo()["relayfee"] utxos = create_confirmed_utxos(min_relay_fee, node, 10) self.disconnect_nodes(0, 1) self.log.info("Generate transactions that only node 0 knows about") # generate a wallet txn addr = node.getnewaddress() wallet_tx_hsh = node.sendtoaddress(addr, 0.0001) # generate a txn using sendrawtransaction us0 = utxos.pop() inputs = [{"txid": us0["txid"], "vout": us0["vout"]}] outputs = {addr: 0.0001} tx = node.createrawtransaction(inputs, outputs) node.settxfee(min_relay_fee) txF = node.fundrawtransaction(tx) txFS = node.signrawtransactionwithwallet(txF["hex"]) rpc_tx_hsh = node.sendrawtransaction(txFS["hex"]) # check transactions are in unbroadcast using rpc mempoolinfo = self.nodes[0].getmempoolinfo() assert_equal(mempoolinfo['unbroadcastcount'], 2) mempool = self.nodes[0].getrawmempool(True) for tx in mempool: assert_equal(mempool[tx]['unbroadcast'], True) # check that second node doesn't have these two txns mempool = self.nodes[1].getrawmempool() assert rpc_tx_hsh not in mempool assert wallet_tx_hsh not in mempool # ensure that unbroadcast txs are persisted to mempool.dat self.restart_node(0) self.log.info("Reconnect nodes & check if they are sent to node 1") self.connect_nodes(0, 1) # fast forward into the future & ensure that the second node has the txns node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY) self.sync_mempools(timeout=30) mempool = self.nodes[1].getrawmempool() assert rpc_tx_hsh in mempool assert wallet_tx_hsh in mempool # check that transactions are no longer in first node's unbroadcast set mempool = self.nodes[0].getrawmempool(True) for tx in mempool: assert_equal(mempool[tx]['unbroadcast'], False) self.log.info( "Add another connection & ensure transactions aren't broadcast again" ) conn = node.add_p2p_connection(P2PTxInvStore()) node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY) time.sleep(2) # allow sufficient time for possibility of broadcast assert_equal(len(conn.get_invs()), 0) self.disconnect_nodes(0, 1) node.disconnect_p2ps()
def run_test(self): # Track test coverage statistics self.restart_counts = [0, 0, 0] # Track the restarts for nodes 0-2 self.crashed_on_restart = 0 # Track count of crashes during recovery # Start by creating a lot of utxos on node3 initial_height = self.nodes[3].getblockcount() utxo_list = create_confirmed_utxos( self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000) self.log.info("Prepped %d utxo entries", len(utxo_list)) # Sync these blocks with the other nodes block_hashes_to_sync = [] for height in range(initial_height + 1, self.nodes[3].getblockcount() + 1): block_hashes_to_sync.append(self.nodes[3].getblockhash(height)) self.log.debug("Syncing %d blocks with other nodes", len(block_hashes_to_sync)) # Syncing the blocks could cause nodes to crash, so the test begins here. self.sync_node3blocks(block_hashes_to_sync) starting_tip_height = self.nodes[3].getblockcount() # Main test loop: # each time through the loop, generate a bunch of transactions, # and then either mine a single new block on the tip, or some-sized reorg. for i in range(40): self.log.info("Iteration %d, generating 2500 transactions %s", i, self.restart_counts) # Generate a bunch of small-ish transactions self.generate_small_transactions(self.nodes[3], 2500, utxo_list) # Pick a random block between current tip, and starting tip current_height = self.nodes[3].getblockcount() random_height = random.randint(starting_tip_height, current_height) self.log.debug("At height %d, considering height %d", current_height, random_height) if random_height > starting_tip_height: # Randomly reorg from this point with some probability (1/4 for # tip, 1/5 for tip-1, ...) if random.random() < 1.0 / (current_height + 4 - random_height): self.log.debug("Invalidating block at height %d", random_height) self.nodes[3].invalidateblock( self.nodes[3].getblockhash(random_height)) # Now generate new blocks until we pass the old tip height self.log.debug("Mining longer tip") block_hashes = [] while current_height + 1 > self.nodes[3].getblockcount(): block_hashes.extend(self.nodes[3].generate( min(10, current_height + 1 - self.nodes[3].getblockcount()))) self.log.debug("Syncing %d new blocks...", len(block_hashes)) self.sync_node3blocks(block_hashes) utxo_list = self.nodes[3].listunspent() self.log.debug("Node3 utxo count: %d", len(utxo_list)) # Check that the utxo hashes agree with node3 # Useful side effect: each utxo cache gets flushed here, so that we # won't get crashes on shutdown at the end of the test. self.verify_utxo_hash() # Check the test coverage self.log.info("Restarted nodes: %s; crashes on restart: %d", self.restart_counts, self.crashed_on_restart) # If no nodes were restarted, we didn't test anything. assert self.restart_counts != [0, 0, 0] # Make sure we tested the case of crash-during-recovery. assert self.crashed_on_restart > 0 # Warn if any of the nodes escaped restart. for i in range(3): if self.restart_counts[i] == 0: self.log.warning("Node %d never crashed during utxo flush!", i)
def run_test(self): # Test `prioritisetransaction` required parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction) assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '') assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0) # Test `prioritisetransaction` invalid extra parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0) # Test `prioritisetransaction` invalid `txid` assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) # Test `prioritisetransaction` invalid `dummy` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0) assert_raises_rpc_error( -8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0) # Test `prioritisetransaction` invalid `fee_delta` assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo') self.txouts = gen_return_txouts() self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] utxo_count = 90 utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], utxo_count) base_fee = self.relayfee * 100 # our transactions are smaller than 100kb txids = [] # Create 3 batches of transactions at 3 different fee rate levels range_size = utxo_count // 3 for i in range(3): txids.append([]) start_range = i * range_size end_range = start_range + range_size txids[i] = create_lots_of_big_transactions( self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i + 1) * base_fee) # Make sure that the size of each group of transactions exceeds # MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create # more transactions. mempool = self.nodes[0].getrawmempool(True) sizes = [0, 0, 0] for i in range(3): for j in txids[i]: assert (j in mempool) sizes[i] += mempool[j]['size'] assert (sizes[i] > MAX_BLOCK_BASE_SIZE) # Fail => raise utxo_count # add a fee delta to something in the cheapest bucket and make sure it gets mined # also check that a different entry in the cheapest bucket is NOT mined self.nodes[0].prioritisetransaction(txid=txids[0][0], fee_delta=int(3 * base_fee * COIN)) self.nodes[0].generate(1) mempool = self.nodes[0].getrawmempool() self.log.info("Assert that prioritised transaction was mined") assert (txids[0][0] not in mempool) assert (txids[0][1] in mempool) high_fee_tx = None for x in txids[2]: if x not in mempool: high_fee_tx = x # Something high-fee should have been mined! assert (high_fee_tx != None) # Add a prioritisation before a tx is in the mempool (de-prioritising a # high-fee transaction so that it's now low fee). self.nodes[0].prioritisetransaction( txid=high_fee_tx, fee_delta=-int(2 * base_fee * COIN)) # Add everything back to mempool self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) # Check to make sure our high fee rate tx is back in the mempool mempool = self.nodes[0].getrawmempool() assert (high_fee_tx in mempool) # Now verify the modified-high feerate transaction isn't mined before # the other high fee transactions. Keep mining until our mempool has # decreased by all the high fee size that we calculated above. while (self.nodes[0].getmempoolinfo()['bytes'] > sizes[0] + sizes[1]): self.nodes[0].generate(1) # High fee transaction should not have been mined, but other high fee rate # transactions should have been. mempool = self.nodes[0].getrawmempool() self.log.info( "Assert that de-prioritised transaction is still in mempool") assert (high_fee_tx in mempool) for x in txids[2]: if (x != high_fee_tx): assert (x not in mempool) # Create a free transaction. Should be rejected. utxo_list = self.nodes[0].listunspent() assert (len(utxo_list) > 0) utxo = utxo_list[0] inputs = [] outputs = {} inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) outputs[self.nodes[0].getnewaddress()] = utxo["amount"] raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) tx_hex = self.nodes[0].signrawtransactionwithwallet(raw_tx)["hex"] tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) assert (tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee # to be the minimum for a 1000-byte transaction and check that it is # accepted. self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=int(self.relayfee * COIN)) self.log.info( "Assert that prioritised free transaction is accepted to mempool") assert_equal(self.nodes[0].sendrawtransaction(tx_hex), tx_id) assert (tx_id in self.nodes[0].getrawmempool()) # Test that calling prioritisetransaction is sufficient to trigger # getblocktemplate to (eventually) return a new block. mock_time = int(time.time()) self.nodes[0].setmocktime(mock_time) template = self.nodes[0].getblocktemplate() self.nodes[0].prioritisetransaction( txid=tx_id, fee_delta=-int(self.relayfee * COIN)) self.nodes[0].setmocktime(mock_time + 10) new_template = self.nodes[0].getblocktemplate() assert (template != new_template)
def run_test(self): # Test `prioritisetransaction` required parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction) assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '') assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0) # Test `prioritisetransaction` invalid extra parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0) # Test `prioritisetransaction` invalid `txid` assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) # Test `prioritisetransaction` invalid `dummy` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0) assert_raises_rpc_error(-8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0) # Test `prioritisetransaction` invalid `fee_delta` assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo') self.txouts = gen_return_txouts() self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] utxo_count = 90 utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], utxo_count) base_fee = self.relayfee*100 # our transactions are smaller than 100kb txids = [] # Create 3 batches of transactions at 3 different fee rate levels range_size = utxo_count // 3 for i in range(3): txids.append([]) start_range = i * range_size end_range = start_range + range_size txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i+1)*base_fee) # Make sure that the size of each group of transactions exceeds # MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create # more transactions. mempool = self.nodes[0].getrawmempool(True) sizes = [0, 0, 0] for i in range(3): for j in txids[i]: assert(j in mempool) sizes[i] += mempool[j]['size'] assert(sizes[i] > MAX_BLOCK_BASE_SIZE) # Fail => raise utxo_count # add a fee delta to something in the cheapest bucket and make sure it gets mined # also check that a different entry in the cheapest bucket is NOT mined self.nodes[0].prioritisetransaction(txid=txids[0][0], fee_delta=int(3*base_fee*COIN)) self.nodes[0].generate(1) mempool = self.nodes[0].getrawmempool() self.log.info("Assert that prioritised transaction was mined") assert(txids[0][0] not in mempool) assert(txids[0][1] in mempool) high_fee_tx = None for x in txids[2]: if x not in mempool: high_fee_tx = x # Something high-fee should have been mined! assert(high_fee_tx != None) # Add a prioritisation before a tx is in the mempool (de-prioritising a # high-fee transaction so that it's now low fee). self.nodes[0].prioritisetransaction(txid=high_fee_tx, fee_delta=-int(2*base_fee*COIN)) # Add everything back to mempool self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) # Check to make sure our high fee rate tx is back in the mempool mempool = self.nodes[0].getrawmempool() assert(high_fee_tx in mempool) # Now verify the modified-high feerate transaction isn't mined before # the other high fee transactions. Keep mining until our mempool has # decreased by all the high fee size that we calculated above. while (self.nodes[0].getmempoolinfo()['bytes'] > sizes[0] + sizes[1]): self.nodes[0].generate(1) # High fee transaction should not have been mined, but other high fee rate # transactions should have been. mempool = self.nodes[0].getrawmempool() self.log.info("Assert that de-prioritised transaction is still in mempool") assert(high_fee_tx in mempool) for x in txids[2]: if (x != high_fee_tx): assert(x not in mempool) # Create a free transaction. Should be rejected. utxo_list = self.nodes[0].listunspent() assert(len(utxo_list) > 0) utxo = utxo_list[0] inputs = [] outputs = {} inputs.append({"txid" : utxo["txid"], "vout" : utxo["vout"]}) outputs[self.nodes[0].getnewaddress()] = utxo["amount"] raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) tx_hex = self.nodes[0].signrawtransactionwithwallet(raw_tx)["hex"] tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) assert(tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee # to be the minimum for a 1000-byte transaction and check that it is # accepted. self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=int(self.relayfee*COIN)) self.log.info("Assert that prioritised free transaction is accepted to mempool") assert_equal(self.nodes[0].sendrawtransaction(tx_hex), tx_id) assert(tx_id in self.nodes[0].getrawmempool()) # Test that calling prioritisetransaction is sufficient to trigger # getblocktemplate to (eventually) return a new block. mock_time = int(time.time()) self.nodes[0].setmocktime(mock_time) template = self.nodes[0].getblocktemplate() self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=-int(self.relayfee*COIN)) self.nodes[0].setmocktime(mock_time+10) new_template = self.nodes[0].getblocktemplate() assert(template != new_template)
def run_test(self): # Track test coverage statistics self.restart_counts = [0, 0, 0] # Track the restarts for nodes 0-2 self.crashed_on_restart = 0 # Track count of crashes during recovery # Start by creating a lot of utxos on node3 initial_height = self.nodes[3].getblockcount() utxo_list = create_confirmed_utxos(self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000) self.log.info("Prepped %d utxo entries", len(utxo_list)) # Sync these blocks with the other nodes block_hashes_to_sync = [] for height in range(initial_height + 1, self.nodes[3].getblockcount() + 1): block_hashes_to_sync.append(self.nodes[3].getblockhash(height)) self.log.debug("Syncing %d blocks with other nodes", len(block_hashes_to_sync)) # Syncing the blocks could cause nodes to crash, so the test begins here. self.sync_node3blocks(block_hashes_to_sync) starting_tip_height = self.nodes[3].getblockcount() # Main test loop: # each time through the loop, generate a bunch of transactions, # and then either mine a single new block on the tip, or some-sized reorg. for i in range(40): self.log.info("Iteration %d, generating 2500 transactions %s", i, self.restart_counts) # Generate a bunch of small-ish transactions self.generate_small_transactions(self.nodes[3], 2500, utxo_list) # Pick a random block between current tip, and starting tip current_height = self.nodes[3].getblockcount() random_height = random.randint(starting_tip_height, current_height) self.log.debug("At height %d, considering height %d", current_height, random_height) if random_height > starting_tip_height: # Randomly reorg from this point with some probability (1/4 for # tip, 1/5 for tip-1, ...) if random.random() < 1.0 / (current_height + 4 - random_height): self.log.debug("Invalidating block at height %d", random_height) self.nodes[3].invalidateblock(self.nodes[3].getblockhash(random_height)) # Now generate new blocks until we pass the old tip height self.log.debug("Mining longer tip") block_hashes = [] while current_height + 1 > self.nodes[3].getblockcount(): block_hashes.extend(self.nodes[3].generate(min(10, current_height + 1 - self.nodes[3].getblockcount()))) self.log.debug("Syncing %d new blocks...", len(block_hashes)) self.sync_node3blocks(block_hashes) utxo_list = self.nodes[3].listunspent() self.log.debug("Node3 utxo count: %d", len(utxo_list)) # Check that the utxo hashes agree with node3 # Useful side effect: each utxo cache gets flushed here, so that we # won't get crashes on shutdown at the end of the test. self.verify_utxo_hash() # Check the test coverage self.log.info("Restarted nodes: %s; crashes on restart: %d", self.restart_counts, self.crashed_on_restart) # If no nodes were restarted, we didn't test anything. assert self.restart_counts != [0, 0, 0] # Make sure we tested the case of crash-during-recovery. assert self.crashed_on_restart > 0 # Warn if any of the nodes escaped restart. for i in range(3): if self.restart_counts[i] == 0: self.log.warn("Node %d never crashed during utxo flush!", i)
def run_test(self): transaction_overhead = 2048 mempool_size = self.mempool_size total_number_of_transactions = self.total_number_of_transactions number_of_good_transactions = total_number_of_transactions * 90 // 100 number_of_cheap_transactions = total_number_of_transactions - number_of_good_transactions last_transaction_factor = total_number_of_transactions * 15 // 100 transaction_size = mempool_size * ONE_MEGABYTE // total_number_of_transactions - transaction_overhead relayfee = self.nodes[0].getnetworkinfo()['relayfee'] utxos = create_confirmed_utxos(relayfee, self.nodes[0], total_number_of_transactions + 1) # Transactions with higher fee rate # size: 6MiB, fee: 10,000,000 satoshi (0.1 BSV) --> fee rate: 1.6 sat/byte good_fee = decimal.Decimal('0.1') good_txids = [] for i in range(number_of_good_transactions): txid = send_tx_with_data(self.nodes[0], utxos.pop(), good_fee, transaction_size) self.log.debug("Inserted good transaction %d %s", i + 1, txid) good_txids.append(txid) assert_equal(len(self.nodes[0].getrawmempool()), number_of_good_transactions) self.log.info("%d transactions successfully arrived to mempool.", number_of_good_transactions) # Transactions with lower fee rate # size: 6MiB, fee: 2,500,000 satoshi (0.025 BSV) --> fee rate: 0.4 sat/byte cheap_fee = good_fee / 4 cheap_txids = [] for i in range(number_of_cheap_transactions): txid = send_tx_with_data(self.nodes[0], utxos.pop(), cheap_fee, transaction_size) self.log.debug("Inserted cheap transaction %d %s", i + 1, txid) cheap_txids.append(txid) assert_equal(len(self.nodes[0].getrawmempool()), total_number_of_transactions) self.log.info("%d transactions successfully arrived to mempool.", total_number_of_transactions) # The mempool should now be full. Insert the last, large transaction # size: 42MiB, fee: 35,000,000 satoshi (0.35 BSV) --> fee rate: 0.8 sat/byte self.log.info("Inserting last transaction") last_fee = last_transaction_factor * good_fee / 2 last_size = last_transaction_factor * transaction_size assert_raises_rpc_error(-26, 'mempool full', send_tx_with_data, self.nodes[0], utxos.pop(), last_fee, last_size) # Now let's see what happens. There should be no cheap transactions in the pool any more. mempool = self.nodes[0].getrawmempool() assert_equal(len(mempool), number_of_good_transactions) self.log.info("%d transactions were evicted.", total_number_of_transactions - len(mempool)) for txid in cheap_txids: assert (txid not in mempool) self.log.info("All transactions with insufficient fee were evicted.")
def run_test(self): # Test `prioritisetransaction` required parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction) assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '') assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0) # Test `prioritisetransaction` invalid extra parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0) # Test `prioritisetransaction` invalid `txid` assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) # Test `prioritisetransaction` invalid `dummy` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0) assert_raises_rpc_error(-8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0) # Test `prioritisetransaction` invalid `fee_delta` assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo') self.txouts = gen_return_txouts() self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] utxo_count = 90 utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], utxo_count) base_fee = self.relayfee*100 # our transactions are smaller than 100kb txids = [] # Create 3 batches of transactions at 3 different fee rate levels range_size = utxo_count // 3 for i in range(3): txids.append([]) start_range = i * range_size end_range = start_range + range_size txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i+1)*base_fee) # Make sure that the size of each group of transactions exceeds # MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create # more transactions. mempool = self.nodes[0].getrawmempool(True) sizes = [0, 0, 0] for i in range(3): for j in txids[i]: assert j in mempool sizes[i] += mempool[j]['vsize'] assert sizes[i] > MAX_BLOCK_BASE_SIZE # Fail => raise utxo_count # add a fee delta to something in the cheapest bucket and make sure it gets mined # also check that a different entry in the cheapest bucket is NOT mined self.nodes[0].prioritisetransaction(txid=txids[0][0], fee_delta=int(3*base_fee*COIN)) self.nodes[0].generate(1) mempool = self.nodes[0].getrawmempool() self.log.info("Assert that prioritised transaction was mined") assert txids[0][0] not in mempool assert txids[0][1] in mempool high_fee_tx = None for x in txids[2]: if x not in mempool: high_fee_tx = x