diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index 2405268ca..a7b7dba71 100644 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -1,517 +1,514 @@ # Copyright (c) 2014-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test BIP68 implementation.""" import time from test_framework.blocktools import create_block from test_framework.messages import ( XEC, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, ToHex, ) from test_framework.script import CScript from test_framework.test_framework import BitcoinTestFramework from test_framework.txtools import pad_tx from test_framework.util import ( assert_equal, assert_greater_than, assert_raises_rpc_error, satoshi_round, ) SEQUENCE_LOCKTIME_DISABLE_FLAG = 1 << 31 # this means use time (0 means height) SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22 # this is a bit-shift SEQUENCE_LOCKTIME_GRANULARITY = 9 SEQUENCE_LOCKTIME_MASK = 0x0000FFFF # RPC error for non-BIP68 final transactions NOT_FINAL_ERROR = "non-BIP68-final" class BIP68Test(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.extra_args = [ [ "-noparkdeepreorg", "-acceptnonstdtxn=1", ], [ "-acceptnonstdtxn=0", "-automaticunparking=1", ], ] def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] - # Generate some coins - self.generate(self.nodes[0], 110) - self.log.info("Running test disable flag") self.test_disable_flag() self.log.info("Running test sequence-lock-confirmed-inputs") self.test_sequence_lock_confirmed_inputs() self.log.info("Running test sequence-lock-unconfirmed-inputs") self.test_sequence_lock_unconfirmed_inputs() self.log.info("Running test BIP68 not consensus before versionbits activation") self.test_bip68_not_consensus() self.log.info("Activating BIP68 (and 112/113)") self.activateCSV() print("Verifying nVersion=2 transactions are standard.") print( "Note that with current versions of bitcoin software, nVersion=2" " transactions are always standard (independent of BIP68 activation" " status)." ) self.test_version2_relay() self.log.info("Passed") # Test that BIP68 is not in effect if tx version is 1, or if # the first sequence bit is set. def test_disable_flag(self): # Create some unconfirmed inputs new_addr = self.nodes[0].getnewaddress() # send 2,000,000 XEC self.nodes[0].sendtoaddress(new_addr, 2000000) utxos = self.nodes[0].listunspent(0, 0) assert len(utxos) > 0 utxo = utxos[0] tx1 = CTransaction() value = int(satoshi_round(utxo["amount"] - self.relayfee) * XEC) # Check that the disable flag disables relative locktime. # If sequence locks were used, this would require 1 block for the # input to mature. sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1 tx1.vin = [ CTxIn( COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value ) ] tx1.vout = [CTxOut(value, CScript([b"a"]))] pad_tx(tx1) tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"] tx1_id = self.nodes[0].sendrawtransaction(tx1_signed) tx1_id = int(tx1_id, 16) # This transaction will enable sequence-locks, so this transaction should # fail tx2 = CTransaction() tx2.nVersion = 2 sequence_value = sequence_value & 0x7FFFFFFF tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)] tx2.vout = [CTxOut(int(value - self.relayfee * XEC), CScript([b"a"]))] pad_tx(tx2) tx2.rehash() assert_raises_rpc_error( -26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2) ) # Setting the version back down to 1 should disable the sequence lock, # so this should be accepted. tx2.nVersion = 1 self.nodes[0].sendrawtransaction(ToHex(tx2)) # Calculate the median time past of a prior block ("confirmations" before # the current tip). def get_median_time_past(self, confirmations): block_hash = self.nodes[0].getblockhash( self.nodes[0].getblockcount() - confirmations ) return self.nodes[0].getblockheader(block_hash)["mediantime"] # Test that sequence locks are respected for transactions spending # confirmed inputs. def test_sequence_lock_confirmed_inputs(self): # Create lots of confirmed utxos, and use them to generate lots of random # transactions. max_outputs = 50 addresses = [] while len(addresses) < max_outputs: addresses.append(self.nodes[0].getnewaddress()) while len(self.nodes[0].listunspent()) < 200: import random random.shuffle(addresses) num_outputs = random.randint(1, max_outputs) outputs = {} for i in range(num_outputs): outputs[addresses[i]] = random.randint(1, 20) * 10000 self.nodes[0].sendmany("", outputs) self.generate(self.nodes[0], 1) utxos = self.nodes[0].listunspent() # Try creating a lot of random transactions. # Each time, choose a random number of inputs, and randomly set # some of those inputs to be sequence locked (and randomly choose # between height/time locking). Small random chance of making the locks # all pass. for _ in range(400): # Randomly choose up to 10 inputs num_inputs = random.randint(1, 10) random.shuffle(utxos) # Track whether any sequence locks used should fail should_pass = True # Track whether this transaction was built with sequence locks using_sequence_locks = False tx = CTransaction() tx.nVersion = 2 value = 0 for j in range(num_inputs): # this disables sequence locks sequence_value = 0xFFFFFFFE # 50% chance we enable sequence locks if random.randint(0, 1): using_sequence_locks = True # 10% of the time, make the input sequence value pass input_will_pass = random.randint(1, 10) == 1 sequence_value = utxos[j]["confirmations"] if not input_will_pass: sequence_value += 1 should_pass = False # Figure out what the median-time-past was for the confirmed input # Note that if an input has N confirmations, we're going back N blocks # from the tip so that we're looking up MTP of the block # PRIOR to the one the input appears in, as per the BIP68 # spec. orig_time = self.get_median_time_past(utxos[j]["confirmations"]) # MTP of the tip cur_time = self.get_median_time_past(0) # can only timelock this input if it's not too old -- # otherwise use height can_time_lock = True if ( (cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY ) >= SEQUENCE_LOCKTIME_MASK: can_time_lock = False # if time-lockable, then 50% chance we make this a time # lock if random.randint(0, 1) and can_time_lock: # Find first time-lock value that fails, or latest one # that succeeds time_delta = sequence_value << SEQUENCE_LOCKTIME_GRANULARITY if input_will_pass and time_delta > cur_time - orig_time: sequence_value = ( cur_time - orig_time ) >> SEQUENCE_LOCKTIME_GRANULARITY elif not input_will_pass and time_delta <= cur_time - orig_time: sequence_value = ( (cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY ) + 1 sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx.vin.append( CTxIn( COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value, ) ) value += utxos[j]["amount"] * XEC # Overestimate the size of the tx - signatures should be less than # 120 bytes, and leave 50 for the output tx_size = len(ToHex(tx)) // 2 + 120 * num_inputs + 50 tx.vout.append( CTxOut( int(value - self.relayfee * tx_size * XEC / 1000), CScript([b"a"]) ) ) rawtx = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))["hex"] if using_sequence_locks and not should_pass: # This transaction should be rejected assert_raises_rpc_error( -26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx ) else: # This raw transaction should be accepted self.nodes[0].sendrawtransaction(rawtx) utxos = self.nodes[0].listunspent() # Test that sequence locks on unconfirmed inputs must have nSequence # height or time of 0 to be accepted. # Then test that BIP68-invalid transactions are removed from the mempool # after a reorg. def test_sequence_lock_unconfirmed_inputs(self): # Store height so we can easily reset the chain at the end of the test cur_height = self.nodes[0].getblockcount() # Create a mempool tx. txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2000000) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) tx1.rehash() # As the fees are calculated prior to the transaction being signed, # there is some uncertainty that calculate fee provides the correct # minimal fee. Since regtest coins are free, let's go ahead and # increase the fee by an order of magnitude to ensure this test # passes. fee_multiplier = 10 # Anyone-can-spend mempool tx. # Sequence lock of 0 should pass. tx2 = CTransaction() tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [CTxOut(int(0), CScript([b"a"]))] tx2.vout[0].nValue = tx1.vout[0].nValue - fee_multiplier * self.nodes[ 0 ].calculate_fee(tx2) tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() self.nodes[0].sendrawtransaction(tx2_raw) # Create a spend of the 0th output of orig_tx with a sequence lock # of 1, and test what happens when submitting. # orig_tx.vout[0] must be an anyone-can-spend output def test_nonzero_locks(orig_tx, node, use_height_lock): sequence_value = 1 if not use_height_lock: sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx = CTransaction() tx.nVersion = 2 tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)] tx.vout = [ CTxOut( int( orig_tx.vout[0].nValue - fee_multiplier * node.calculate_fee(tx) ), CScript([b"a"]), ) ] pad_tx(tx) tx.rehash() if orig_tx.hash in node.getrawmempool(): # sendrawtransaction should fail if the tx is in the mempool assert_raises_rpc_error( -26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx) ) else: # sendrawtransaction should succeed if the tx is not in the # mempool node.sendrawtransaction(ToHex(tx)) return tx test_nonzero_locks(tx2, self.nodes[0], use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], use_height_lock=False) # Now mine some blocks, but make sure tx2 doesn't get mined. # Use prioritisetransaction to lower the effective feerate to 0 self.nodes[0].prioritisetransaction( txid=tx2.hash, fee_delta=-fee_multiplier * self.nodes[0].calculate_fee(tx2) ) cur_time = int(time.time()) for _ in range(10): self.nodes[0].setmocktime(cur_time + 600) self.generate(self.nodes[0], 1, sync_fun=self.no_op) cur_time += 600 assert tx2.hash in self.nodes[0].getrawmempool() test_nonzero_locks(tx2, self.nodes[0], use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], use_height_lock=False) # Mine tx2, and then try again self.nodes[0].prioritisetransaction( txid=tx2.hash, fee_delta=fee_multiplier * self.nodes[0].calculate_fee(tx2) ) # Advance the time on the node so that we can test timelocks self.nodes[0].setmocktime(cur_time + 600) # Save block template now to use for the reorg later tmpl = self.nodes[0].getblocktemplate() self.generate(self.nodes[0], 1) assert tx2.hash not in self.nodes[0].getrawmempool() # Now that tx2 is not in the mempool, a sequence locked spend should # succeed tx3 = test_nonzero_locks(tx2, self.nodes[0], use_height_lock=False) assert tx3.hash in self.nodes[0].getrawmempool() self.generate(self.nodes[0], 1) assert tx3.hash not in self.nodes[0].getrawmempool() # One more test, this time using height locks tx4 = test_nonzero_locks(tx3, self.nodes[0], use_height_lock=True) assert tx4.hash in self.nodes[0].getrawmempool() # Now try combining confirmed and unconfirmed inputs tx5 = test_nonzero_locks(tx4, self.nodes[0], use_height_lock=True) assert tx5.hash not in self.nodes[0].getrawmempool() utxos = self.nodes[0].listunspent() tx5.vin.append( CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1) ) tx5.vout[0].nValue += int(utxos[0]["amount"] * XEC) raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"] assert_raises_rpc_error( -26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5 ) # Test mempool-BIP68 consistency after reorg # # State of the transactions in the last blocks: # ... -> [ tx2 ] -> [ tx3 ] # tip-1 tip # And currently tx4 is in the mempool. # # If we invalidate the tip, tx3 should get added to the mempool, causing # tx4 to be removed (fails sequence-lock). self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) assert tx4.hash not in self.nodes[0].getrawmempool() assert tx3.hash in self.nodes[0].getrawmempool() # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in # diagram above). # This would cause tx2 to be added back to the mempool, which in turn causes # tx3 to be removed. for i in range(2): block = create_block(tmpl=tmpl, ntime=cur_time) block.rehash() block.solve() tip = block.sha256 assert_equal( None if i == 1 else "inconclusive", self.nodes[0].submitblock(ToHex(block)), ) tmpl = self.nodes[0].getblocktemplate() tmpl["previousblockhash"] = f"{tip:x}" tmpl["transactions"] = [] cur_time += 1 mempool = self.nodes[0].getrawmempool() assert tx3.hash not in mempool assert tx2.hash in mempool # Reset the chain and get rid of the mocktimed-blocks self.nodes[0].setmocktime(0) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height + 1)) self.generate(self.nodes[0], 10, sync_fun=self.no_op) def get_csv_status(self): height = self.nodes[0].getblockchaininfo()["blocks"] return height >= 576 # Make sure that BIP68 isn't being used to validate blocks, prior to # versionbits activation. If more blocks are mined prior to this test # being run, then it's possible the test has activated the soft fork, and # this test should be moved to run earlier, or deleted. def test_bip68_not_consensus(self): assert_equal(self.get_csv_status(), False) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2000000) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) tx1.rehash() # Make an anyone-can-spend transaction tx2 = CTransaction() tx2.nVersion = 1 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [ CTxOut(int(tx1.vout[0].nValue - self.relayfee * XEC), CScript([b"a"])) ] # sign tx2 tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) pad_tx(tx2) tx2.rehash() self.nodes[0].sendrawtransaction(ToHex(tx2)) # Now make an invalid spend of tx2 according to BIP68 # 100 block relative locktime sequence_value = 100 tx3 = CTransaction() tx3.nVersion = 2 tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)] tx3.vout = [ CTxOut(int(tx2.vout[0].nValue - self.relayfee * XEC), CScript([b"a"])) ] pad_tx(tx3) tx3.rehash() assert_raises_rpc_error( -26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3) ) # make a block that violates bip68; ensure that the tip updates block = create_block(tmpl=self.nodes[0].getblocktemplate()) block.vtx.extend(sorted([tx1, tx2, tx3], key=lambda tx: tx.get_id())) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() assert_equal(None, self.nodes[0].submitblock(ToHex(block))) assert_equal(self.nodes[0].getbestblockhash(), block.hash) def activateCSV(self): # activation should happen at block height 576 csv_activation_height = 576 height = self.nodes[0].getblockcount() assert_greater_than(csv_activation_height - height, 1) self.generate( self.nodes[0], csv_activation_height - height - 1, sync_fun=self.no_op ) assert_equal(self.get_csv_status(), False) self.disconnect_nodes(0, 1) self.generate(self.nodes[0], 1, sync_fun=self.no_op) assert_equal(self.get_csv_status(), True) # We have a block that has CSV activated, but we want to be at # the activation point, so we invalidate the tip. self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) self.connect_nodes(0, 1) self.sync_blocks() # Use self.nodes[1] to test that version 2 transactions are standard. def test_version2_relay(self): inputs = [] outputs = {self.nodes[1].getnewaddress(): 1000000.0} rawtx = self.nodes[1].createrawtransaction(inputs, outputs) rawtxfund = self.nodes[1].fundrawtransaction(rawtx)["hex"] tx = FromHex(CTransaction(), rawtxfund) tx.nVersion = 2 tx_signed = self.nodes[1].signrawtransactionwithwallet(ToHex(tx))["hex"] self.nodes[1].sendrawtransaction(tx_signed) if __name__ == "__main__": BIP68Test().main() diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index 4af0094d5..1eccadc30 100644 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -1,106 +1,102 @@ # Copyright (c) 2014-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test longpolling with getblocktemplate.""" import random import threading from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import get_rpc_proxy from test_framework.wallet import MiniWallet class LongpollThread(threading.Thread): def __init__(self, node): threading.Thread.__init__(self) # query current longpollid templat = node.getblocktemplate() self.longpollid = templat["longpollid"] # create a new connection to the node, we can't use the same # connection from two threads self.node = get_rpc_proxy( node.url, 1, timeout=600, coveragedir=node.coverage_dir ) def run(self): self.node.getblocktemplate({"longpollid": self.longpollid}) class GetBlockTemplateLPTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.supports_cli = False 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.generate(self.nodes[0], 10) templat = self.nodes[0].getblocktemplate() longpollid = templat["longpollid"] # longpollid should not change between successive invocations if # nothing else happens templat2 = self.nodes[0].getblocktemplate() assert templat2["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 # wait 5 seconds or until thread exits thr.join(5) 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" ) # generate a block on another node self.generate(miniwallets[1], 1) # check that thread will exit now that new transaction entered mempool # wait 5 seconds or until thread exits thr.join(5) 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() # generate a block on own node self.generate(miniwallets[0], 1) # wait 5 seconds or until thread exits thr.join(5) assert not thr.is_alive() - # Add enough mature utxos to the wallets, so that all txs spend - # confirmed coins - self.generate(self.nodes[0], 100) - 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.10") * 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() if __name__ == "__main__": GetBlockTemplateLPTest().main() diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index 726a2963d..4bd9ab91b 100644 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -1,180 +1,176 @@ # Copyright (c) 2016-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test processing of feefilter messages.""" from decimal import Decimal from test_framework.messages import MSG_TX, msg_feefilter from test_framework.p2p import P2PInterface, p2p_lock from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, uint256_hex from test_framework.wallet import MiniWallet class FeefilterConn(P2PInterface): feefilter_received = False def on_feefilter(self, message): self.feefilter_received = True def assert_feefilter_received(self, recv: bool): with p2p_lock: assert_equal(self.feefilter_received, recv) class TestP2PConn(P2PInterface): def __init__(self): super().__init__() self.txinvs = [] def on_inv(self, message): for i in message.inv: if i.type == MSG_TX: self.txinvs.append(uint256_hex(i.hash)) def wait_for_invs_to_match(self, invs_expected): invs_expected.sort() self.wait_until(lambda: invs_expected == sorted(self.txinvs)) def clear_invs(self): with p2p_lock: self.txinvs = [] class FeeFilterTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 # We lower the various required feerates for this test # to catch a corner-case where feefilter used to slightly undercut # mempool and wallet feerate calculation based on GetFee # rounding down 3 places, leading to stranded transactions. # See issue #16499 # grant noban permission to all peers to speed up tx relay / mempool # sync self.extra_args = [ [ "-minrelaytxfee=1", "-mintxfee=1", "-whitelist=noban@127.0.0.1", ] ] * self.num_nodes def run_test(self): self.test_feefilter_forcerelay() self.test_feefilter() self.test_feefilter_blocksonly() def test_feefilter_forcerelay(self): self.log.info( "Check that peers without forcerelay permission (default) get a feefilter" " message" ) self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received( True ) self.log.info( "Check that peers with forcerelay permission do not get a feefilter message" ) self.restart_node(0, extra_args=["-whitelist=forcerelay@127.0.0.1"]) self.nodes[0].add_p2p_connection(FeefilterConn()).assert_feefilter_received( False ) # Restart to disconnect peers and load default extra_args self.restart_node(0) self.connect_nodes(1, 0) def test_feefilter(self): node1 = self.nodes[1] node0 = self.nodes[0] miniwallet = MiniWallet(node1) - # Add enough mature utxos to the wallet, so that all txs spend - # confirmed coins - self.generate(miniwallet, 5) - self.generate(node1, 100) conn = self.nodes[0].add_p2p_connection(TestP2PConn()) self.log.info("Test txs paying 0.2 sat/byte are received by test connection") txids = [ miniwallet.send_self_transfer(fee_rate=Decimal("2.00"), from_node=node1)[ "txid" ] for _ in range(3) ] conn.wait_for_invs_to_match(txids) conn.clear_invs() # Set a fee filter of 0.15 sat/byte on test connection conn.send_and_ping(msg_feefilter(150)) self.log.info("Test txs paying 0.15 sat/byte are received by test connection") txids = [ miniwallet.send_self_transfer(fee_rate=Decimal("1.50"), from_node=node1)[ "txid" ] for _ in range(3) ] conn.wait_for_invs_to_match(txids) conn.clear_invs() self.log.info( "Test txs paying 0.1 sat/byte are no longer received by test connection" ) txids = [ miniwallet.send_self_transfer(fee_rate=Decimal("1.00"), from_node=node1)[ "txid" ] for _ in range(3) ] self.sync_mempools() # must be sure node 0 has received all txs # Send one transaction from node0 that should be received, so that we # we can sync the test on receipt (if node1's txs were relayed, they'd # be received by the time this node0 tx is received). This is # unfortunately reliant on the current relay behavior where we batch up # to 35 entries in an inv, which means that when this next transaction # is eligible for relay, the prior transactions from node1 are eligible # as well. txids = [ miniwallet.send_self_transfer(fee_rate=Decimal("200.00"), from_node=node0)[ "txid" ] ] conn.wait_for_invs_to_match(txids) conn.clear_invs() # must be sure node 1 has received all txs self.sync_mempools() self.log.info("Remove fee filter and check txs are received again") conn.send_and_ping(msg_feefilter(0)) txids = [ miniwallet.send_self_transfer(fee_rate=Decimal("200.00"), from_node=node1)[ "txid" ] for _ in range(3) ] conn.wait_for_invs_to_match(txids) conn.clear_invs() def test_feefilter_blocksonly(self): """Test that we don't send fee filters to block-relay-only peers and when we're in blocksonly mode.""" self.log.info("Check that we don't send fee filters to block-relay-only peers.") feefilter_peer = self.nodes[0].add_outbound_p2p_connection( FeefilterConn(), p2p_idx=0, connection_type="block-relay-only" ) feefilter_peer.sync_with_ping() feefilter_peer.assert_feefilter_received(False) self.log.info("Check that we don't send fee filters when in blocksonly mode.") self.restart_node(0, ["-blocksonly"]) feefilter_peer = self.nodes[0].add_p2p_connection(FeefilterConn()) feefilter_peer.sync_with_ping() feefilter_peer.assert_feefilter_received(False) if __name__ == "__main__": FeeFilterTest().main() diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py index c355abb83..1964b9e8d 100644 --- a/test/functional/p2p_leak_tx.py +++ b/test/functional/p2p_leak_tx.py @@ -1,65 +1,61 @@ # Copyright (c) 2017-2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test that we don't leak txs to inbound peers that we haven't yet announced to""" from test_framework.messages import MSG_TX, CInv, msg_getdata from test_framework.p2p import P2PDataStore, p2p_lock from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal from test_framework.wallet import MiniWallet class P2PNode(P2PDataStore): def on_inv(self, msg): pass class P2PLeakTxTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 def run_test(self): # The block and tx generating node gen_node = self.nodes[0] miniwallet = MiniWallet(gen_node) - # Add enough mature utxos to the wallet, so that all txs spend - # confirmed coins - self.generate(miniwallet, 1) - self.generate(gen_node, 100) # An "attacking" inbound peer inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) MAX_REPEATS = 100 self.log.info(f"Running test up to {MAX_REPEATS} times.") for i in range(MAX_REPEATS): self.log.info(f"Run repeat {i + 1}") txid = miniwallet.send_self_transfer(from_node=gen_node)["txid"] 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(f"tx {txid} was not yet announced to us.") 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( f"tx {txid} was already announced to us. Try test again." ) assert int(txid, 16) in [ inv.hash for inv in inbound_peer.last_message["inv"].inv ] if __name__ == "__main__": P2PLeakTxTest().main()