Skip to content

Commit 1691eaa

Browse files
committed
[rpc] return effective-feerate in testmempoolaccept and submitpackage
1 parent d6c7b78 commit 1691eaa

File tree

4 files changed

+50
-20
lines changed

4 files changed

+50
-20
lines changed

src/rpc/mempool.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ static RPCHelpMan testmempoolaccept()
126126
{RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
127127
{
128128
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
129+
{RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/false, "the effective feerate in " + CURRENCY_UNIT + " per KvB. May differ from the base feerate if, for example, there are modified fees from prioritisetransaction or a package feerate was used."},
129130
}},
130131
{RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection string (only present when 'allowed' is false)"},
131132
}},
@@ -217,6 +218,7 @@ static RPCHelpMan testmempoolaccept()
217218
result_inner.pushKV("vsize", virtual_size);
218219
UniValue fees(UniValue::VOBJ);
219220
fees.pushKV("base", ValueFromAmount(fee));
221+
fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
220222
result_inner.pushKV("fees", fees);
221223
}
222224
} else {
@@ -768,6 +770,7 @@ static RPCHelpMan submitpackage()
768770
{RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141."},
769771
{RPCResult::Type::OBJ, "fees", "Transaction fees", {
770772
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
773+
{RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/true, "if the transaction was not already in the mempool, the effective feerate in " + CURRENCY_UNIT + " per KvB. For example, the package feerate and/or feerate with modified fees from prioritisetransaction."},
771774
}},
772775
}}
773776
}},
@@ -856,6 +859,7 @@ static RPCHelpMan submitpackage()
856859
CHECK_NONFATAL(it != package_result.m_tx_results.end());
857860
UniValue result_inner{UniValue::VOBJ};
858861
result_inner.pushKV("txid", tx->GetHash().GetHex());
862+
const auto& tx_result = it->second;
859863
if (it->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS) {
860864
result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
861865
}
@@ -864,6 +868,12 @@ static RPCHelpMan submitpackage()
864868
result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
865869
UniValue fees(UniValue::VOBJ);
866870
fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
871+
if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
872+
// Effective feerate is not provided for MEMPOOL_ENTRY transactions even
873+
// though modified fees is known, because it is unknown whether package
874+
// feerate was used when it was originally submitted.
875+
fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
876+
}
867877
result_inner.pushKV("fees", fees);
868878
if (it->second.m_replaced_transactions.has_value()) {
869879
for (const auto& ptx : it->second.m_replaced_transactions.value()) {

test/functional/mempool_accept.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ def check_mempool_result(self, result_expected, *args, **kwargs):
5858
"""Wrapper to check result of testmempoolaccept on node_0's mempool"""
5959
result_test = self.nodes[0].testmempoolaccept(*args, **kwargs)
6060
for r in result_test:
61-
r.pop('wtxid') # Skip check for now
61+
# Skip these checks for now
62+
r.pop('wtxid')
63+
if "fees" in r:
64+
r["fees"].pop("effective-feerate")
6265
assert_equal(result_expected, result_test)
6366
assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state
6467

test/functional/p2p_segwit.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,9 @@ def test_standardness_v0(self):
622622
if not self.segwit_active:
623623
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
624624
# in blocks and the tx is impossible to mine right now.
625-
assert_equal(
626-
self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]),
625+
testres3 = self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()])
626+
testres3[0]["fees"].pop("effective-feerate")
627+
assert_equal(testres3,
627628
[{
628629
'txid': tx3.hash,
629630
'wtxid': tx3.getwtxid(),
@@ -639,8 +640,9 @@ def test_standardness_v0(self):
639640
tx3 = tx
640641
tx3.vout = [tx3_out]
641642
tx3.rehash()
642-
assert_equal(
643-
self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]),
643+
testres3_replaced = self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()])
644+
testres3_replaced[0]["fees"].pop("effective-feerate")
645+
assert_equal(testres3_replaced,
644646
[{
645647
'txid': tx3.hash,
646648
'wtxid': tx3.getwtxid(),

test/functional/rpc_packages.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
assert_raises_rpc_error,
2121
)
2222
from test_framework.wallet import (
23+
COIN,
2324
DEFAULT_FEE,
2425
MiniWallet,
2526
)
@@ -239,11 +240,13 @@ def test_rbf(self):
239240
coin = self.wallet.get_utxo()
240241
fee = Decimal("0.00125000")
241242
replaceable_tx = self.wallet.create_self_transfer(utxo_to_spend=coin, sequence=MAX_BIP125_RBF_SEQUENCE, fee = fee)
242-
testres_replaceable = node.testmempoolaccept([replaceable_tx["hex"]])
243-
assert_equal(testres_replaceable, [
244-
{"txid": replaceable_tx["txid"], "wtxid": replaceable_tx["wtxid"],
245-
"allowed": True, "vsize": replaceable_tx["tx"].get_vsize(), "fees": { "base": fee }}
246-
])
243+
testres_replaceable = node.testmempoolaccept([replaceable_tx["hex"]])[0]
244+
assert_equal(testres_replaceable["txid"], replaceable_tx["txid"])
245+
assert_equal(testres_replaceable["wtxid"], replaceable_tx["wtxid"])
246+
assert testres_replaceable["allowed"]
247+
assert_equal(testres_replaceable["vsize"], replaceable_tx["tx"].get_vsize())
248+
assert_equal(testres_replaceable["fees"]["base"], fee)
249+
assert_fee_amount(fee, replaceable_tx["tx"].get_vsize(), testres_replaceable["fees"]["effective-feerate"])
247250

248251
# Replacement transaction is identical except has double the fee
249252
replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=coin, sequence=MAX_BIP125_RBF_SEQUENCE, fee = 2 * fee)
@@ -287,11 +290,13 @@ def test_submit_child_with_parents(self, num_parents, partial_submit):
287290
peer = node.add_p2p_connection(P2PTxInvStore())
288291

289292
package_txns = []
293+
presubmitted_wtxids = set()
290294
for _ in range(num_parents):
291295
parent_tx = self.wallet.create_self_transfer(fee=DEFAULT_FEE)
292296
package_txns.append(parent_tx)
293297
if partial_submit and random.choice([True, False]):
294298
node.sendrawtransaction(parent_tx["hex"])
299+
presubmitted_wtxids.add(parent_tx["wtxid"])
295300
child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=[tx["new_utxo"] for tx in package_txns], fee_per_output=10000) #DEFAULT_FEE
296301
package_txns.append(child_tx)
297302

