RPC/blockchain: getmempoolinfo: Return fee_histogram in older format (only)

This commit is contained in:
Luke Dashjr 2020-04-02 15:10:19 +00:00
parent 521f921d83
commit 68f7f13ad0
2 changed files with 67 additions and 72 deletions

View File

@ -719,26 +719,17 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool, const std::optional<MempoolHi
// Track total amount of available fees in fee rate groups // Track total amount of available fees in fee rate groups
CAmount total_fees = 0; CAmount total_fees = 0;
UniValue groups(UniValue::VOBJ); UniValue info(UniValue::VOBJ);
for (size_t i = 0; i < floors.size(); ++i) { for (size_t i = 0; i < floors.size(); ++i) {
UniValue info_sub(UniValue::VOBJ); UniValue info_sub(UniValue::VOBJ);
info_sub.pushKV("size", sizes.at(i)); info_sub.pushKV("sizes", sizes[i]);
info_sub.pushKV("count", count.at(i)); info_sub.pushKV("count", count.at(i));
info_sub.pushKV("fees", fees.at(i)); info_sub.pushKV("fees", fees.at(i));
info_sub.pushKV("from", floors.at(i)); info_sub.pushKV("from_feerate", floors[i]);
info_sub.pushKV("to_feerate", i == floors.size() - 1 ? std::numeric_limits<int64_t>::max() : floors[i + 1]);
if (i == floors.size() - 1) {
info_sub.pushKV("to", NullUniValue);
} else {
info_sub.pushKV("to", floors[i + 1] - 1);
}
total_fees += fees.at(i); total_fees += fees.at(i);
groups.pushKV(ToString(floors.at(i)), info_sub); info.pushKV(ToString(floors[i]), info_sub);
} }
UniValue info(UniValue::VOBJ);
info.pushKV("fee_rate_groups", groups);
info.pushKV("total_fees", total_fees); info.pushKV("total_fees", total_fees);
ret.pushKV("fee_histogram", info); ret.pushKV("fee_histogram", info);
} }
@ -773,18 +764,17 @@ static RPCHelpMan getmempoolinfo()
{RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection"}, {RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection"},
{RPCResult::Type::OBJ, "fee_histogram", /*optional=*/true, "", {RPCResult::Type::OBJ, "fee_histogram", /*optional=*/true, "",
{ {
{RPCResult::Type::OBJ_DYN, "fee_rate_groups", "", {RPCResult::Type::OBJ, "<fee_rate_group>", "Fee rate group named by its lower bound (in " + CURRENCY_ATOM + "/vB), identical to the \"from_feerate\" field below",
{ {
{RPCResult::Type::OBJ, "<fee_rate_group>", "Fee rate group named by its lower bound (in " + CURRENCY_ATOM + "/vB), identical to the \"from\" field below", {RPCResult::Type::NUM, "sizes", "Cumulative size of all transactions in the fee rate group (in vBytes)"},
{
{RPCResult::Type::NUM, "size", "Cumulative size of all transactions in the fee rate group (in vBytes)"},
{RPCResult::Type::NUM, "count", "Number of transactions in the fee rate group"}, {RPCResult::Type::NUM, "count", "Number of transactions in the fee rate group"},
{RPCResult::Type::NUM, "fees", "Cumulative fees of all transactions in the fee rate group (in " + CURRENCY_ATOM + ")"}, {RPCResult::Type::NUM, "fees", "Cumulative fees of all transactions in the fee rate group (in " + CURRENCY_ATOM + ")"},
{RPCResult::Type::NUM, "from", "Group contains transactions with fee rates equal or greater than this value (in " + CURRENCY_ATOM + "/vB)"}, {RPCResult::Type::NUM, "from_feerate", "Group contains transactions with fee rates equal or greater than this value (in " + CURRENCY_ATOM + "/vB)"},
{RPCResult::Type::ANY, "to", "Group contains transactions with fee rates equal or less than this value (in " + CURRENCY_ATOM + "/vB)"}, {RPCResult::Type::ANY, "to_feerate", "Group contains transactions with fee rates equal or less than this value (in " + CURRENCY_ATOM + "/vB)"},
}}}},
{RPCResult::Type::NUM, "total_fees", "Total available fees in mempool (in " + CURRENCY_ATOM + ")"},
}}, }},
{RPCResult::Type::ELISION, "", ""},
{RPCResult::Type::NUM, "total_fees", "Total available fees in mempool (in " + CURRENCY_ATOM + ")"},
}, /*skip_type_check=*/ true},
}}, }},
RPCExamples{ RPCExamples{
HelpExampleCli("getmempoolinfo", "") + HelpExampleCli("getmempoolinfo", "") +

View File

@ -61,10 +61,10 @@ class MempoolFeeHistogramTest(BitcoinTestFramework):
assert_equal(0, total_fees) assert_equal(0, total_fees)
for i in ['1', '2', '3']: for i in ['1', '2', '3']:
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['size']) assert_equal(0, info['fee_histogram'][i]['sizes'])
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['count']) assert_equal(0, info['fee_histogram'][i]['count'])
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['fees']) assert_equal(0, info['fee_histogram'][i]['fees'])
assert_equal(int(i), info['fee_histogram']['fee_rate_groups'][i]['from']) assert_equal(int(i), info['fee_histogram'][i]['from_feerate'])
self.log.info("Test that we have two spendable UTXOs and lock the second one") self.log.info("Test that we have two spendable UTXOs and lock the second one")
utxos = node.listunspent() utxos = node.listunspent()
@ -80,28 +80,28 @@ class MempoolFeeHistogramTest(BitcoinTestFramework):
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram']) (non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
assert_equal(1, non_empty_groups) assert_equal(1, non_empty_groups)
assert_equal(3, empty_groups) assert_equal(3, empty_groups)
assert_equal(1, info['fee_histogram']['fee_rate_groups'][tx1_info['feerate']]['count']) assert_equal(1, info['fee_histogram'][tx1_info['feerate']]['count'])
assert_equal(total_fees, info['fee_histogram']['total_fees']) assert_equal(total_fees, info['fee_histogram']['total_fees'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['size']) assert_equal(0, info['fee_histogram']['1']['sizes'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['count']) assert_equal(0, info['fee_histogram']['1']['count'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['fees']) assert_equal(0, info['fee_histogram']['1']['fees'])
assert_equal(1, info['fee_histogram']['fee_rate_groups']['1']['from']) assert_equal(1, info['fee_histogram']['1']['from_feerate'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['3']['size']) assert_equal(0, info['fee_histogram']['3']['sizes'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['3']['count']) assert_equal(0, info['fee_histogram']['3']['count'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['3']['fees']) assert_equal(0, info['fee_histogram']['3']['fees'])
assert_equal(3, info['fee_histogram']['fee_rate_groups']['3']['from']) assert_equal(3, info['fee_histogram']['3']['from_feerate'])
assert_equal(188, info['fee_histogram']['fee_rate_groups']['5']['size']) assert_equal(188, info['fee_histogram']['5']['sizes'])
assert_equal(1, info['fee_histogram']['fee_rate_groups']['5']['count']) assert_equal(1, info['fee_histogram']['5']['count'])
assert_equal(940, info['fee_histogram']['fee_rate_groups']['5']['fees']) assert_equal(940, info['fee_histogram']['5']['fees'])
assert_equal(5, info['fee_histogram']['fee_rate_groups']['5']['from']) assert_equal(5, info['fee_histogram']['5']['from_feerate'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['10']['size']) assert_equal(0, info['fee_histogram']['10']['sizes'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['10']['count']) assert_equal(0, info['fee_histogram']['10']['count'])
assert_equal(0, info['fee_histogram']['fee_rate_groups']['10']['fees']) assert_equal(0, info['fee_histogram']['10']['fees'])
assert_equal(10, info['fee_histogram']['fee_rate_groups']['10']['from']) assert_equal(10, info['fee_histogram']['10']['from_feerate'])
self.log.info("Send tx2 transaction with 14 sat/vB fee rate (spends tx1 UTXO)") self.log.info("Send tx2 transaction with 14 sat/vB fee rate (spends tx1 UTXO)")
tx2_txid = node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("25.0"), fee_rate=14, subtractfeefromamount=True) tx2_txid = node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("25.0"), fee_rate=14, subtractfeefromamount=True)
@ -115,7 +115,7 @@ class MempoolFeeHistogramTest(BitcoinTestFramework):
tx1p2_feerate = get_actual_fee_rate(tx1_info['fee'] + tx2_info['fee'], tx1_info['vsize'] + tx2_info['vsize']) tx1p2_feerate = get_actual_fee_rate(tx1_info['fee'] + tx2_info['fee'], tx1_info['vsize'] + tx2_info['vsize'])
assert_equal(1, non_empty_groups) assert_equal(1, non_empty_groups)
assert_equal(14, empty_groups) assert_equal(14, empty_groups)
assert_equal(2, info['fee_histogram']['fee_rate_groups'][tx1p2_feerate]['count']) assert_equal(2, info['fee_histogram'][tx1p2_feerate]['count'])
assert_equal(total_fees, info['fee_histogram']['total_fees']) assert_equal(total_fees, info['fee_histogram']['total_fees'])
# Unlock the second UTXO which we locked # Unlock the second UTXO which we locked
@ -132,26 +132,27 @@ class MempoolFeeHistogramTest(BitcoinTestFramework):
# tx1 should be grouped with tx2 + tx3 (descendants) # tx1 should be grouped with tx2 + tx3 (descendants)
# tx2 should be grouped with tx1 (ancestors only) # tx2 should be grouped with tx1 (ancestors only)
# tx3 should be alone # tx3 should be alone
expected_histogram = { expected_histogram = dict(
'fee_rate_groups': dict( ( tuple(
(str(n), { (str(n), {
'from': n, 'from_feerate': n,
'to': n, 'to_feerate': n + 1,
'count': 0, 'count': 0,
'fees': 0, 'fees': 0,
'size': 0, 'sizes': 0,
}) for n in range(1, 16) }) for n in range(1, 16)
) ), ) + (
'total_fees': tx1_info['fee'] + tx2_info['fee'] + tx3_info['fee'], ('total_fees', tx1_info['fee'] + tx2_info['fee'] + tx3_info['fee']),
} )
expected_frg = expected_histogram['fee_rate_groups'] )
expected_frg['15']['to'] = None expected_frg = expected_histogram
expected_frg['15']['to_feerate'] = 9223372036854775807
tx1p2p3_feerate = get_actual_fee_rate(expected_histogram['total_fees'], tx1_info['vsize'] + tx2_info['vsize'] + tx3_info['vsize']) tx1p2p3_feerate = get_actual_fee_rate(expected_histogram['total_fees'], tx1_info['vsize'] + tx2_info['vsize'] + tx3_info['vsize'])
def inc_expected(feerate, txinfo): def inc_expected(feerate, txinfo):
this_frg = expected_frg[feerate] this_frg = expected_frg[feerate]
this_frg['count'] += 1 this_frg['count'] += 1
this_frg['fees'] += txinfo['fee'] this_frg['fees'] += txinfo['fee']
this_frg['size'] += txinfo['vsize'] this_frg['sizes'] += txinfo['vsize']
inc_expected(tx1p2p3_feerate, tx1_info) inc_expected(tx1p2p3_feerate, tx1_info)
inc_expected(tx1p2_feerate, tx2_info) inc_expected(tx1p2_feerate, tx2_info)
inc_expected(tx3_info['feerate'], tx3_info) inc_expected(tx3_info['feerate'], tx3_info)
@ -163,26 +164,23 @@ class MempoolFeeHistogramTest(BitcoinTestFramework):
# Verify that the 6 sat/vB fee rate group has one transaction, and the 8-9 sat/vB fee rate group has two # Verify that the 6 sat/vB fee rate group has one transaction, and the 8-9 sat/vB fee rate group has two
for collapse_n in (9, 11, 13, 15): for collapse_n in (9, 11, 13, 15):
for field in ('count', 'size', 'fees'): for field in ('count', 'sizes', 'fees'):
expected_frg[str(collapse_n - 1)][field] += expected_frg[str(collapse_n)][field] expected_frg[str(collapse_n - 1)][field] += expected_frg[str(collapse_n)][field]
expected_frg[str(collapse_n - 1)]['to'] += 1 expected_frg[str(collapse_n - 1)]['to_feerate'] += 1
del expected_frg[str(collapse_n)] del expected_frg[str(collapse_n)]
expected_frg['14']['to'] += 1 # 16 is also skipped expected_frg['14']['to_feerate'] += 1 # 16 is also skipped
for new_n in (17, 20, 25) + tuple(range(30, 90, 10)) + (100, 120, 140, 170, 200, 250) + tuple(range(300, 900, 100)) + (1000, 1200, 1400, 1700, 2000, 2500) + tuple(range(3000, 9000, 1000)) + (10000,): for new_n in (17, 20, 25) + tuple(range(30, 90, 10)) + (100, 120, 140, 170, 200, 250) + tuple(range(300, 900, 100)) + (1000, 1200, 1400, 1700, 2000, 2500) + tuple(range(3000, 9000, 1000)) + (10000,):
frinfo = info['fee_histogram']['fee_rate_groups'][str(new_n)] frinfo = info['fee_histogram'][str(new_n)]
if new_n == 10000: assert frinfo['to_feerate'] > frinfo['from_feerate']
assert frinfo['to'] is None del frinfo['to_feerate']
else:
assert frinfo['to'] > frinfo['from']
del frinfo['to']
assert_equal(frinfo, { assert_equal(frinfo, {
'from': new_n, 'from_feerate': new_n,
'count': 0, 'count': 0,
'fees': 0, 'fees': 0,
'size': 0, 'sizes': 0,
}) })
del info['fee_histogram']['fee_rate_groups'][str(new_n)] del info['fee_histogram'][str(new_n)]
assert_equal(expected_histogram, info['fee_histogram']) assert_equal(expected_histogram, info['fee_histogram'])
self.log.info("Test getmempoolinfo(with_fee_histogram=False) does not return fee histogram") self.log.info("Test getmempoolinfo(with_fee_histogram=False) does not return fee histogram")
@ -193,16 +191,23 @@ class MempoolFeeHistogramTest(BitcoinTestFramework):
empty_count = 0 empty_count = 0
non_empty_count = 0 non_empty_count = 0
for key, bin in histogram['fee_rate_groups'].items(): for key, bin in histogram.items():
assert_equal(int(key), bin['from']) if key == 'total_fees':
continue
assert_equal(int(key), bin['from_feerate'])
if bin['fees'] > 0: if bin['fees'] > 0:
assert_greater_than(bin['count'], 0) assert_greater_than(bin['count'], 0)
else: else:
assert_equal(bin['count'], 0) assert_equal(bin['count'], 0)
assert_greater_than_or_equal(bin['fees'], 0) assert_greater_than_or_equal(bin['fees'], 0)
assert_greater_than_or_equal(bin['size'], 0) assert_greater_than_or_equal(bin['sizes'], 0)
if bin['to'] is not None: if bin['to_feerate'] is not None:
assert_greater_than_or_equal(bin['to'], bin['from']) assert_greater_than_or_equal(bin['to_feerate'], bin['from_feerate'])
for next_key in sorted((*(int(a) for a in histogram.keys() if a != 'total_fees'), 0x7fffffffffffffff)):
if int(next_key) <= int(key):
continue
assert_equal(bin['to_feerate'], int(next_key))
break
total_fees += bin['fees'] total_fees += bin['fees']
if bin['count'] == 0: if bin['count'] == 0: