Skip to content

Commit 5952292

Browse files
committed
wallet, rpc: show mempool conflicts in gettransaction result
1 parent 54e07ee commit 5952292

File tree

3 files changed

+28
-1
lines changed

3 files changed

+28
-1
lines changed

src/wallet/rpc/transactions.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
4040
for (const uint256& conflict : wallet.GetTxConflicts(wtx))
4141
conflicts.push_back(conflict.GetHex());
4242
entry.pushKV("walletconflicts", conflicts);
43+
UniValue mempool_conflicts(UniValue::VARR);
44+
for (const Txid& mempool_conflict : wtx.mempool_conflicts)
45+
mempool_conflicts.push_back(mempool_conflict.GetHex());
46+
entry.pushKV("mempoolconflicts", mempool_conflicts);
4347
entry.pushKV("time", wtx.GetTxTime());
4448
entry.pushKV("timereceived", int64_t{wtx.nTimeReceived});
4549

@@ -417,6 +421,10 @@ static std::vector<RPCResult> TransactionDescriptionString()
417421
}},
418422
{RPCResult::Type::STR_HEX, "replaced_by_txid", /*optional=*/true, "Only if 'category' is 'send'. The txid if this tx was replaced."},
419423
{RPCResult::Type::STR_HEX, "replaces_txid", /*optional=*/true, "Only if 'category' is 'send'. The txid if this tx replaces another."},
424+
{RPCResult::Type::ARR, "mempoolconflicts", "Transactions that directly conflict with either this transaction or an ancestor transaction",
425+
{
426+
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
427+
}},
420428
{RPCResult::Type::STR, "to", /*optional=*/true, "If a comment to is associated with the transaction."},
421429
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
422430
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},

test/functional/wallet_basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ def run_test(self):
679679
"category": baz["category"],
680680
"vout": baz["vout"]}
681681
expected_fields = frozenset({'amount', 'bip125-replaceable', 'confirmations', 'details', 'fee',
682-
'hex', 'lastprocessedblock', 'time', 'timereceived', 'trusted', 'txid', 'wtxid', 'walletconflicts'})
682+
'hex', 'lastprocessedblock', 'time', 'timereceived', 'trusted', 'txid', 'wtxid', 'walletconflicts', 'mempoolconflicts'})
683683
verbose_field = "decoded"
684684
expected_verbose_fields = expected_fields | {verbose_field}
685685

test/functional/wallet_conflicts.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ def test_mempool_conflict(self):
178178
assert_equal(alice.listunspent(), [unspents[0]])
179179
assert_equal(alice.getbalance(), 25)
180180

181+
assert_equal(alice.gettransaction(tx1_txid)["mempoolconflicts"], [tx2_txid])
182+
181183
self.log.info("Test scenario where a mempool conflict is removed")
182184

183185
# broadcast tx3, replaces tx2 in mempool
@@ -187,6 +189,7 @@ def test_mempool_conflict(self):
187189
# tx1 is no longer conflicted.
188190
alice.sendrawtransaction(tx3)
189191

192+
assert_equal(alice.gettransaction(tx1_txid)["mempoolconflicts"], [])
190193
assert tx1_txid not in self.nodes[0].getrawmempool()
191194

192195
# now all of alice's outputs should be considered spent
@@ -262,6 +265,10 @@ def test_mempool_and_block_conflicts(self):
262265
assert tx2_txid in bob.getrawmempool()
263266
assert tx1_conflict_txid in bob.getrawmempool()
264267

268+
assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [tx1_conflict_txid])
269+
assert_equal(bob.gettransaction(tx2_txid)["mempoolconflicts"], [])
270+
assert_equal(bob.gettransaction(tx3_txid)["mempoolconflicts"], [tx1_conflict_txid])
271+
265272
# check that tx3 is now conflicted, so the output from tx2 can now be spent
266273
assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("24.99990000"))
267274

@@ -348,6 +355,8 @@ def test_descendants_with_mempool_conflicts(self):
348355
assert_equal(alice.getbalance(), 25)
349356
assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("24.99990000"))
350357

358+
assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [])
359+
351360
raw_tx = bob.createrawtransaction(inputs=[bob.listunspent(minconf=0)[0]], outputs=[{carol.getnewaddress() : 24.999}])
352361
# Bob creates a child to tx1
353362
tx1_child = bob.signrawtransactionwithwallet(raw_tx)['hex']
@@ -356,6 +365,8 @@ def test_descendants_with_mempool_conflicts(self):
356365
self.sync_mempools()
357366

358367
# Currently neither tx1 nor tx1_child should have any conflicts
368+
assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [])
369+
assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"], [])
359370
assert tx1_txid in bob.getrawmempool()
360371
assert tx1_child_txid in bob.getrawmempool()
361372
assert_equal(len(bob.getrawmempool()), 2)
@@ -378,13 +389,21 @@ def test_descendants_with_mempool_conflicts(self):
378389
assert tx1_conflict_txid in bob.getrawmempool()
379390
assert_equal(len(bob.getrawmempool()), 1)
380391

392+
# Now both tx1 and tx1_child are conflicted by tx1_conflict
393+
assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [tx1_conflict_txid])
394+
assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"], [tx1_conflict_txid])
395+
381396
# Now create a conflict to tx1_conflict, so that it gets kicked out of the mempool
382397
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{carol.getnewaddress() : 24.9895}])
383398
tx1_conflict_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
384399
tx1_conflict_conflict_txid = alice.sendrawtransaction(tx1_conflict_conflict)
385400

386401
self.sync_mempools()
387402

403+
# Now that tx1_conflict has been removed, both tx1 and tx1_child
404+
assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [])
405+
assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"], [])
406+
388407
# Both tx1 and tx1_child are still not in the mempool because they have not be re-broadcasted
389408
assert tx1_txid not in bob.getrawmempool()
390409
assert tx1_child_txid not in bob.getrawmempool()

0 commit comments

Comments
 (0)