@@ -302,14 +307,14 @@ def test_submit_child_with_parents(self, num_parents, partial_submit):
302307
for package_txn in package_txns:
303308
tx = package_txn["tx"]
304309
assert tx.getwtxid() in submitpackage_result["tx-results"]
305-
tx_result = submitpackage_result["tx-results"][tx.getwtxid()]
306-
assert_equal(tx_result, {
307-
"txid": package_txn["txid"],
308-
"vsize": tx.get_vsize(),
309-
"fees": {
310-
"base": DEFAULT_FEE,
311-
}
312-
})
310+
wtxid = tx.getwtxid()
311+
assert wtxid in submitpackage_result["tx-results"]
312+
tx_result = submitpackage_result["tx-results"][wtxid]
313+
assert_equal(tx_result["txid"], tx.rehash())
314+
assert_equal(tx_result["vsize"], tx.get_vsize())
315+
assert_equal(tx_result["fees"]["base"], DEFAULT_FEE)
316+
if wtxid not in presubmitted_wtxids:
317+
assert_fee_amount(DEFAULT_FEE, tx.get_vsize(), tx_result["fees"]["effective-feerate"])
313318

314319
# submitpackage result should be consistent with testmempoolaccept and getmempoolentry
315320
self.assert_equal_package_results(node, testmempoolaccept_result, submitpackage_result)
@@ -328,8 +333,11 @@ def test_submit_cpfp(self):
328333
node = self.nodes[0]
329334
peer = node.add_p2p_connection(P2PTxInvStore())
330335

336+
# Package with 2 parents and 1 child. One parent pays for itself using modified fees, and
337+
# another has 0 fees but is bumped by child.
331338
tx_poor = self.wallet.create_self_transfer(fee=0, fee_rate=0)
332-
tx_rich = self.wallet.create_self_transfer(fee=DEFAULT_FEE)
339+
tx_rich = self.wallet.create_self_transfer(fee=0, fee_rate=0)
340+
node.prioritisetransaction(tx_rich["txid"], 0, int(DEFAULT_FEE * COIN))
333341
package_txns = [tx_rich, tx_poor]
334342
coins = [tx["new_utxo"] for tx in package_txns]
335343
tx_child = self.wallet.create_self_transfer_multi(utxos_to_spend=coins, fee_per_output=10000) #DEFAULT_FEE
@@ -340,9 +348,16 @@ def test_submit_cpfp(self):
340348
rich_parent_result = submitpackage_result["tx-results"][tx_rich["wtxid"]]
341349
poor_parent_result = submitpackage_result["tx-results"][tx_poor["wtxid"]]
342350
child_result = submitpackage_result["tx-results"][tx_child["tx"].getwtxid()]
343-
assert_equal(rich_parent_result["fees"]["base"], DEFAULT_FEE)
351+
assert_equal(rich_parent_result["fees"]["base"], 0)
344352
assert_equal(poor_parent_result["fees"]["base"], 0)
345353
assert_equal(child_result["fees"]["base"], DEFAULT_FEE)
354+
# The "rich" parent does not require CPFP so its effective feerate.
355+
assert_fee_amount(DEFAULT_FEE, tx_rich["tx"].get_vsize(), rich_parent_result["fees"]["effective-feerate"])
356+
# The "poor" parent and child's effective feerates are the same, composed of the child's fee
357+
# divided by their combined vsize.
358+
assert_fee_amount(DEFAULT_FEE, tx_poor["tx"].get_vsize() + tx_child["tx"].get_vsize(), poor_parent_result["fees"]["effective-feerate"])
359+
assert_fee_amount(DEFAULT_FEE, tx_poor["tx"].get_vsize() + tx_child["tx"].get_vsize(), child_result["fees"]["effective-feerate"])
360+
346361
# Package feerate is calculated for the remaining transactions after deduplication and
347362
# individual submission. Since this package had a 0-fee parent, package feerate must have
348363
# been used and returned.

0 commit comments

Comments
 (0)