diff options
author | MarcoFalke <falke.marco@gmail.com> | 2021-09-29 09:07:41 +0200 |
---|---|---|
committer | MarcoFalke <falke.marco@gmail.com> | 2021-09-29 09:13:07 +0200 |
commit | 33e31f8df920acd3a4b13aab5bd6a6733480133a (patch) | |
tree | 83ff6d7ec3ee150c27191e4cf411424a5866c6d8 /test | |
parent | 8daecf4d1d86823f7ba2c9f02eaf0d49893084be (diff) | |
parent | cfdb6baa22b8eb526fcbe1e5131bf4a3fd0cd672 (diff) |
Merge bitcoin/bitcoin#23079: test: use MiniWallet for p2p_filter.py
cfdb6baa22b8eb526fcbe1e5131bf4a3fd0cd672 test: use MiniWallet for p2p_filter.py (Sebastian Falbesoner)
6fc2cd3f09d023b2e971dcdf8472956259f94c1d test: introduce helper to create random P2WPKH scriptPubKeys (Sebastian Falbesoner)
aa26797f69e6ed10efcad9fb07be7f5b3b0ee513 test: MiniWallet: add `send_to` method to create arbitrary txouts (Sebastian Falbesoner)
Pull request description:
This PR enables one more of the non-wallet functional tests (p2p_filter.py) to be run even with the Bitcoin Core wallet disabled by using the MiniWallet instead, as proposed in #20078.
For this purpose, a MiniWallet method `send_to` is introduced first, which allows to create arbitrary outputs (scriptPubKey/amount). Note that the implementation for this is already present in feature_rbf.py (recently added in PR #22998), i.e. it is simply moved to the MiniWallet interface.
ACKs for top commit:
laanwj:
Code review ACK cfdb6baa22b8eb526fcbe1e5131bf4a3fd0cd672
Tree-SHA512: 13b063631f0d7af065b7757cfe8b47c9be6cb9850ac5db2968a2bba4f5a18cdc9f89173a9b03971545356225082042f5fdbe49d3036027d18e8b7eb042d04f5e
Diffstat (limited to 'test')
-rwxr-xr-x | test/functional/feature_rbf.py | 18 | ||||
-rwxr-xr-x | test/functional/p2p_filter.py | 34 | ||||
-rw-r--r-- | test/functional/test_framework/wallet.py | 28 |
3 files changed, 50 insertions, 30 deletions
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index a6e9cd2ed1..d759a5aab5 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -19,7 +19,6 @@ from test_framework.script import CScript, OP_DROP from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_greater_than, assert_raises_rpc_error, ) from test_framework.script_util import ( @@ -96,23 +95,10 @@ class ReplaceByFeeTest(BitcoinTestFramework): def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT): """Create a txout with a given amount and scriptPubKey - Assumes that MiniWallet has enough funds to cover the amount and the fixed fee - (from it's internal utxos, the one with the largest value is taken). - confirmed - txouts created will be confirmed in the blockchain; unconfirmed otherwise. """ - # MiniWallet only supports sweeping utxos to its own internal scriptPubKey, so in - # order to create an output with arbitrary amount/scriptPubKey, we have to add it - # manually after calling the create_self_transfer method. The MiniWallet output's - # nValue has to be adapted accordingly (amount and fee deduction). To keep things - # simple, we use a fixed fee of 1000 Satoshis here. - fee = 1000 - tx = self.wallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx'] - assert_greater_than(tx.vout[0].nValue, amount + fee) - tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet - tx.vout.append(CTxOut(amount, scriptPubKey)) # desired output -> to be returned - txid = self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex()) + txid, n = self.wallet.send_to(from_node=node, scriptPubKey=scriptPubKey, amount=amount) # If requested, ensure txouts are confirmed. if confirmed: @@ -125,7 +111,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): assert new_size < mempool_size mempool_size = new_size - return COutPoint(int(txid, 16), 1) + return COutPoint(int(txid, 16), n) def test_simple_doublespend(self): """Simple doublespend""" diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py index a040665fba..0d8c298bea 100755 --- a/test/functional/p2p_filter.py +++ b/test/functional/p2p_filter.py @@ -8,6 +8,7 @@ Test BIP 37 from test_framework.messages import ( CInv, + COIN, MAX_BLOOM_FILTER_SIZE, MAX_BLOOM_HASH_FUNCS, MSG_BLOCK, @@ -28,11 +29,15 @@ from test_framework.p2p import ( ) from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE from test_framework.test_framework import BitcoinTestFramework +from test_framework.wallet import ( + MiniWallet, + random_p2wpkh, +) class P2PBloomFilter(P2PInterface): # This is a P2SH watch-only wallet - watch_script_pubkey = 'a914ffffffffffffffffffffffffffffffffffffffff87' + watch_script_pubkey = bytes.fromhex('a914ffffffffffffffffffffffffffffffffffffffff87') # The initial filter (n=10, fp=0.000001) with just the above scriptPubKey added watch_filter_init = msg_filterload( data= @@ -93,8 +98,9 @@ class FilterTest(BitcoinTestFramework): '-whitelist=noban@127.0.0.1', # immediate tx relay ]] - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() + def generatetoscriptpubkey(self, scriptpubkey): + """Helper to generate a single block to the given scriptPubKey.""" + return self.generatetodescriptor(self.nodes[0], 1, f'raw({scriptpubkey.hex()})')[0] def test_size_limits(self, filter_peer): self.log.info('Check that too large filter is rejected') @@ -130,8 +136,7 @@ class FilterTest(BitcoinTestFramework): filter_peer = P2PBloomFilter() self.log.debug("Create a tx relevant to the peer before connecting") - filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address'] - txid = self.nodes[0].sendtoaddress(filter_address, 90) + txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN) self.log.debug("Send a mempool msg after connecting and check that the tx is received") self.nodes[0].add_p2p_connection(filter_peer) @@ -142,8 +147,7 @@ class FilterTest(BitcoinTestFramework): def test_frelay_false(self, filter_peer): self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set") filter_peer.tx_received = False - filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address'] - self.nodes[0].sendtoaddress(filter_address, 90) + self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN) # Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays filter_peer.sync_with_ping() assert not filter_peer.tx_received @@ -156,45 +160,44 @@ class FilterTest(BitcoinTestFramework): filter_peer.send_and_ping(filter_peer.watch_filter_init) # If fRelay is not already True, sending filterload sets it to True assert self.nodes[0].getpeerinfo()[0]['relaytxes'] - filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address'] self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block') - block_hash = self.generatetoaddress(self.nodes[0], 1, filter_address)[0] + block_hash = self.generatetoscriptpubkey(filter_peer.watch_script_pubkey) txid = self.nodes[0].getblock(block_hash)['tx'][0] filter_peer.wait_for_merkleblock(block_hash) filter_peer.wait_for_tx(txid) self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block') filter_peer.tx_received = False - block_hash = self.generatetoaddress(self.nodes[0], 1, self.nodes[0].getnewaddress())[0] + block_hash = self.generatetoscriptpubkey(random_p2wpkh()) filter_peer.wait_for_merkleblock(block_hash) assert not filter_peer.tx_received self.log.info('Check that we not receive a tx if the filter does not match a mempool tx') filter_peer.merkleblock_received = False filter_peer.tx_received = False - self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 90) + self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=random_p2wpkh(), amount=7 * COIN) filter_peer.sync_send_with_ping() assert not filter_peer.merkleblock_received assert not filter_peer.tx_received self.log.info('Check that we receive a tx if the filter matches a mempool tx') filter_peer.merkleblock_received = False - txid = self.nodes[0].sendtoaddress(filter_address, 90) + txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN) filter_peer.wait_for_tx(txid) assert not filter_peer.merkleblock_received self.log.info('Check that after deleting filter all txs get relayed again') filter_peer.send_and_ping(msg_filterclear()) for _ in range(5): - txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 7) + txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=random_p2wpkh(), amount=7 * COIN) filter_peer.wait_for_tx(txid) self.log.info('Check that request for filtered blocks is ignored if no filter is set') filter_peer.merkleblock_received = False filter_peer.tx_received = False with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']): - block_hash = self.generatetoaddress(self.nodes[0], 1, self.nodes[0].getnewaddress())[0] + block_hash = self.generatetoscriptpubkey(random_p2wpkh()) filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))]) filter_peer.sync_with_ping() assert not filter_peer.merkleblock_received @@ -210,6 +213,9 @@ class FilterTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() 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) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index cea59c6d69..ef27cb3221 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -28,6 +28,7 @@ from test_framework.script import ( OP_NOP, SIGHASH_ALL, ) +from test_framework.script_util import key_to_p2wpkh_script from test_framework.util import ( assert_equal, assert_greater_than_or_equal, @@ -146,6 +147,25 @@ class MiniWallet: self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx['hex']) return tx + def send_to(self, *, from_node, scriptPubKey, amount, fee=1000): + """ + Create and send a tx with an output to a given scriptPubKey/amount, + plus a change output to our internal address. To keep things simple, a + fixed fee given in Satoshi is used. + + Note that this method fails if there is no single internal utxo + available that can cover the cost for the amount and the fixed fee + (the utxo with the largest value is taken). + + Returns a tuple (txid, n) referring to the created external utxo outpoint. + """ + tx = self.create_self_transfer(from_node=from_node, fee_rate=0, mempool_valid=False)['tx'] + assert_greater_than_or_equal(tx.vout[0].nValue, amount + fee) + tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet + tx.vout.append(CTxOut(amount, scriptPubKey)) # arbitrary output -> to be returned + txid = self.sendrawtransaction(from_node=from_node, tx_hex=tx.serialize().hex()) + return txid, 1 + def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0): """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed.""" self._utxos = sorted(self._utxos, key=lambda k: k['value']) @@ -188,6 +208,14 @@ class MiniWallet: return txid +def random_p2wpkh(): + """Generate a random P2WPKH scriptPubKey. Can be used when a random destination is needed, + but no compiled wallet is available (e.g. as replacement to the getnewaddress RPC).""" + key = ECKey() + key.generate() + return key_to_p2wpkh_script(key.get_pubkey().get_bytes()) + + def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE): """Build a transaction that spends parent_txid.vout[n] and produces one output with amount = parent_value with a fee deducted. |