Skip to content

Commit ae674a0

Browse files
author
merge-script
committed
Merge bitcoin/bitcoin#22998: test: use MiniWallet for make_utxo helper in feature_rbf.py
f680d27 test: use MiniWallet for make_utxo helper in feature_rbf.py (Sebastian Falbesoner) 0f27524 test: scale amounts in test_doublespend_tree down by factor 10 (Sebastian Falbesoner) d1e2481 test: scale amounts in test_doublespend_chain down by factor 10 (Sebastian Falbesoner) Pull request description: This PR aims to further increase MiniWallet usage in the functional test feature_rbf.py by using it in the `make_utxo(...)` helper, which is the only part that needs a wallet for most sub-tests. In order to do that, the amounts for the utxos have to be scaled down in two sub-tests first (`test_doublespend_chain` and `test_doublespend_tree`, see first two commits), since we need amounts passed to `make_utxo` than can be funded by only one input. For creating UTXOs with a value of 50 BTC, we'd need to implement a method for consolidating multiple utxos into one first, which seems to be overkill. Note that after this PR's change, there is only one sub-test left (`test_rpc`) that needs the wallet compiled into bitcoind. ACKs for top commit: MarcoFalke: review ACK f680d27 🦐 Tree-SHA512: 46c8c245086a9e79855c4ede2f8f412333cf2658136805196b203b3567c89398d77fcb80715c0bb72fdc84331cc67544b2fdc259193a3adcb2fc36e147c26fce
2 parents 8f022a5 + f680d27 commit ae674a0

File tree

2 files changed

+39
-36
lines changed

2 files changed

+39
-36
lines changed

test/functional/feature_rbf.py

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from copy import deepcopy
88
from decimal import Decimal
99

10-
from test_framework.blocktools import COINBASE_MATURITY
1110
from test_framework.messages import (
1211
BIP125_SEQUENCE_NUMBER,
1312
COIN,
@@ -18,10 +17,18 @@
1817
)
1918
from test_framework.script import CScript, OP_DROP
2019
from test_framework.test_framework import BitcoinTestFramework
21-
from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
22-
from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, DUMMY_2_P2WPKH_SCRIPT
20+
from test_framework.util import (
21+
assert_equal,
22+
assert_greater_than,
23+
assert_raises_rpc_error,
24+
)
25+
from test_framework.script_util import (
26+
DUMMY_P2WPKH_SCRIPT,
27+
DUMMY_2_P2WPKH_SCRIPT,
28+
)
2329
from test_framework.wallet import MiniWallet
2430

31+
2532
MAX_REPLACEMENT_LIMIT = 100
2633
class ReplaceByFeeTest(BitcoinTestFramework):
2734
def set_test_params(self):
@@ -89,29 +96,23 @@ def run_test(self):
8996
def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
9097
"""Create a txout with a given amount and scriptPubKey
9198
92-
Mines coins as needed.
99+
Assumes that MiniWallet has enough funds to cover the amount and the fixed fee
100+
(from it's internal utxos, the one with the largest value is taken).
93101
94102
confirmed - txouts created will be confirmed in the blockchain;
95103
unconfirmed otherwise.
96104
"""
97-
fee = 1 * COIN
98-
while node.getbalance() < satoshi_round((amount + fee) / COIN):
99-
self.generate(node, COINBASE_MATURITY)
100-
101-
new_addr = node.getnewaddress()
102-
txid = node.sendtoaddress(new_addr, satoshi_round((amount + fee) / COIN))
103-
tx1 = node.getrawtransaction(txid, 1)
104-
txid = int(txid, 16)
105-
i, _ = next(filter(lambda vout: new_addr == vout[1]['scriptPubKey']['address'], enumerate(tx1['vout'])))
106-
107-
tx2 = CTransaction()
108-
tx2.vin = [CTxIn(COutPoint(txid, i))]
109-
tx2.vout = [CTxOut(amount, scriptPubKey)]
110-
tx2.rehash()
111-
112-
signed_tx = node.signrawtransactionwithwallet(tx2.serialize().hex())
113-
114-
txid = node.sendrawtransaction(signed_tx['hex'], 0)
105+
# MiniWallet only supports sweeping utxos to its own internal scriptPubKey, so in
106+
# order to create an output with arbitrary amount/scriptPubKey, we have to add it
107+
# manually after calling the create_self_transfer method. The MiniWallet output's
108+
# nValue has to be adapted accordingly (amount and fee deduction). To keep things
109+
# simple, we use a fixed fee of 1000 Satoshis here.
110+
fee = 1000
111+
tx = self.wallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx']
112+
assert_greater_than(tx.vout[0].nValue, amount + fee)
113+
tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet
114+
tx.vout.append(CTxOut(amount, scriptPubKey)) # desired output -> to be returned
115+
txid = self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
115116

