Skip to content

Commit 488e745

Browse files
committed
Merge bitcoin/bitcoin#12677: RPC: Add ancestor{count,size,fees} to listunspent output
6cb60f3 doc/release-notes: Add new listunspent fields (Luke Dashjr) 0be2f17 QA: Add tests for listunspent ancestor{count,size,fees} to mempool_packages (Luke Dashjr) 6966e80 RPC: Add ancestor{count,size,fees} to listunspent output (Luke Dashjr) 3f77dfd Expose ancestorsize and ancestorfees via getTransactionAncestry (Luke Dashjr) Pull request description: Requested by a user ACKs for top commit: prayank23: reACK bitcoin/bitcoin@6cb60f3 fjahr: Code review re-ACK 6cb60f3 kiminuo: ACK [6cb60f3](bitcoin/bitcoin@6cb60f3) achow101: Code Review ACK 6cb60f3 naumenkogs: ACK 6cb60f3 darosior: utACK 6cb60f3 Tree-SHA512: 5d16e5799558691e5853ab7ea2cc85514cb45da3ce69134d855c71845beef32ec6af5ab28d4462683e9800c8ea126f162773a9d3d5660edac08fd8edbfeda173
2 parents d809d8b + 6cb60f3 commit 488e745

File tree

7 files changed

+47
-8
lines changed

7 files changed

+47
-8
lines changed

doc/release-notes-12677.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Notable changes
2+
===============
3+
4+
Updated RPCs
5+
------------
6+
7+
- `listunspent` now includes `ancestorcount`, `ancestorsize`, and
8+
`ancestorfees` for each transaction output that is still in the mempool.

src/interfaces/chain.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class Chain
177177
std::string& err_string) = 0;
178178

179179
//! Calculate mempool ancestor and descendant counts for the given transaction.
180-
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
180+
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;
181181

182182
//! Get the node's package limits.
183183
//! Currently only returns the ancestor and descendant count limits, but could be enhanced to

src/node/interfaces.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,11 +575,11 @@ class ChainImpl : public Chain
575575
// that Chain clients do not need to know about.
576576
return TransactionError::OK == err;
577577
}
578-
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
578+
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize, CAmount* ancestorfees) override
579579
{
580580
ancestors = descendants = 0;
581581
if (!m_node.mempool) return;
582-
m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants);
582+
m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants, ancestorsize, ancestorfees);
583583
}
584584
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
585585
{

src/txmempool.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1174,12 +1174,14 @@ uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
11741174
return maximum;
11751175
}
11761176

1177-
void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const {
1177+
void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* const ancestorsize, CAmount* const ancestorfees) const {
11781178
LOCK(cs);
11791179
auto it = mapTx.find(txid);
11801180
ancestors = descendants = 0;
11811181
if (it != mapTx.end()) {
11821182
ancestors = it->GetCountWithAncestors();
1183+
if (ancestorsize) *ancestorsize = it->GetSizeWithAncestors();
1184+
if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors();
11831185
descendants = CalculateDescendantMaximum(it);
11841186
}
11851187
}

src/txmempool.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,8 +748,10 @@ class CTxMemPool
748748
/**
749749
* Calculate the ancestor and descendant count for the given transaction.
750750
* The counts include the transaction itself.
751+
* When ancestors is non-zero (ie, the transaction itself is in the mempool),
752+
* ancestorsize and ancestorfees will also be set to the appropriate values.
751753
*/
752-
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
754+
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) const;
753755

754756
/** @returns true if the mempool is fully loaded */
755757
bool IsLoaded() const;

src/wallet/rpcwallet.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2960,6 +2960,9 @@ static RPCHelpMan listunspent()
29602960
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
29612961
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
29622962
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
2963+
{RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
2964+
{RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
2965+
{RPCResult::Type::STR_AMOUNT, "ancestorfees", /* optional */ true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
29632966
{RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
29642967
{RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
29652968
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
@@ -3126,6 +3129,16 @@ static RPCHelpMan listunspent()
31263129
entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
31273130
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
31283131
entry.pushKV("confirmations", out.nDepth);
3132+
if (!out.nDepth) {
3133+
size_t ancestor_count, descendant_count, ancestor_size;
3134+
CAmount ancestor_fees;
3135+
pwallet->chain().getTransactionAncestry(out.tx->GetHash(), ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
3136+
if (ancestor_count) {
3137+
entry.pushKV("ancestorcount", uint64_t(ancestor_count));
3138+
entry.pushKV("ancestorsize", uint64_t(ancestor_size));
3139+
entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
3140+
}
3141+
}
31293142
entry.pushKV("spendable", out.fSpendable);
31303143
entry.pushKV("solvable", out.fSolvable);
31313144
if (out.fSolvable) {

test/functional/mempool_packages.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,17 @@ def run_test(self):
5151
txid = utxo[0]['txid']
5252
vout = utxo[0]['vout']
5353
value = utxo[0]['amount']
54+
assert 'ancestorcount' not in utxo[0]
55+
assert 'ancestorsize' not in utxo[0]
56+
assert 'ancestorfees' not in utxo[0]
5457

5558
fee = Decimal("0.0001")
5659
# MAX_ANCESTORS transactions off a confirmed tx should be fine
5760
chain = []
5861
witness_chain = []
59-
for _ in range(MAX_ANCESTORS):
62+
ancestor_vsize = 0
63+
ancestor_fees = Decimal(0)
64+
for i in range(MAX_ANCESTORS):
6065
(txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
6166
value = sent_value
6267
chain.append(txid)
@@ -65,6 +70,15 @@ def run_test(self):
6570
witnesstx = self.nodes[0].decoderawtransaction(fulltx, True)
6671
witness_chain.append(witnesstx['hash'])
6772

73+
# Check that listunspent ancestor{count, size, fees} yield the correct results
74+
wallet_unspent = self.nodes[0].listunspent(minconf=0)
75+
this_unspent = next(utxo_info for utxo_info in wallet_unspent if utxo_info['txid'] == txid)
76+
assert_equal(this_unspent['ancestorcount'], i + 1)
77+
ancestor_vsize += self.nodes[0].getrawtransaction(txid=txid, verbose=True)['vsize']
78+
assert_equal(this_unspent['ancestorsize'], ancestor_vsize)
79+
ancestor_fees -= self.nodes[0].gettransaction(txid=txid)['fee']
80+
assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN)
81+
6882
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
6983
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
7084
peer_inv_store.wait_for_broadcast(witness_chain)
@@ -77,9 +91,9 @@ def run_test(self):
7791
descendant_fees = 0
7892
descendant_vsize = 0
7993

80-
ancestor_vsize = sum([mempool[tx]['vsize'] for tx in mempool])
94+
assert_equal(ancestor_vsize, sum([mempool[tx]['vsize'] for tx in mempool]))
8195
ancestor_count = MAX_ANCESTORS
82-
ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])
96+
assert_equal(ancestor_fees, sum([mempool[tx]['fee'] for tx in mempool]))
8397

8498
descendants = []
8599
ancestors = list(chain)

0 commit comments

Comments
 (0)