aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorW. J. van der Laan <laanwj@protonmail.com>2021-09-20 19:19:10 +0200
committerW. J. van der Laan <laanwj@protonmail.com>2021-09-20 19:25:43 +0200
commit488e7455608dd075c017c1c9bb6ed34b1706e45e (patch)
tree7214a6f39d10f06fba43f8f109b71658c118ec47
parentd809d8bf12647f44805f1b45aa6267a96807e913 (diff)
parent6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 (diff)
downloadbitcoin-488e7455608dd075c017c1c9bb6ed34b1706e45e.tar.xz
Merge bitcoin/bitcoin#12677: RPC: Add ancestor{count,size,fees} to listunspent output
6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 doc/release-notes: Add new listunspent fields (Luke Dashjr) 0be2f17ef5649c2d77efbbbdd9222332b2ebf0d2 QA: Add tests for listunspent ancestor{count,size,fees} to mempool_packages (Luke Dashjr) 6966e80f453c46d5d0a923118205f19ac2f4e336 RPC: Add ancestor{count,size,fees} to listunspent output (Luke Dashjr) 3f77dfdaf0f0bfe0c4662a616d6943f31bdd5bf4 Expose ancestorsize and ancestorfees via getTransactionAncestry (Luke Dashjr) Pull request description: Requested by a user ACKs for top commit: prayank23: reACK https://github.com/bitcoin/bitcoin/commit/6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 fjahr: Code review re-ACK 6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 kiminuo: ACK [6cb60f3](https://github.com/bitcoin/bitcoin/commit/6cb60f3e6d652ffa4cf570426a7cf1f690d15c45) achow101: Code Review ACK 6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 naumenkogs: ACK 6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 darosior: utACK 6cb60f3e6d652ffa4cf570426a7cf1f690d15c45 Tree-SHA512: 5d16e5799558691e5853ab7ea2cc85514cb45da3ce69134d855c71845beef32ec6af5ab28d4462683e9800c8ea126f162773a9d3d5660edac08fd8edbfeda173
-rw-r--r--doc/release-notes-12677.md8
-rw-r--r--src/interfaces/chain.h2
-rw-r--r--src/node/interfaces.cpp4
-rw-r--r--src/txmempool.cpp4
-rw-r--r--src/txmempool.h4
-rw-r--r--src/wallet/rpcwallet.cpp13
-rwxr-xr-xtest/functional/mempool_packages.py20
7 files changed, 47 insertions, 8 deletions
diff --git a/doc/release-notes-12677.md b/doc/release-notes-12677.md
new file mode 100644
index 0000000000..d6fea9eae7
--- /dev/null
+++ b/doc/release-notes-12677.md
@@ -0,0 +1,8 @@
+Notable changes
+===============
+
+Updated RPCs
+------------
+
+- `listunspent` now includes `ancestorcount`, `ancestorsize`, and
+`ancestorfees` for each transaction output that is still in the mempool.
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index eceede3c8f..9a97cad1f8 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -177,7 +177,7 @@ public:
std::string& err_string) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
- virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
+ virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;
//! Get the node's package limits.
//! Currently only returns the ancestor and descendant count limits, but could be enhanced to
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index c62d7e5d0b..d7860f0115 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -575,11 +575,11 @@ public:
// that Chain clients do not need to know about.
return TransactionError::OK == err;
}
- void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
+ void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize, CAmount* ancestorfees) override
{
ancestors = descendants = 0;
if (!m_node.mempool) return;
- m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants);
+ m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants, ancestorsize, ancestorfees);
}
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
{
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index d5a888ac67..043564294f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -1174,12 +1174,14 @@ uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
return maximum;
}
-void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const {
+void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* const ancestorsize, CAmount* const ancestorfees) const {
LOCK(cs);
auto it = mapTx.find(txid);
ancestors = descendants = 0;
if (it != mapTx.end()) {
ancestors = it->GetCountWithAncestors();
+ if (ancestorsize) *ancestorsize = it->GetSizeWithAncestors();
+ if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors();
descendants = CalculateDescendantMaximum(it);
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 0a84a6e6b1..d1308aeeed 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -748,8 +748,10 @@ public:
/**
* Calculate the ancestor and descendant count for the given transaction.
* The counts include the transaction itself.
+ * When ancestors is non-zero (ie, the transaction itself is in the mempool),
+ * ancestorsize and ancestorfees will also be set to the appropriate values.
*/
- void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
+ void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) const;
/** @returns true if the mempool is fully loaded */
bool IsLoaded() const;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index e922f4ede9..7d194ae262 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2960,6 +2960,9 @@ static RPCHelpMan listunspent()
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
+ {RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
+ {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)"},
{RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
{RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
@@ -3126,6 +3129,16 @@ static RPCHelpMan listunspent()
entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
entry.pushKV("confirmations", out.nDepth);
+ if (!out.nDepth) {
+ size_t ancestor_count, descendant_count, ancestor_size;
+ CAmount ancestor_fees;
+ pwallet->chain().getTransactionAncestry(out.tx->GetHash(), ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
+ if (ancestor_count) {
+ entry.pushKV("ancestorcount", uint64_t(ancestor_count));
+ entry.pushKV("ancestorsize", uint64_t(ancestor_size));
+ entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
+ }
+ }
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index b9344ad6da..c042961937 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -51,12 +51,17 @@ class MempoolPackagesTest(BitcoinTestFramework):
txid = utxo[0]['txid']
vout = utxo[0]['vout']
value = utxo[0]['amount']
+ assert 'ancestorcount' not in utxo[0]
+ assert 'ancestorsize' not in utxo[0]
+ assert 'ancestorfees' not in utxo[0]
fee = Decimal("0.0001")
# MAX_ANCESTORS transactions off a confirmed tx should be fine
chain = []
witness_chain = []
- for _ in range(MAX_ANCESTORS):
+ ancestor_vsize = 0
+ ancestor_fees = Decimal(0)
+ for i in range(MAX_ANCESTORS):
(txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
value = sent_value
chain.append(txid)
@@ -65,6 +70,15 @@ class MempoolPackagesTest(BitcoinTestFramework):
witnesstx = self.nodes[0].decoderawtransaction(fulltx, True)
witness_chain.append(witnesstx['hash'])
+ # Check that listunspent ancestor{count, size, fees} yield the correct results
+ wallet_unspent = self.nodes[0].listunspent(minconf=0)
+ this_unspent = next(utxo_info for utxo_info in wallet_unspent if utxo_info['txid'] == txid)
+ assert_equal(this_unspent['ancestorcount'], i + 1)
+ ancestor_vsize += self.nodes[0].getrawtransaction(txid=txid, verbose=True)['vsize']
+ assert_equal(this_unspent['ancestorsize'], ancestor_vsize)
+ ancestor_fees -= self.nodes[0].gettransaction(txid=txid)['fee']
+ assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN)
+
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
peer_inv_store.wait_for_broadcast(witness_chain)
@@ -77,9 +91,9 @@ class MempoolPackagesTest(BitcoinTestFramework):
descendant_fees = 0
descendant_vsize = 0
- ancestor_vsize = sum([mempool[tx]['vsize'] for tx in mempool])
+ assert_equal(ancestor_vsize, sum([mempool[tx]['vsize'] for tx in mempool]))
ancestor_count = MAX_ANCESTORS
- ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])
+ assert_equal(ancestor_fees, sum([mempool[tx]['fee'] for tx in mempool]))
descendants = []
ancestors = list(chain)