diff options
Diffstat (limited to 'src/validation.cpp')
-rw-r--r-- | src/validation.cpp | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/src/validation.cpp b/src/validation.cpp index 2c36f0d00d..ddaefbf274 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -585,12 +585,14 @@ private: // of checking a given transaction. struct Workspace { explicit Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {} - /** Txids of mempool transactions that this transaction directly conflicts with. */ + /** Txids of mempool transactions that this transaction directly conflicts with or may + * replace via sibling eviction. */ std::set<Txid> m_conflicts; - /** Iterators to mempool entries that this transaction directly conflicts with. */ + /** Iterators to mempool entries that this transaction directly conflicts with or may + * replace via sibling eviction. */ CTxMemPool::setEntries m_iters_conflicting; /** Iterators to all mempool entries that would be replaced by this transaction, including - * those it directly conflicts with and their descendants. */ + * m_conflicts and their descendants. */ CTxMemPool::setEntries m_all_conflicting; /** All mempool ancestors of this transaction. */ CTxMemPool::setEntries m_ancestors; @@ -598,9 +600,12 @@ private: * inserted into the mempool until Finalize(). */ std::unique_ptr<CTxMemPoolEntry> m_entry; /** Pointers to the transactions that have been removed from the mempool and replaced by - * this transaction, used to return to the MemPoolAccept caller. Only populated if + * this transaction (everything in m_all_conflicting), used to return to the MemPoolAccept caller. Only populated if * validation is successful and the original transactions are removed. */ std::list<CTransactionRef> m_replaced_transactions; + /** Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting, + * m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */ + bool m_sibling_eviction{false}; /** Virtual size of the transaction as used by the mempool, calculated using serialized size * of the transaction and sigops. */ @@ -690,7 +695,8 @@ private: Chainstate& m_active_chainstate; - /** Whether the transaction(s) would replace any mempool transactions. If so, RBF rules apply. */ + /** Whether the transaction(s) would replace any mempool transactions and/or evict any siblings. + * If so, RBF rules apply. */ bool m_rbf{false}; }; @@ -954,8 +960,27 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) } ws.m_ancestors = *ancestors; + // Even though just checking direct mempool parents for inheritance would be sufficient, we + // check using the full ancestor set here because it's more convenient to use what we have + // already calculated. if (const auto err{SingleV3Checks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) { - return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "v3-rule-violation", err->first); + // Disabled within package validation. + if (err->second != nullptr && args.m_allow_replacement) { + // Potential sibling eviction. Add the sibling to our list of mempool conflicts to be + // included in RBF checks. + ws.m_conflicts.insert(err->second->GetHash()); + // Adding the sibling to m_iters_conflicting here means that it doesn't count towards + // RBF Carve Out above. This is correct, since removing to-be-replaced transactions from + // the descendant count is done separately in SingleV3Checks for v3 transactions. + ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value()); + ws.m_sibling_eviction = true; + // The sibling will be treated as part of the to-be-replaced set in ReplacementChecks. + // Note that we are not checking whether it opts in to replaceability via BIP125 or v3 + // (which is normally done in PreChecks). However, the only way a v3 transaction can + // have a non-v3 and non-BIP125 descendant is due to a reorg. + } else { + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "v3-rule-violation", err->first); + } } // A transaction that spends outputs that would be replaced by it is invalid. Now @@ -995,18 +1020,21 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws) // Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not // TX_RECONSIDERABLE, because it cannot be bypassed using package validation. // This must be changed if package RBF is enabled. - return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string); } // Calculate all conflicting entries and enforce Rule #5. if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, ws.m_all_conflicting)}) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, - "too many potential replacements", *err_string); + strprintf("too many potential replacements%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string); } // Enforce Rule #2. if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, ws.m_iters_conflicting)}) { + // Sibling eviction is only done for v3 transactions, which cannot have multiple ancestors. + Assume(!ws.m_sibling_eviction); return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, - "replacement-adds-unconfirmed", *err_string); + strprintf("replacement-adds-unconfirmed%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string); } // Check if it's economically rational to mine this transaction rather than the ones it // replaces and pays for its own relay fees. Enforce Rules #3 and #4. @@ -1019,7 +1047,8 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws) // Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not // TX_RECONSIDERABLE, because it cannot be bypassed using package validation. // This must be changed if package RBF is enabled. - return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string); } return true; } |