-
Notifications
You must be signed in to change notification settings - Fork 38.8k
test: MiniWallet: more deterministic coin selection for coinbase UTXOs (oldest first) #23375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: MiniWallet: more deterministic coin selection for coinbase UTXOs (oldest first) #23375
Conversation
…s (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.
|
Concept ACK going to test soon.. |
|
Interesting. I assumed that I guess it can't hurt to be explicit. |
Your assumptions regarding
Those two problems could be solved on their own (sort in descending order and pop off the first element works I guess?), but rather than always taking care to append to the UTXOs in the right order (which is i.e. impossible if |
shaavan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK d2c4904
This change allows considering the height of utxo while sorting the utxos (based on values). This addition is especially helpful in case two utxos have the same value. This addition also prevents "premature spend of coinbase" error when a non-coinbase transaction of equal value is available.
mjdietzx
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree this should be done. Thinking more about your approach here, but seems nice and simple.
What about unconfirmed utxos though? If we have an unconfirmed utxo is our utxo set, what should its height be?
| 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}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this tx is in a block, don't you need to set utxo.height accordingly? ie will height always be 0 as is hardcoded here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now scan_tx is used only immediately after broadcasting a transaction (internally in MiniWallet.sendrawtransaction(), and in one instance in wallet_bumpfee.py), i.e. on unconfirmed txs. I don't think it makes much sense to ever call it for confirmed transactions (though I could miss something) -- setting the 'height' field is particularly important for coinbase UTXOs, and those seem to be all tackled by MiniWallet.generate (explicitely generate block to MiniWallet address) and MiniWallet.rescan_utxo (scan in pre-mined chain for MiniWallet address).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@theStack thanks for the explanation, I was in doubt about it but now it is clear for me.
| 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'])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Genius 👀
|
review ACK d2c4904 Nice change. Previously it picked the last-mined largest value, which is wrong. It should pick the first-mined largest value. I added this bug 😅 |
…ion 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#23371 (comment) (PR bitcoin#23371). ACKs for top commit: MarcoFalke: review ACK d2c4904 shaavan: ACK d2c4904 Tree-SHA512: 15d67b42fb8b77fd53022ea2ab8a6ed2b615567f3ce73bab16c06bfcb687c1a04dcb0360d0c2287c526b604cd3ac5eef7b14ce46fc31e23047ce1a3290027306
The coin selection strategy for MiniWallet is quite straight-forward: simply pick a single UTXO with the largest value:
bitcoin/test/functional/test_framework/wallet.py
Lines 173 to 174 in ab25ef8
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-coinbasereject if an UTXO from a too-recent block is picked. Introduce block height as second criteria (saved inself._utxosin the methodsgenerate(...)andrescan_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 viascan_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 #23371 (comment) (PR #23371).