116117
# If requested, ensure txouts are confirmed.
117118
if confirmed:
@@ -124,7 +125,7 @@ def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRI
124125
assert new_size < mempool_size
125126
mempool_size = new_size
126127

127-
return COutPoint(int(txid, 16), 0)
128+
return COutPoint(int(txid, 16), 1)
128129

129130
def test_simple_doublespend(self):
130131
"""Simple doublespend"""
@@ -161,14 +162,14 @@ def test_simple_doublespend(self):
161162
def test_doublespend_chain(self):
162163
"""Doublespend of a long chain"""
163164

164-
initial_nValue = 50 * COIN
165+
initial_nValue = 5 * COIN
165166
tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
166167

167168
prevout = tx0_outpoint
168169
remaining_value = initial_nValue
169170
chain_txids = []
170-
while remaining_value > 10 * COIN:
171-
remaining_value -= 1 * COIN
171+
while remaining_value > 1 * COIN:
172+
remaining_value -= int(0.1 * COIN)
172173
tx = CTransaction()
173174
tx.vin = [CTxIn(prevout, nSequence=0)]
174175
tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
@@ -178,10 +179,10 @@ def test_doublespend_chain(self):
178179
prevout = COutPoint(int(txid, 16), 0)
179180

180181
# Whether the double-spend is allowed is evaluated by including all
181-
# child fees - 40 BTC - so this attempt is rejected.
182+
# child fees - 4 BTC - so this attempt is rejected.
182183
dbl_tx = CTransaction()
183184
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
184-
dbl_tx.vout = [CTxOut(initial_nValue - 30 * COIN, DUMMY_P2WPKH_SCRIPT)]
185+
dbl_tx.vout = [CTxOut(initial_nValue - 3 * COIN, DUMMY_P2WPKH_SCRIPT)]
185186
dbl_tx_hex = dbl_tx.serialize().hex()
186187

187188
# This will raise an exception due to insufficient fee
@@ -190,7 +191,7 @@ def test_doublespend_chain(self):
190191
# Accepted with sufficient fee
191192
dbl_tx = CTransaction()
192193
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
193-
dbl_tx.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
194+
dbl_tx.vout = [CTxOut(int(0.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
194195
dbl_tx_hex = dbl_tx.serialize().hex()
195196
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
196197

@@ -201,10 +202,10 @@ def test_doublespend_chain(self):
201202
def test_doublespend_tree(self):
202203
"""Doublespend of a big tree of transactions"""
203204

204-
initial_nValue = 50 * COIN
205+
initial_nValue = 5 * COIN
205206
tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
206207

207-
def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _total_txs=None):
208+
def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.00001 * COIN, _total_txs=None):
208209
if _total_txs is None:
209210
_total_txs = [0]
210211
if _total_txs[0] >= max_txs:
@@ -235,7 +236,7 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _to
235236
_total_txs=_total_txs):
236237
yield x
237238

238-
fee = int(0.0001 * COIN)
239+
fee = int(0.00001 * COIN)
239240
n = MAX_REPLACEMENT_LIMIT
240241
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
241242
assert_equal(len(tree_txs), n)
@@ -248,10 +249,10 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _to
248249
# This will raise an exception due to insufficient fee
249250
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
250251

251-
# 1 BTC fee is enough
252+
# 0.1 BTC fee is enough
252253
dbl_tx = CTransaction()
253254
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
254-
dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, DUMMY_P2WPKH_SCRIPT)]
255+
dbl_tx.vout = [CTxOut(initial_nValue - fee * n - int(0.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
255256
dbl_tx_hex = dbl_tx.serialize().hex()
256257
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
257258

@@ -264,7 +265,7 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _to
264265
# Try again, but with more total transactions than the "max txs
265266
# double-spent at once" anti-DoS limit.
266267
for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2):
267-
fee = int(0.0001 * COIN)
268+
fee = int(0.00001 * COIN)
268269
tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
269270
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
270271
assert_equal(len(tree_txs), n)

test/functional/test_framework/wallet.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_
180180
return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
181181

182182
def sendrawtransaction(self, *, from_node, tx_hex):
183-
from_node.sendrawtransaction(tx_hex)
183+
txid = from_node.sendrawtransaction(tx_hex)
184184
self.scan_tx(from_node.decoderawtransaction(tx_hex))
185+
return txid
186+
185187

186188
def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE):
187189
"""Build a transaction that spends parent_txid.vout[n] and produces one output with

0 commit comments

Comments
 (0)