Skip to content

Commit 564e1ab

Browse files
author
MarcoFalke
committed
Merge #19800: test: Mockwallet
fa188c9 test: Use MiniWalet in p2p_feefilter (MarcoFalke) fa39c62 test: inline hashToHex (MarcoFalke) Pull request description: This introduces a minimalistic test wallet, which can be used as a drop in replacement for the Bitcoin Core wallet to create dummy transactions with a given fee rate. ACKs for top commit: jnewbery: utACK fa188c9 Tree-SHA512: 0aad9cb14eea4f0055bd6a47cc8c8f82a16941b152598c3bf1e083aae84cca4ffa23f0b854a362a68be1b917deba1b5ec7c0207b63b0805d747ba9a7d1d82efe
2 parents 4f229d8 + fa188c9 commit 564e1ab

File tree

2 files changed

+75
-17
lines changed

2 files changed

+75
-17
lines changed

test/functional/p2p_feefilter.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@
1010
from test_framework.p2p import P2PInterface, p2p_lock
1111
from test_framework.test_framework import BitcoinTestFramework
1212
from test_framework.util import assert_equal
13-
14-
15-
def hashToHex(hash):
16-
return format(hash, '064x')
13+
from test_framework.wallet import MiniWallet
1714

1815

1916
class FeefilterConn(P2PInterface):
@@ -35,7 +32,7 @@ def __init__(self):
3532
def on_inv(self, message):
3633
for i in message.inv:
3734
if (i.type == MSG_TX) or (i.type == MSG_WTX):
38-
self.txinvs.append(hashToHex(i.hash))
35+
self.txinvs.append('{:064x}'.format(i.hash))
3936

4037
def wait_for_invs_to_match(self, invs_expected):
4138
invs_expected.sort()
@@ -61,9 +58,6 @@ def set_test_params(self):
6158
6259
]] * self.num_nodes
6360

64-
def skip_test_if_missing_module(self):
65-
self.skip_if_no_wallet()
66-
6761
def run_test(self):
6862
self.test_feefilter_forcerelay()
6963
self.test_feefilter()
@@ -83,27 +77,28 @@ def test_feefilter_forcerelay(self):
8377
def test_feefilter(self):
8478
node1 = self.nodes[1]
8579
node0 = self.nodes[0]
80+
miniwallet = MiniWallet(node1)
81+
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
82+
miniwallet.generate(5)
83+
node1.generate(100)
8684

8785
conn = self.nodes[0].add_p2p_connection(TestP2PConn())
8886

8987
self.log.info("Test txs paying 0.2 sat/byte are received by test connection")
90-
node1.settxfee(Decimal("0.00000200"))
91-
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
88+
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000200'), from_node=node1)['wtxid'] for _ in range(3)]
9289
conn.wait_for_invs_to_match(txids)
9390
conn.clear_invs()
9491

9592
# Set a fee filter of 0.15 sat/byte on test connection
9693
conn.send_and_ping(msg_feefilter(150))
9794

9895
self.log.info("Test txs paying 0.15 sat/byte are received by test connection")
99-
node1.settxfee(Decimal("0.00000150"))
100-
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
96+
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000150'), from_node=node1)['wtxid'] for _ in range(3)]
10197
conn.wait_for_invs_to_match(txids)
10298
conn.clear_invs()
10399

104100
self.log.info("Test txs paying 0.1 sat/byte are no longer received by test connection")
105-
node1.settxfee(Decimal("0.00000100"))
106-
[node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
101+
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000100'), from_node=node1)['wtxid'] for _ in range(3)]
107102
self.sync_mempools() # must be sure node 0 has received all txs
108103

109104
# Send one transaction from node0 that should be received, so that we
@@ -113,14 +108,13 @@ def test_feefilter(self):
113108
# to 35 entries in an inv, which means that when this next transaction
114109
# is eligible for relay, the prior transactions from node1 are eligible
115110
# as well.
116-
node0.settxfee(Decimal("0.00020000"))
117-
txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
111+
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node0)['wtxid'] for _ in range(3)]
118112
conn.wait_for_invs_to_match(txids)
119113
conn.clear_invs()
120114

121115
self.log.info("Remove fee filter and check txs are received again")
122116
conn.send_and_ping(msg_feefilter(0))
123-
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
117+
txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node1)['wtxid'] for _ in range(3)]
124118
conn.wait_for_invs_to_match(txids)
125119
conn.clear_invs()
126120

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2020 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""A limited-functionality wallet, which may replace a real wallet in tests"""
6+
7+
from decimal import Decimal
8+
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
9+
from test_framework.messages import (
10+
COIN,
11+
COutPoint,
12+
CTransaction,
13+
CTxIn,
14+
CTxInWitness,
15+
CTxOut,
16+
)
17+
from test_framework.script import (
18+
CScript,
19+
OP_TRUE,
20+
)
21+
from test_framework.util import (
22+
assert_equal,
23+
hex_str_to_bytes,
24+
satoshi_round,
25+
)
26+
27+
28+
class MiniWallet:
29+
def __init__(self, test_node):
30+
self._test_node = test_node
31+
self._utxos = []
32+
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
33+
self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
34+
35+
def generate(self, num_blocks):
36+
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
37+
blocks = self._test_node.generatetoaddress(num_blocks, self._address)
38+
for b in blocks:
39+
cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
40+
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
41+
return blocks
42+
43+
def send_self_transfer(self, *, fee_rate, from_node):
44+
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
45+
self._utxos = sorted(self._utxos, key=lambda k: -k['value'])
46+
largest_utxo = self._utxos.pop() # Pick the largest utxo and hope it covers the fee
47+
vsize = Decimal(96)
48+
send_value = satoshi_round(largest_utxo['value'] - fee_rate * (vsize / 1000))
49+
fee = largest_utxo['value'] - send_value
50+
assert (send_value > 0)
51+
52+
tx = CTransaction()
53+
tx.vin = [CTxIn(COutPoint(int(largest_utxo['txid'], 16), largest_utxo['vout']))]
54+
tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
55+
tx.wit.vtxinwit = [CTxInWitness()]
56+
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
57+
tx_hex = tx.serialize().hex()
58+
59+
txid = from_node.sendrawtransaction(tx_hex)
60+
self._utxos.append({'txid': txid, 'vout': 0, 'value': send_value})
61+
tx_info = from_node.getmempoolentry(txid)
62+
assert_equal(tx_info['vsize'], vsize)
63+
assert_equal(tx_info['fee'], fee)
64+
return {'txid': txid, 'wtxid': tx_info['wtxid'], 'hex': tx_hex}

0 commit comments

Comments
 (0)