Skip to content

Commit 8bac3b1

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#23375: test: MiniWallet: more deterministic coin selection for coinbase UTXOs (oldest first)
d2c4904 test: MiniWallet: more deterministic coin selection for coinbase UTXOs (oldest first) (Sebastian Falbesoner) Pull request description: The coin selection strategy for MiniWallet is quite straight-forward: simply pick a single UTXO with the largest value: https://github.com/bitcoin/bitcoin/blob/ab25ef8c7f767258d5fe44f53b35ad8bd51ed5cd/test/functional/test_framework/wallet.py#L173-L174 If there are several candidates with the same value, however, it is not clear which one is taken. This can be particularly problematic for coinbase outputs with fixed block subsidy, since spending could lead to a `bad-txns-premature-spend-of-coinbase` reject if an UTXO from a too-recent block is picked. Introduce block height as second criteria (saved in `self._utxos` in the methods `generate(...)` and `rescan_utxos(...)`), in order to avoid potential issues with coinbases that are not matured yet. If there is a tie between coinbase UTXOs and non-coinbase UTXOs (the latter are added via `scan_tx(...)`), prefer the non-coinbase UTXOs, since those don't need to mature. The issue came up while refactoring the test rpc_blockchain.py, see bitcoin/bitcoin#23371 (comment) (PR #23371). ACKs for top commit: MarcoFalke: review ACK d2c4904 shaavan: ACK d2c4904 Tree-SHA512: 15d67b42fb8b77fd53022ea2ab8a6ed2b615567f3ce73bab16c06bfcb687c1a04dcb0360d0c2287c526b604cd3ac5eef7b14ce46fc31e23047ce1a3290027306
2 parents c426e0d + d2c4904 commit 8bac3b1

File tree

1 file changed

+6
-5
lines changed

1 file changed

+6
-5
lines changed

test/functional/test_framework/wallet.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ def rescan_utxos(self):
8888
res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
8989
assert_equal(True, res['success'])
9090
for utxo in res['unspents']:
91-
self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount']})
91+
self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount'], 'height': utxo['height']})
9292

9393
def scan_tx(self, tx):
9494
"""Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
9595
for out in tx['vout']:
9696
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
97-
self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
97+
self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value'], 'height': 0})
9898

9999
def sign_tx(self, tx, fixed_length=True):
100100
"""Sign tx that has been created by MiniWallet in P2PK mode"""
@@ -115,8 +115,9 @@ def generate(self, num_blocks, **kwargs):
115115
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
116116
blocks = self._test_node.generatetodescriptor(num_blocks, self.get_descriptor(), **kwargs)
117117
for b in blocks:
118-
cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
119-
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
118+
block_info = self._test_node.getblock(blockhash=b, verbosity=2)
119+
cb_tx = block_info['tx'][0]
120+
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value'], 'height': block_info['height']})
120121
return blocks
121122

122123
def get_descriptor(self):
@@ -170,7 +171,7 @@ def send_to(self, *, from_node, scriptPubKey, amount, fee=1000):
170171

171172
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
172173
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
173-
self._utxos = sorted(self._utxos, key=lambda k: k['value'])
174+
self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height']))
174175
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
175176
if self._priv_key is None:
176177
vsize = Decimal(96) # anyone-can-spend

0 commit comments

Comments
 (0)