def run_test(self): wallet = MiniWallet(self.nodes[0]) wallet.generate(200) chain_height = self.nodes[0].getblockcount() assert_equal(chain_height, 200) # Coinbase at height chain_height-100+1 ok in mempool, should # get mined. Coinbase at height chain_height-100+2 is # too immature to spend. b = [self.nodes[0].getblockhash(n) for n in range(101, 103)] coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] utxo_101 = wallet.get_utxo(txid=coinbase_txids[0]) utxo_102 = wallet.get_utxo(txid=coinbase_txids[1]) spend_101_id = wallet.send_self_transfer( from_node=self.nodes[0], utxo_to_spend=utxo_101)["txid"] # coinbase at height 102 should be too immature to spend assert_raises_rpc_error( -26, "bad-txns-premature-spend-of-coinbase", lambda: wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_102)) # mempool should have just spend_101: assert_equal(self.nodes[0].getrawmempool(), [spend_101_id]) # mine a block, spend_101 should get confirmed self.nodes[0].generate(1) assert_equal(set(self.nodes[0].getrawmempool()), set()) # ... and now height 102 can be spent: spend_102_id = wallet.send_self_transfer( from_node=self.nodes[0], utxo_to_spend=utxo_102)["txid"] assert_equal(self.nodes[0].getrawmempool(), [spend_102_id])
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.wallet.rescan_utxos() filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter()) self.log.info('Test filter size limits') self.test_size_limits(filter_peer) self.log.info('Test BIP 37 for a node with fRelay = True (default)') self.test_filter(filter_peer) self.nodes[0].disconnect_p2ps() self.log.info('Test BIP 37 for a node with fRelay = False') # Add peer but do not send version yet filter_peer_without_nrelay = self.nodes[0].add_p2p_connection( P2PBloomFilter(), send_version=False, wait_for_verack=False) # Send version with relay=False version_without_fRelay = msg_version() version_without_fRelay.nVersion = P2P_VERSION version_without_fRelay.strSubVer = P2P_SUBVERSION version_without_fRelay.nServices = P2P_SERVICES version_without_fRelay.relay = 0 filter_peer_without_nrelay.send_message(version_without_fRelay) filter_peer_without_nrelay.wait_for_verack() assert not self.nodes[0].getpeerinfo()[0]['relaytxes'] self.test_frelay_false(filter_peer_without_nrelay) self.test_filter(filter_peer_without_nrelay) self.test_msg_mempool()
def run_test(self): wallet = MiniWallet(self.nodes[0]) # Invalidate two blocks, so that miniwallet has access to a coin that will mature in the next block chain_height = 198 self.nodes[0].invalidateblock(self.nodes[0].getblockhash(chain_height + 1)) assert_equal(chain_height, self.nodes[0].getblockcount()) # Coinbase at height chain_height-100+1 ok in mempool, should # get mined. Coinbase at height chain_height-100+2 is # too immature to spend. wallet.scan_blocks(start=chain_height - 100 + 1, num=1) utxo_mature = wallet.get_utxo() wallet.scan_blocks(start=chain_height - 100 + 2, num=1) utxo_immature = wallet.get_utxo() spend_mature_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_mature)["txid"] # other coinbase should be too immature to spend immature_tx = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_immature, mempool_valid=False) assert_raises_rpc_error(-26, "bad-txns-premature-spend-of-coinbase", lambda: self.nodes[0].sendrawtransaction(immature_tx['hex'])) # mempool should have just the mature one assert_equal(self.nodes[0].getrawmempool(), [spend_mature_id]) # mine a block, mature one should get confirmed self.nodes[0].generate(1) assert_equal(set(self.nodes[0].getrawmempool()), set()) # ... and now previously immature can be spent: spend_new_id = self.nodes[0].sendrawtransaction(immature_tx['hex']) assert_equal(self.nodes[0].getrawmempool(), [spend_new_id])
def run_test(self): self.miniwallet = MiniWallet(self.nodes[0]) # Add enough mature utxos to the wallet, so that all txs spend confirmed coins self.miniwallet.rescan_utxos() self.blocksonly_mode_tests() self.blocks_relay_conn_tests()
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.generate(self.wallet, 101) self.test_uncache() self.test_add_spent() self.test_flush()
def run_test(self): gen_node = self.nodes[0] # The block and tx generating node miniwallet = MiniWallet(gen_node) # Add enough mature utxos to the wallet, so that all txs spend confirmed coins miniwallet.generate(1) gen_node.generate(100) inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer MAX_REPEATS = 100 self.log.info("Running test up to {} times.".format(MAX_REPEATS)) for i in range(MAX_REPEATS): self.log.info('Run repeat {}'.format(i + 1)) txid = miniwallet.send_self_transfer(from_node=gen_node)['wtxid'] want_tx = msg_getdata() want_tx.inv.append(CInv(t=MSG_TX, h=int(txid, 16))) with p2p_lock: inbound_peer.last_message.pop('notfound', None) inbound_peer.send_and_ping(want_tx) if inbound_peer.last_message.get('notfound'): self.log.debug('tx {} was not yet announced to us.'.format(txid)) self.log.debug("node has responded with a notfound message. End test.") assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16)) with p2p_lock: inbound_peer.last_message.pop('notfound') break else: self.log.debug('tx {} was already announced to us. Try test again.'.format(txid)) assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
def test_muhash_implementation(self): self.log.info("Test MuHash implementation consistency") node = self.nodes[0] wallet = MiniWallet(node) mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 node.setmocktime(mocktime) # Generate 100 blocks and remove the first since we plan to spend its # coinbase block_hashes = self.generate(wallet, 1) + self.generate(node, 99) blocks = list( map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks.pop(0) # Create a spending transaction and mine a block which includes it txid = wallet.send_self_transfer(from_node=node)['txid'] tx_block = self.generateblock(node, output=wallet.get_address(), transactions=[txid]) blocks.append( from_hex(CBlock(), node.getblock(tx_block['hash'], False))) # Serialize the outputs that should be in the UTXO set and add them to # a MuHash object muhash = MuHash3072() for height, block in enumerate(blocks): # The Genesis block coinbase is not part of the UTXO set and we # spent the first mined block height += 2 for tx in block.vtx: for n, tx_out in enumerate(tx.vout): coinbase = 1 if not tx.vin[0].prevout.hash else 0 # Skip witness commitment if (coinbase and n > 0): continue data = COutPoint(int(tx.rehash(), 16), n).serialize() data += struct.pack("<i", height * 2 + coinbase) data += tx_out.serialize() muhash.insert(data) finalized = muhash.digest() node_muhash = node.gettxoutsetinfo("muhash")['muhash'] assert_equal(finalized[::-1].hex(), node_muhash) self.log.info("Test deterministic UTXO set hash results") assert_equal( node.gettxoutsetinfo()['hash_serialized_2'], "221f245cf4c9010eeb7f5183d342c002ae6c1c27e98aa357dccb788c21d98049") assert_equal( node.gettxoutsetinfo("muhash")['muhash'], "7c0890c68501f7630d36aeb3999dc924e63af084ae1bbfba11dd462144637635")
def test_muhash_implementation(self): self.log.info("Test MuHash implementation consistency") node = self.nodes[0] wallet = MiniWallet(node) mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 node.setmocktime(mocktime) # Generate 100 blocks and remove the first since we plan to spend its # coinbase block_hashes = self.generate(wallet, 1) + self.generate(node, 99) blocks = list( map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks.pop(0) # Create a spending transaction and mine a block which includes it txid = wallet.send_self_transfer(from_node=node)['txid'] tx_block = self.generateblock(node, output=wallet.get_address(), transactions=[txid]) blocks.append( from_hex(CBlock(), node.getblock(tx_block['hash'], False))) # Serialize the outputs that should be in the UTXO set and add them to # a MuHash object muhash = MuHash3072() for height, block in enumerate(blocks): # The Genesis block coinbase is not part of the UTXO set and we # spent the first mined block height += 2 for tx in block.vtx: for n, tx_out in enumerate(tx.vout): coinbase = 1 if not tx.vin[0].prevout.hash else 0 # Skip witness commitment if (coinbase and n > 0): continue data = COutPoint(int(tx.rehash(), 16), n).serialize() data += struct.pack("<i", height * 2 + coinbase) data += tx_out.serialize() muhash.insert(data) finalized = muhash.digest() node_muhash = node.gettxoutsetinfo("muhash")['muhash'] assert_equal(finalized[::-1].hex(), node_muhash) self.log.info("Test deterministic UTXO set hash results") assert_equal( node.gettxoutsetinfo()['hash_serialized_2'], "1ec3e58b0be79fca5917c7c30b9761f12adb609d0233cf2b42fa17ec419f0056") assert_equal( node.gettxoutsetinfo("muhash")['muhash'], "faee25ca4765facb643b7a2d96531c568cb52ad84de5ae3d420a92967621ec17")
def test_muhash_implementation(self): self.log.info("Test MuHash implementation consistency") node = self.nodes[0] wallet = MiniWallet(node) mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 node.setmocktime(mocktime) # Generate 100 blocks and remove the first since we plan to spend its # coinbase block_hashes = self.generate(wallet, 1) + self.generate(node, 99) blocks = list( map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks.pop(0) # Create a spending transaction and mine a block which includes it txid = wallet.send_self_transfer(from_node=node)['txid'] tx_block = self.generateblock(node, output=wallet.get_address(), transactions=[txid]) blocks.append( from_hex(CBlock(), node.getblock(tx_block['hash'], False))) # Serialize the outputs that should be in the UTXO set and add them to # a MuHash object muhash = MuHash3072() for height, block in enumerate(blocks): # The Genesis block coinbase is not part of the UTXO set and we # spent the first mined block height += 2 for tx in block.vtx: for n, tx_out in enumerate(tx.vout): coinbase = 1 if not tx.vin[0].prevout.hash else 0 # Skip witness commitment if (coinbase and n > 0): continue data = COutPoint(int(tx.rehash(), 16), n).serialize() data += struct.pack("<i", height * 2 + coinbase) data += tx_out.serialize() muhash.insert(data) finalized = muhash.digest() node_muhash = node.gettxoutsetinfo("muhash")['muhash'] assert_equal(finalized[::-1].hex(), node_muhash) self.log.info("Test deterministic UTXO set hash results") assert_equal( node.gettxoutsetinfo()['hash_serialized_2'], "3a570529b4c32e77268de1f81b903c75cc2da53c48df0d125c1e697ba7c8c7b7") assert_equal( node.gettxoutsetinfo("muhash")['muhash'], "a13e0e70eb8acc786549596e3bc154623f1a5a622ba2f70715f6773ec745f435")
def test_muhash_implementation(self): self.log.info("Test MuHash implementation consistency") node = self.nodes[0] wallet = MiniWallet(node) mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 node.setmocktime(mocktime) # Generate 100 blocks and remove the first since we plan to spend its # coinbase block_hashes = self.generate(wallet, 1) + self.generate(node, 99) blocks = list( map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks.pop(0) # Create a spending transaction and mine a block which includes it txid = wallet.send_self_transfer(from_node=node)['txid'] tx_block = self.generateblock(node, output=wallet.get_address(), transactions=[txid]) blocks.append( from_hex(CBlock(), node.getblock(tx_block['hash'], False))) # Serialize the outputs that should be in the UTXO set and add them to # a MuHash object muhash = MuHash3072() for height, block in enumerate(blocks): # The Genesis block coinbase is not part of the UTXO set and we # spent the first mined block height += 2 for tx in block.vtx: for n, tx_out in enumerate(tx.vout): coinbase = 1 if not tx.vin[0].prevout.hash else 0 # Skip witness commitment if (coinbase and n > 0): continue data = COutPoint(int(tx.rehash(), 16), n).serialize() data += struct.pack("<i", height * 2 + coinbase) data += tx_out.serialize() muhash.insert(data) finalized = muhash.digest() node_muhash = node.gettxoutsetinfo("muhash")['muhash'] assert_equal(finalized[::-1].hex(), node_muhash) self.log.info("Test deterministic UTXO set hash results") assert_equal( node.gettxoutsetinfo()['hash_serialized_2'], "5b1b44097406226c0eb8e1362cd17a1f346522cf9390a8175a57a5262cb1963f") assert_equal( node.gettxoutsetinfo("muhash")['muhash'], "4b8803075d7151d06fad3e88b68ba726886794873fbfa841d12aefb2cc2b881b")
def run_test(self): self.miniwallet = MiniWallet(self.nodes[0]) # Add enough mature utxos to the wallet, so that all txs spend confirmed coins self.miniwallet.generate(2) self.nodes[0].generate(COINBASE_MATURITY) self.blocksonly_mode_tests() self.blocks_relay_conn_tests()
def test_muhash_implementation(self): self.log.info("Test MuHash implementation consistency") node = self.nodes[0] wallet = MiniWallet(node) mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 node.setmocktime(mocktime) # Generate 100 blocks and remove the first since we plan to spend its # coinbase block_hashes = wallet.generate(1) + node.generate(99) blocks = list( map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks.pop(0) # Create a spending transaction and mine a block which includes it txid = wallet.send_self_transfer(from_node=node)['txid'] tx_block = node.generateblock(output=wallet.get_address(), transactions=[txid]) blocks.append( from_hex(CBlock(), node.getblock(tx_block['hash'], False))) # Serialize the outputs that should be in the UTXO set and add them to # a MuHash object muhash = MuHash3072() for height, block in enumerate(blocks): # The Genesis block coinbase is not part of the UTXO set and we # spent the first mined block height += 2 for tx in block.vtx: for n, tx_out in enumerate(tx.vout): coinbase = 1 if not tx.vin[0].prevout.hash else 0 # Skip witness commitment if (coinbase and n > 0): continue data = COutPoint(int(tx.rehash(), 16), n).serialize() data += struct.pack("<i", height * 2 + coinbase) data += tx_out.serialize() muhash.insert(data) finalized = muhash.digest() node_muhash = node.gettxoutsetinfo("muhash")['muhash'] assert_equal(finalized[::-1].hex(), node_muhash) self.log.info("Test deterministic UTXO set hash results") assert_equal( node.gettxoutsetinfo()['hash_serialized_2'], "03f3bedef7a3e64686e13b57ec08b1ada40528d8e01f64e077750e225ddb8c07") assert_equal( node.gettxoutsetinfo("muhash")['muhash'], "69ebd7142d443a89c227637ef9a21c05287a98f0acdd40ba7e3ef79d1f4e412d")
def run_test(self): txouts = gen_return_txouts() node = self.nodes[0] miniwallet = MiniWallet(node) relayfee = node.getnetworkinfo()['relayfee'] self.log.info('Check that mempoolminfee is minrelaytxfee') assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) tx_batch_size = 25 num_of_batches = 3 # Generate UTXOs to flood the mempool # 1 to create a tx initially that will be evicted from the mempool later # 3 batches of multiple transactions with a fee rate much higher than the previous UTXO # And 1 more to verify that this tx does not get added to the mempool with a fee rate less than the mempoolminfee self.generate(miniwallet, 1 + (num_of_batches * tx_batch_size) + 1) # Mine 99 blocks so that the UTXOs are allowed to be spent self.generate(node, COINBASE_MATURITY - 1) self.log.info('Create a mempool tx that will be evicted') tx_to_be_evicted_id = miniwallet.send_self_transfer( from_node=node, fee_rate=relayfee)["txid"] # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB) # by 130 should result in a fee that corresponds to 2x of that fee rate base_fee = relayfee * 130 self.log.info("Fill up the mempool with txs with higher fee rate") for batch_of_txid in range(num_of_batches): fee = (batch_of_txid + 1) * base_fee self.send_large_txs(node, miniwallet, txouts, fee, tx_batch_size) self.log.info('The tx should be evicted by now') # The number of transactions created should be greater than the ones present in the mempool assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool())) # Initial tx created should not be present in the mempool anymore as it had a lower fee rate assert tx_to_be_evicted_id not in node.getrawmempool() self.log.info('Check that mempoolminfee is larger than minrelaytxfee') assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) # Deliberately try to create a tx with a fee less than the minimum mempool fee to assert that it does not get added to the mempool self.log.info('Create a mempool tx that will not pass mempoolminfee') assert_raises_rpc_error(-26, "mempool min fee not met", miniwallet.send_self_transfer, from_node=node, fee_rate=relayfee, mempool_valid=False)
def test_replacement_relay_fee(self): wallet = MiniWallet(self.nodes[0]) wallet.scan_blocks(start=77, num=1) tx = wallet.send_self_transfer(from_node=self.nodes[0])['tx'] # Higher fee, higher feerate, different txid, but the replacement does not provide a relay # fee conforming to node's `incrementalrelayfee` policy of 1000 sat per KB. tx.vout[0].nValue -= 1 assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
def test_no_inherited_signaling(self): wallet = MiniWallet(self.nodes[0]) wallet.scan_blocks(start=76, num=1) confirmed_utxo = wallet.get_utxo() # Create an explicitly opt-in parent transaction optin_parent_tx = wallet.send_self_transfer( from_node=self.nodes[0], utxo_to_spend=confirmed_utxo, sequence=BIP125_SEQUENCE_NUMBER, fee_rate=Decimal('0.01'), ) assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable']) replacement_parent_tx = wallet.create_self_transfer( from_node=self.nodes[0], utxo_to_spend=confirmed_utxo, sequence=BIP125_SEQUENCE_NUMBER, fee_rate=Decimal('0.02'), ) # Test if parent tx can be replaced. res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']])[0] # Parent can be replaced. assert_equal(res['allowed'], True) # Create an opt-out child tx spending the opt-in parent parent_utxo = wallet.get_utxo(txid=optin_parent_tx['txid']) optout_child_tx = wallet.send_self_transfer( from_node=self.nodes[0], utxo_to_spend=parent_utxo, sequence=0xffffffff, fee_rate=Decimal('0.01'), ) # Reports true due to inheritance assert_equal(True, self.nodes[0].getmempoolentry(optout_child_tx['txid'])['bip125-replaceable']) replacement_child_tx = wallet.create_self_transfer( from_node=self.nodes[0], utxo_to_spend=parent_utxo, sequence=0xffffffff, fee_rate=Decimal('0.02'), mempool_valid=False, ) # Broadcast replacement child tx # BIP 125 : # 1. The original transactions signal replaceability explicitly or through inheritance as described in the above # Summary section. # The original transaction (`optout_child_tx`) doesn't signal RBF but its parent (`optin_parent_tx`) does. # The replacement transaction (`replacement_child_tx`) should be able to replace the original transaction. # See CVE-2021-31876 for further explanations. assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable']) assert_raises_rpc_error(-26, 'txn-mempool-conflict', self.nodes[0].sendrawtransaction, replacement_child_tx["hex"], 0)
def run_test(self): self.log.info( "Warning: this test will take about 70 seconds in the best case. Be patient." ) self.log.info( "Test that longpollid doesn't change between successive getblocktemplate() invocations if nothing else happens" ) self.nodes[0].generate(10) template = self.nodes[0].getblocktemplate({'rules': ['segwit']}) longpollid = template['longpollid'] template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert template2['longpollid'] == longpollid self.log.info("Test that longpoll waits if we do nothing") thr = LongpollThread(self.nodes[0]) thr.start() # check that thread still lives thr.join(5) # wait 5 seconds or until thread exits assert thr.is_alive() miniwallets = [MiniWallet(node) for node in self.nodes] self.log.info( "Test that longpoll will terminate if another node generates a block" ) miniwallets[1].generate(1) # generate a block on another node # check that thread will exit now that new transaction entered mempool thr.join(5) # wait 5 seconds or until thread exits assert not thr.is_alive() self.log.info( "Test that longpoll will terminate if we generate a block ourselves" ) thr = LongpollThread(self.nodes[0]) thr.start() miniwallets[0].generate(1) # generate a block on own node thr.join(5) # wait 5 seconds or until thread exits assert not thr.is_alive() # Add enough mature utxos to the wallets, so that all txs spend confirmed coins self.nodes[0].generate(100) self.sync_blocks() self.log.info( "Test that introducing a new transaction into the mempool will terminate the longpoll" ) thr = LongpollThread(self.nodes[0]) thr.start() # generate a random transaction and submit it min_relay_fee = self.nodes[0].getnetworkinfo()["relayfee"] fee_rate = min_relay_fee + Decimal('0.00000010') * random.randint( 0, 20) miniwallets[0].send_self_transfer(from_node=random.choice(self.nodes), fee_rate=fee_rate) # after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned thr.join(60 + 20) assert not thr.is_alive()
def run_test(self): mini_wallet = MiniWallet(self.nodes[1]) mini_wallet.rescan_utxos() spend_utxo = mini_wallet.get_utxo() mini_wallet.send_self_transfer(from_node=self.nodes[1], utxo_to_spend=spend_utxo) self.generate(self.nodes[1], 1) self.log.info("Check legacy txindex") self.nodes[0].getrawtransaction( txid=spend_utxo["txid"]) # Requires -txindex self.stop_nodes() legacy_chain_dir = os.path.join(self.nodes[0].datadir, self.chain) self.log.info("Migrate legacy txindex") migrate_chain_dir = os.path.join(self.nodes[2].datadir, self.chain) shutil.rmtree(migrate_chain_dir) shutil.copytree(legacy_chain_dir, migrate_chain_dir) with self.nodes[2].assert_debug_log([ "Upgrading txindex database...", "txindex is enabled at height 200", ]): self.start_node(2, extra_args=["-txindex"]) self.nodes[2].getrawtransaction( txid=spend_utxo["txid"]) # Requires -txindex self.log.info("Drop legacy txindex") drop_index_chain_dir = os.path.join(self.nodes[1].datadir, self.chain) shutil.rmtree(drop_index_chain_dir) shutil.copytree(legacy_chain_dir, drop_index_chain_dir) self.nodes[1].assert_start_raises_init_error( extra_args=["-txindex"], expected_msg= "Error: The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.", ) # Build txindex from scratch and check there is no error this time self.start_node(1, extra_args=["-txindex"]) self.nodes[2].getrawtransaction( txid=spend_utxo["txid"]) # Requires -txindex self.stop_nodes() self.log.info("Check migrated txindex can not be read by legacy node") err_msg = f": You need to rebuild the database using -reindex to change -txindex.{os.linesep}Please restart with -reindex or -reindex-chainstate to recover." shutil.rmtree(legacy_chain_dir) shutil.copytree(migrate_chain_dir, legacy_chain_dir) self.nodes[0].assert_start_raises_init_error(extra_args=["-txindex"], expected_msg=err_msg) shutil.rmtree(legacy_chain_dir) shutil.copytree(drop_index_chain_dir, legacy_chain_dir) self.nodes[0].assert_start_raises_init_error(extra_args=["-txindex"], expected_msg=err_msg)
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) # the pre-mined test framework chain contains coinbase outputs to the # MiniWallet's default address in blocks 76-100 (see method # BitcoinTestFramework._initialize_chain()) self.wallet.rescan_utxos() self.log.info("Running test simple doublespend...") self.test_simple_doublespend() self.log.info("Running test doublespend chain...") self.test_doublespend_chain() self.log.info("Running test doublespend tree...") self.test_doublespend_tree() self.log.info("Running test replacement feeperkb...") self.test_replacement_feeperkb() self.log.info("Running test spends of conflicting outputs...") self.test_spends_of_conflicting_outputs() self.log.info("Running test new unconfirmed inputs...") self.test_new_unconfirmed_inputs() self.log.info("Running test too many replacements...") self.test_too_many_replacements() self.log.info( "Running test too many replacements using default mempool params..." ) self.test_too_many_replacements_with_default_mempool_params() self.log.info("Running test opt-in...") self.test_opt_in() self.log.info("Running test RPC...") self.test_rpc() self.log.info("Running test prioritised transactions...") self.test_prioritised_transactions() self.log.info("Running test no inherited signaling...") self.test_no_inherited_signaling() self.log.info("Running test replacement relay fee...") self.test_replacement_relay_fee() self.log.info("Running test full replace by fee...") self.test_fullrbf() self.log.info("Passed")
def _test_getblock(self): node = self.nodes[0] miniwallet = MiniWallet(node) miniwallet.scan_blocks(num=5) fee_per_byte = Decimal('0.00000010') fee_per_kb = 1000 * fee_per_byte miniwallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node) blockhash = node.generate(1)[0] self.log.info( "Test that getblock with verbosity 1 doesn't include fee") block = node.getblock(blockhash, 1) assert 'fee' not in block['tx'][1] self.log.info( 'Test that getblock with verbosity 2 includes expected fee') block = node.getblock(blockhash, 2) tx = block['tx'][1] assert 'fee' in tx assert_equal(tx['fee'], tx['vsize'] * fee_per_byte) self.log.info( "Test that getblock with verbosity 2 still works with pruned Undo data" ) datadir = get_datadir_path(self.options.tmpdir, 0) self.log.info( "Test that getblock with invalid verbosity type returns proper error message" ) assert_raises_rpc_error(-1, "JSON value is not an integer as expected", node.getblock, blockhash, "2") def move_block_file(old, new): old_path = os.path.join(datadir, self.chain, 'blocks', old) new_path = os.path.join(datadir, self.chain, 'blocks', new) os.rename(old_path, new_path) # Move instead of deleting so we can restore chain state afterwards move_block_file('rev00000.dat', 'rev_wrong') block = node.getblock(blockhash, 2) assert 'fee' not in block['tx'][1] # Restore chain state move_block_file('rev_wrong', 'rev00000.dat') assert 'previousblockhash' not in node.getblock(node.getblockhash(0)) assert 'nextblockhash' not in node.getblock(node.getbestblockhash())
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.wallet.rescan_utxos() # MAX_ANCESTORS transactions off a confirmed tx should be fine chain = [] utxo = self.wallet.get_utxo() for _ in range(4): utxo, utxo2 = self.chain_tx([utxo], num_outputs=2) chain.append(utxo2) for _ in range(MAX_ANCESTORS - 4): utxo, = self.chain_tx([utxo]) chain.append(utxo) second_chain, = self.chain_tx([self.wallet.get_utxo()]) # Check mempool has MAX_ANCESTORS + 1 transactions in it assert_equal(len(self.nodes[0].getrawmempool()), MAX_ANCESTORS + 1) # Adding one more transaction on to the chain should fail. assert_raises_rpc_error( -26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", self.chain_tx, [utxo]) # ...even if it chains on from some point in the middle of the chain. assert_raises_rpc_error( -26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[2]]) assert_raises_rpc_error( -26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[1]]) # ...even if it chains on to two parent transactions with one in the chain. assert_raises_rpc_error( -26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[0], second_chain]) # ...especially if its > 40k weight assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[0]], num_outputs=350) # But not if it chains directly off the first transaction replacable_tx = self.wallet.send_self_transfer_multi( from_node=self.nodes[0], utxos_to_spend=[chain[0]])['tx'] # and the second chain should work just fine self.chain_tx([second_chain]) # Make sure we can RBF the chain which used our carve-out rule replacable_tx.vout[0].nValue -= 1000000 self.nodes[0].sendrawtransaction(replacable_tx.serialize().hex()) # Finally, check that we added two transactions assert_equal(len(self.nodes[0].getrawmempool()), MAX_ANCESTORS + 3)
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.log.info("Prepare some coins for multiple *rawtransaction commands") self.generate(self.wallet, 10) self.generate(self.nodes[0], COINBASE_MATURITY + 1) self.getrawtransaction_tests() self.createrawtransaction_tests() self.sendrawtransaction_tests() self.sendrawtransaction_testmempoolaccept_tests() self.decoderawtransaction_tests() self.transaction_version_number_tests() if self.requires_wallet and not self.options.descriptors: self.raw_multisig_transaction_legacy_tests()
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.ctx = zmq.Context() try: self.test_basic() self.test_sequence() self.test_mempool_sync() self.test_reorg() self.test_multiple_interfaces() self.test_ipv6() finally: # Destroy the ZMQ context. self.log.debug("Destroying ZMQ context") self.ctx.destroy(linger=None)
def test_muhash_implementation(self): self.log.info("Test MuHash implementation consistency") node = self.nodes[0] wallet = MiniWallet(node) mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 node.setmocktime(mocktime) # Generate 100 blocks and remove the first since we plan to spend its # coinbase block_hashes = self.generate(wallet, 1) + self.generate(node, 99) blocks = list(map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes)) blocks.pop(0) # Create a spending transaction and mine a block which includes it txid = wallet.send_self_transfer(from_node=node)['txid'] tx_block = self.generateblock(node, output=wallet.get_address(), transactions=[txid]) blocks.append(from_hex(CBlock(), node.getblock(tx_block['hash'], False))) # Serialize the outputs that should be in the UTXO set and add them to # a MuHash object muhash = MuHash3072() for height, block in enumerate(blocks): # The Genesis block coinbase is not part of the UTXO set and we # spent the first mined block height += 2 for tx in block.vtx: for n, tx_out in enumerate(tx.vout): coinbase = 1 if not tx.vin[0].prevout.hash else 0 # Skip witness commitment if (coinbase and n > 0): continue data = COutPoint(int(tx.rehash(), 16), n).serialize() data += struct.pack("<i", height * 2 + coinbase) data += tx_out.serialize() muhash.insert(data) finalized = muhash.digest() node_muhash = node.gettxoutsetinfo("muhash")['muhash'] assert_equal(finalized[::-1].hex(), node_muhash) self.log.info("Test deterministic UTXO set hash results") assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "f9aa4fb5ffd10489b9a6994e70ccf1de8a8bfa2d5f201d9857332e9954b0855d") assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "d1725b2fe3ef43e55aa4907480aea98d406fc9e0bf8f60169e2305f1fbf5961b")
def run_test(self): node = self.nodes[0] wallet = MiniWallet(node) # Add enough mature utxos to the wallet so that all txs spend confirmed coins wallet.generate(3) node.generate(100) # Spend block 1/2/3's coinbase transactions # Mine a block # Create three more transactions, spending the spends # Mine another block # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block # ... make sure all the transactions are confirmed again blocks = [] spends1_ids = [ wallet.send_self_transfer(from_node=node)['txid'] for _ in range(3) ] blocks.extend(node.generate(1)) spends2_ids = [ wallet.send_self_transfer(from_node=node)['txid'] for _ in range(3) ] blocks.extend(node.generate(1)) spends_ids = set(spends1_ids + spends2_ids) # mempool should be empty, all txns confirmed assert_equal(set(node.getrawmempool()), set()) confirmed_txns = set( node.getblock(blocks[0])['tx'] + node.getblock(blocks[1])['tx']) # Checks that all spend txns are contained in the mined blocks assert (spends_ids < confirmed_txns) # Use invalidateblock to re-org back node.invalidateblock(blocks[0]) # All txns should be back in mempool with 0 confirmations assert_equal(set(node.getrawmempool()), spends_ids) # Generate another block, they should all get mined node.generate(1) # mempool should be empty, all txns confirmed assert_equal(set(node.getrawmempool()), set()) confirmed_txns = set( node.getblock(blocks[0])['tx'] + node.getblock(blocks[1])['tx']) assert (spends_ids < confirmed_txns)
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.mine_chain() self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete self._test_getblockchaininfo() self._test_getchaintxstats() self._test_gettxoutsetinfo() self._test_getblockheader() self._test_getdifficulty() self._test_getnetworkhashps() self._test_stopatheight() self._test_waitforblockheight() self._test_getblock() assert self.nodes[0].verifychain(4, 0)
def run_test(self): self.log.info("Test that mempool.dat is compatible between versions") old_node, new_node = self.nodes new_wallet = MiniWallet(new_node) new_wallet.generate(1) new_node.generate(100) # Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend. # Otherwise, because coinbases are only valid in a block and not as loose txns, if the nodes aren't synced # unbroadcasted_tx won't pass old_node's `MemPoolAccept::PreChecks`. self.connect_nodes(0, 1) self.sync_blocks() recipient = old_node.getnewaddress() self.stop_node(1) self.log.info("Add a transaction to mempool on old node and shutdown") old_tx_hash = old_node.sendtoaddress(recipient, 0.0001) assert old_tx_hash in old_node.getrawmempool() self.stop_node(0) self.log.info("Move mempool.dat from old to new node") old_node_mempool = os.path.join(old_node.datadir, self.chain, 'mempool.dat') new_node_mempool = os.path.join(new_node.datadir, self.chain, 'mempool.dat') os.rename(old_node_mempool, new_node_mempool) self.log.info("Start new node and verify mempool contains the tx") self.start_node(1) assert old_tx_hash in new_node.getrawmempool() self.log.info( "Add unbroadcasted tx to mempool on new node and shutdown") unbroadcasted_tx_hash = new_wallet.send_self_transfer( from_node=new_node)['txid'] assert unbroadcasted_tx_hash in new_node.getrawmempool() mempool = new_node.getrawmempool(True) assert mempool[unbroadcasted_tx_hash]['unbroadcast'] self.stop_node(1) self.log.info("Move mempool.dat from new to old node") os.rename(new_node_mempool, old_node_mempool) self.log.info( "Start old node again and verify mempool contains both txs") self.start_node(0, ['-nowallet']) assert old_tx_hash in old_node.getrawmempool() assert unbroadcasted_tx_hash in old_node.getrawmempool()
def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address): self.log.info('Test that fee cannot be bumped when it has descendant') # parent is send-to-self, so we don't have to check which output is change when creating the child tx parent_id = spend_one_input(rbf_node, rbf_node_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) tx = rbf_node.signrawtransactionwithwallet(tx) rbf_node.sendrawtransaction(tx["hex"]) assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) # create tx with descendant in the mempool by using MiniWallet miniwallet = MiniWallet(rbf_node) parent_id = spend_one_input(rbf_node, miniwallet.get_address()) tx = rbf_node.gettransaction(txid=parent_id, verbose=True)['decoded'] miniwallet.scan_tx(tx) miniwallet.send_self_transfer(from_node=rbf_node) assert_raises_rpc_error(-8, "Transaction has descendants in the mempool", rbf_node.bumpfee, parent_id) self.clear_mempool()
def run_test(self): self.wallet = MiniWallet(self.nodes[0]) # Add enough mature utxos to the wallet so that all txs spend confirmed coins. self.generate(self.wallet, 35) self.generate(self.nodes[0], COINBASE_MATURITY) self.test_chain_limits() self.test_desc_count_limits() self.test_desc_count_limits_2() self.test_anc_count_limits() self.test_anc_count_limits_2() self.test_anc_count_limits_bushy() # The node will accept our (nonstandard) extra large OP_RETURN outputs self.restart_node(0, extra_args=["-acceptnonstdtxn=1"]) self.test_anc_size_limits() self.test_desc_size_limits()
def run_test(self): # we get spendable outputs from the premined chain starting # at block 76. see BitcoinTestFramework._initialize_chain() for details self.wallet = MiniWallet(self.nodes[0]) self.wallet.rescan_utxos() # we create the tx on the first node and wait until it syncs to node_deprecated # thus, any differences must be coming from getmempoolentry or getrawmempool tx = self.wallet.send_self_transfer(from_node=self.nodes[0]) self.nodes[1].sendrawtransaction(tx["hex"]) deprecated_fields = [ "ancestorfees", "descendantfees", "modifiedfee", "fee" ] self.test_getmempoolentry(tx["txid"], deprecated_fields) self.test_getrawmempool(tx["txid"], deprecated_fields) self.test_deprecated_fields_match(tx["txid"])
def run_test(self): self.log.info("This test is time consuming, please be patient") self.log.info("Splitting inputs so we can generate tx's") # Split two coinbases into many small utxos self.start_node(0) self.wallet = MiniWallet(self.nodes[0]) self.wallet.rescan_utxos() self.initial_split(self.nodes[0]) self.log.info("Finished splitting") # Now we can connect the other nodes, didn't want to connect them earlier # so the estimates would not be affected by the splitting transactions self.start_node(1) self.start_node(2) self.connect_nodes(1, 0) self.connect_nodes(0, 2) self.connect_nodes(2, 1) self.sync_all() self.log.info("Testing estimates with single transactions.") self.sanity_check_estimates_range() # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee self.log.info( "Test fee rate estimation after restarting node with high MempoolMinFee" ) self.test_feerate_mempoolminfee() self.log.info("Restarting node with fresh estimation") self.stop_node(0) fee_dat = os.path.join(self.nodes[0].datadir, self.chain, "fee_estimates.dat") os.remove(fee_dat) self.start_node(0) self.connect_nodes(0, 1) self.connect_nodes(0, 2) self.log.info("Testing estimates with RBF.") self.sanity_check_rbf_estimates(self.confutxo + self.memutxo) self.log.info("Testing that fee estimation is disabled in blocksonly.") self.restart_node(0, ["-blocksonly"]) assert_raises_rpc_error(-32603, "Fee estimation disabled", self.nodes[0].estimatesmartfee, 2)