aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Falbesoner <sebastian.falbesoner@gmail.com>2021-10-27 17:34:54 +0200
committerSebastian Falbesoner <sebastian.falbesoner@gmail.com>2021-10-27 17:49:53 +0200
commitd2c4904ef707e2023ceb8dfbfe61a92c4060e100 (patch)
tree58e73d18758bfe9bb93c34dd207cff2db163f065
parentab25ef8c7f767258d5fe44f53b35ad8bd51ed5cd (diff)
downloadbitcoin-d2c4904ef707e2023ceb8dfbfe61a92c4060e100.tar.xz
test: MiniWallet: more deterministic coin selection for coinbase UTXOs (oldest first)
The coin selection strategy for MiniWallet is quite straight-forward: simply pick a single UTXO with the largest value: self._utxos = sorted(self._utxos, key=lambda k: k['value']) utxo_to_spend = utxo_to_spend or self._utxos.pop() 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.
-rw-r--r--test/functional/test_framework/wallet.py11
1 files changed, 6 insertions, 5 deletions
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 81aad20079..d2c21f3525 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -88,13 +88,13 @@ class MiniWallet:
res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
assert_equal(True, res['success'])
for utxo in res['unspents']:
- self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount']})
+ self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount'], 'height': utxo['height']})
def scan_tx(self, tx):
"""Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
for out in tx['vout']:
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
- self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
+ self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value'], 'height': 0})
def sign_tx(self, tx, fixed_length=True):
"""Sign tx that has been created by MiniWallet in P2PK mode"""
@@ -115,8 +115,9 @@ class MiniWallet:
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
blocks = self._test_node.generatetodescriptor(num_blocks, self.get_descriptor(), **kwargs)
for b in blocks:
- cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
- self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
+ block_info = self._test_node.getblock(blockhash=b, verbosity=2)
+ cb_tx = block_info['tx'][0]
+ self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value'], 'height': block_info['height']})
return blocks
def get_descriptor(self):
@@ -170,7 +171,7 @@ class MiniWallet:
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'])
+ self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height']))
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
if self._priv_key is None:
vsize = Decimal(96) # anyone-can-spend