mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-21 13:10:08 +01:00
Merge bitcoin/bitcoin#28251: validation: fix coins disappearing mid-package evaluation
32c1dd1ad6[test] mempool coins disappearing mid-package evaluation (glozow)a67f460c3f[refactor] split setup in mempool_limit test (glozow)d08696120e[test framework] add ability to spend only confirmed utxos (glozow)3ea71feb11[validation] don't LimitMempoolSize in any subpackage submissions (glozow)d227b7234c[validation] return correct result when already-in-mempool tx gets evicted (glozow)9698b81828[refactor] back-fill results in AcceptPackage (glozow)8ad7ad3392[validation] make PackageMempoolAcceptResult members mutable (glozow)03b87c11ca[validation] add AcceptSubPackage to delegate Accept* calls and clean up m_view (glozow)3f01a3dab1[CCoinsViewMemPool] track non-base coins and allow Reset (glozow)7d7f7a1189[policy] check for duplicate txids in package (glozow) Pull request description: While we are evaluating a package, we split it into "subpackages" for evaluation (currently subpackages all have size 1 except the last one). If a subpackage has size 1, we may add a tx to mempool and call `LimitMempoolSize()`, which evicts transactions if the mempool gets full. We handle the case where the just-submitted transaction is evicted immediately, but we don't handle the case in which a transaction from a previous subpackage (either just submitted or already in mempool) is evicted. Mainly, since the coins created by the evicted transaction are cached in `m_view`, we don't realize the UTXO has disappeared until `CheckInputsFromMempoolAndCache` asserts that they exist. Also, the returned `PackageMempoolAcceptResult` reports that the transaction is in mempool even though it isn't anymore. Fix this by not calling `LimitMempoolSize()` until the very end, and editing the results map with "mempool full" if things fall out. Pointed out by instagibbs infaeed687e5on top of the v3 PR. ACKs for top commit: instagibbs: reACK32c1dd1ad6Tree-SHA512: 61e7f69db4712e5e5bfa27d037ab66bdd97f1bf60a8d9ffb96adb1f0609af012c810d681102ee5c7baec7b5fe8cb7c304a60c63ccc445d00d86a2b7f0e7ddb90
This commit is contained in:
@@ -208,7 +208,7 @@ class MiniWallet:
|
||||
assert_equal(self._mode, MiniWalletMode.ADDRESS_OP_TRUE)
|
||||
return self._address
|
||||
|
||||
def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True) -> dict:
|
||||
def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True, confirmed_only=False) -> dict:
|
||||
"""
|
||||
Returns a utxo and marks it as spent (pops it from the internal list)
|
||||
|
||||
@@ -224,19 +224,23 @@ class MiniWallet:
|
||||
utxo_filter = reversed(mature_coins) # By default the largest utxo
|
||||
if vout is not None:
|
||||
utxo_filter = filter(lambda utxo: vout == utxo['vout'], utxo_filter)
|
||||
if confirmed_only:
|
||||
utxo_filter = filter(lambda utxo: utxo['confirmations'] > 0, utxo_filter)
|
||||
index = self._utxos.index(next(utxo_filter))
|
||||
if mark_as_spent:
|
||||
return self._utxos.pop(index)
|
||||
else:
|
||||
return self._utxos[index]
|
||||
|
||||
def get_utxos(self, *, include_immature_coinbase=False, mark_as_spent=True):
|
||||
def get_utxos(self, *, include_immature_coinbase=False, mark_as_spent=True, confirmed_only=False):
|
||||
"""Returns the list of all utxos and optionally mark them as spent"""
|
||||
if not include_immature_coinbase:
|
||||
blocks_height = self._test_node.getblockchaininfo()['blocks']
|
||||
utxo_filter = filter(lambda utxo: not utxo['coinbase'] or COINBASE_MATURITY - 1 <= blocks_height - utxo['height'], self._utxos)
|
||||
else:
|
||||
utxo_filter = self._utxos
|
||||
if confirmed_only:
|
||||
utxo_filter = filter(lambda utxo: utxo['confirmations'] > 0, utxo_filter)
|
||||
utxos = deepcopy(list(utxo_filter))
|
||||
if mark_as_spent:
|
||||
self._utxos = []
|
||||
@@ -286,14 +290,15 @@ class MiniWallet:
|
||||
locktime=0,
|
||||
sequence=0,
|
||||
fee_per_output=1000,
|
||||
target_weight=0
|
||||
target_weight=0,
|
||||
confirmed_only=False
|
||||
):
|
||||
"""
|
||||
Create and return a transaction that spends the given UTXOs and creates a
|
||||
certain number of outputs with equal amounts. The output amounts can be
|
||||
set by amount_per_output or automatically calculated with a fee_per_output.
|
||||
"""
|
||||
utxos_to_spend = utxos_to_spend or [self.get_utxo()]
|
||||
utxos_to_spend = utxos_to_spend or [self.get_utxo(confirmed_only=confirmed_only)]
|
||||
sequence = [sequence] * len(utxos_to_spend) if type(sequence) is int else sequence
|
||||
assert_equal(len(utxos_to_spend), len(sequence))
|
||||
|
||||
@@ -333,9 +338,17 @@ class MiniWallet:
|
||||
"tx": tx,
|
||||
}
|
||||
|
||||
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), fee=Decimal("0"), utxo_to_spend=None, locktime=0, sequence=0, target_weight=0):
|
||||
def create_self_transfer(self, *,
|
||||
fee_rate=Decimal("0.003"),
|
||||
fee=Decimal("0"),
|
||||
utxo_to_spend=None,
|
||||
locktime=0,
|
||||
sequence=0,
|
||||
target_weight=0,
|
||||
confirmed_only=False
|
||||
):
|
||||
"""Create and return a tx with the specified fee. If fee is 0, use fee_rate, where the resulting fee may be exact or at most one satoshi higher than needed."""
|
||||
utxo_to_spend = utxo_to_spend or self.get_utxo()
|
||||
utxo_to_spend = utxo_to_spend or self.get_utxo(confirmed_only=confirmed_only)
|
||||
assert fee_rate >= 0
|
||||
assert fee >= 0
|
||||
# calculate fee
|
||||
|
||||
Reference in New Issue
Block a user