Skip to content

Commit

Permalink
policy: bip125-replaceable status can't be determined w/o mempool
Browse files Browse the repository at this point in the history
The number of original transactions to be replaced and their
descendant transactions must not exceed \`MAX_BIP125_REPLACEMENT_CANDIDATES\`.
This is according to BIP125 RBF (Rule bitcoin#5). Without an entry in the mempool
(whether it's because we don't have a local mempool or we don't have \`tx\` in
our mempool) this check can't be done, and therefore the bip125-replaceable
status will be unknown regardless of how \`tx\` itself signals.
  • Loading branch information
mjdietzx committed Nov 28, 2021
1 parent 3fc1f8b commit 54782ae
Show file tree
Hide file tree
Showing 4 changed files with 8 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ class ChainImpl : public Chain
}
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
{
if (!m_node.mempool) return IsRBFOptInEmptyMempool(tx);
if (!m_node.mempool) return RBFTransactionState::UNKNOWN;
LOCK(m_node.mempool->cs);
return IsRBFOptIn(tx, *m_node.mempool);
}
Expand Down
16 changes: 5 additions & 11 deletions src/policy/rbf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)

CTxMemPool::setEntries ancestors;

// First check the transaction itself.
if (SignalsOptInRBF(tx)) {
return RBFTransactionState::REPLACEABLE_BIP125;
}

// If this transaction is not in our mempool, then we can't be sure
// we will know about all its inputs.
if (!pool.exists(GenTxid::Txid(tx.GetHash()))) {
return RBFTransactionState::UNKNOWN;
}

// First check the transaction itself.
if (SignalsOptInRBF(tx)) {
return RBFTransactionState::REPLACEABLE_BIP125;
}

// If all the inputs have nSequence >= maxint-1, it still might be
// signaled for RBF if any unconfirmed parents have signaled.
uint64_t noLimit = std::numeric_limits<uint64_t>::max();
Expand All @@ -41,12 +41,6 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
return RBFTransactionState::FINAL;
}

RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
{
// If we don't have a local mempool we can only check the transaction itself.
return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
}

std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting,
Expand Down
1 change: 0 additions & 1 deletion src/policy/rbf.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ enum class RBFTransactionState {
* @return The rbf state
*/
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);

/** Get all descendants of iters_conflicting. Also enforce BIP125 Rule #5, "The number of original
* transactions to be replaced and their descendant transactions which will be evicted from the
Expand Down
4 changes: 2 additions & 2 deletions test/functional/wallet_listtransactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def get_unconfirmed_utxo_entry(node, txid_to_match):
for n in self.nodes[0:2]:
assert_equal(n.gettransaction(txid_1)["bip125-replaceable"], "no")
assert_equal(n.gettransaction(txid_2)["bip125-replaceable"], "no")
assert_equal(n.gettransaction(txid_3)["bip125-replaceable"], "yes")
assert_equal(n.gettransaction(txid_3)["bip125-replaceable"], "unknown")
assert_equal(n.gettransaction(txid_3b)["bip125-replaceable"], "yes")
assert_equal(n.gettransaction(txid_4)["bip125-replaceable"], "unknown")

Expand All @@ -207,7 +207,7 @@ def get_unconfirmed_utxo_entry(node, txid_to_match):
txs = {tx['txid']: tx['bip125-replaceable'] for tx in n.listsinceblock()['transactions']}
assert_equal(txs[txid_1], "no")
assert_equal(txs[txid_2], "no")
assert_equal(txs[txid_3], "yes")
assert_equal(txs[txid_3], "unknown")
assert_equal(txs[txid_3b], "yes")
assert_equal(txs[txid_4], "unknown")

Expand Down

0 comments on commit 54782ae

Please sign in to comment.