Skip to content

Commit cd48372

Browse files
committed
[mempool] Allow rebroadcast for same-txid-different-wtxid transactions
This commit fixes some slightly unexpected behaviour when: - there is already transaction in the mempool (the "mempool tx") - BroadcastTransaction() is called for a transaction with the same txid as the mempool transaction but a different witness (the "new tx") Prior to this commit, if BroadcastTransaction() is called with relay=true, then it'll call RelayTransaction() using the txid/wtxid of the new tx, not the txid/wtxid of the mempool tx. For wtxid relay peers, in SendMessages(), the wtxid of the new tx will be taken from setInventoryTxToSend, but will then be filtered out from the vector of wtxids to announce, since m_mempool.info() won't find the transaction (the mempool contains the mempool tx, which has a different wtxid from the new tx). Fix this by calling RelayTransaction() with the wtxid of the mempool transaction in this case.
1 parent 847b6ed commit cd48372

File tree

1 file changed

+15
-5
lines changed

1 file changed

+15
-5
lines changed

src/node/transaction.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
3434
assert(node.peerman);
3535
assert(node.mempool);
3636
std::promise<void> promise;
37-
uint256 hashTx = tx->GetHash();
37+
uint256 txid = tx->GetHash();
38+
uint256 wtxid = tx->GetWitnessHash();
3839
bool callback_set = false;
3940

4041
{ // cs_main scope
@@ -44,12 +45,21 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
4445
// and return early.
4546
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
4647
for (size_t o = 0; o < tx->vout.size(); o++) {
47-
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
48+
const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));
4849
// IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
4950
// So if the output does exist, then this transaction exists in the chain.
5051
if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
5152
}
52-
if (!node.mempool->exists(hashTx)) {
53+
if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
54+
// There's already a transaction in the mempool with this txid. Don't
55+
// try to submit this transaction to the mempool (since it'll be
56+
// rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
57+
// transaction if relay=true.
58+
//
59+
// The mempool transaction may have the same or different witness (and
60+
// wtxid) as this transaction. Use the mempool's wtxid for reannouncement.
61+
wtxid = mempool_tx->GetWitnessHash();
62+
} else {
5363
// Transaction is not already in the mempool.
5464
if (max_tx_fee > 0) {
5565
// First, call ATMP with test_accept and check the fee. If ATMP
@@ -74,7 +84,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
7484
if (relay) {
7585
// the mempool tracks locally submitted transactions to make a
7686
// best-effort of initial broadcast
77-
node.mempool->AddUnbroadcastTx(hashTx);
87+
node.mempool->AddUnbroadcastTx(txid);
7888
}
7989

8090
if (wait_callback) {
@@ -102,7 +112,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
102112
}
103113

104114
if (relay) {
105-
node.peerman->RelayTransaction(hashTx, tx->GetWitnessHash());
115+
node.peerman->RelayTransaction(txid, wtxid);
106116
}
107117

108118
return TransactionError::OK;

0 commit comments

Comments
 (0)