Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2023 05 mempool vsize #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/rpc/mempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ static RPCHelpMan testmempoolaccept()
{RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
{RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
"If not present, the tx was not fully validated due to a failure in another tx in the list."},
{RPCResult::Type::NUM, "vsize", /*optional=*/true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
{RPCResult::Type::NUM, "vsize", /*optional=*/true, "Maximum of sigop-adjusted size (-bytespersigop) and virtual transaction size as defined in BIP 141."},
{RPCResult::Type::NUM, "vsize_bip141", /*optional=*/true, "Virtual transaction size as defined in BIP 141.\n"
"This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)."},
{RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
{
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
Expand Down Expand Up @@ -223,6 +225,7 @@ static RPCHelpMan testmempoolaccept()
// These can be used to calculate the feerate.
result_inner.pushKV("allowed", true);
result_inner.pushKV("vsize", virtual_size);
result_inner.pushKV("vsize_bip141", GetVirtualTransactionSize(*tx, 0, 0));
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(fee));
fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
Expand Down Expand Up @@ -252,7 +255,8 @@ static RPCHelpMan testmempoolaccept()
static std::vector<RPCResult> MempoolEntryDescription()
{
return {
RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
RPCResult{RPCResult::Type::NUM, "vsize", "maximum of sigop-adjusted size (-bytespersigop) and virtual transaction size as defined in BIP 141."},
RPCResult{RPCResult::Type::NUM, "vsize_bip141", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
Expand Down Expand Up @@ -282,6 +286,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
AssertLockHeld(pool.cs);

info.pushKV("vsize", (int)e.GetTxSize());
info.pushKV("vsize_bip141", GetVirtualTransactionSize(e.GetTx(), 0, 0));
info.pushKV("weight", (int)e.GetTxWeight());
info.pushKV("time", count_seconds(e.GetTime()));
info.pushKV("height", (int)e.GetHeight());
Expand Down Expand Up @@ -840,7 +845,8 @@ static RPCHelpMan submitpackage()
{RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::STR_HEX, "other-wtxid", /*optional=*/true, "The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored."},
{RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141."},
{RPCResult::Type::NUM, "vsize", "Maximum of sigop-adjusted size (-bytespersigop) and virtual transaction size as defined in BIP 141."},
{RPCResult::Type::NUM, "vsize_bip141", "Virtual transaction size as defined in BIP 141."},
{RPCResult::Type::OBJ, "fees", "Transaction fees", {
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
{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."},
Expand Down Expand Up @@ -939,6 +945,7 @@ static RPCHelpMan submitpackage()
if (it->second.m_result_type == MempoolAcceptResult::ResultType::VALID ||
it->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY) {
result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
result_inner.pushKV("vsize_bip141", GetVirtualTransactionSize(*tx, 0, 0));
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
Expand Down
3 changes: 3 additions & 0 deletions test/functional/feature_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def run_test(self):

# Check that weight and vsize are properly reported in mempool entry (txid1)
assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], tx1.get_vsize())
assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize_bip141"], tx1.get_vsize())
assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], tx1.get_weight())

# Now create tx2, which will spend from txid1.
Expand All @@ -304,6 +305,7 @@ def run_test(self):

# Check that weight and vsize are properly reported in mempool entry (txid2)
assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], tx.get_vsize())
assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize_bip141"], tx.get_vsize())
assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], tx.get_weight())

# Now create tx3, which will spend from txid2
Expand All @@ -327,6 +329,7 @@ def run_test(self):

# Check that weight and vsize are properly reported in mempool entry (txid3)
assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], tx.get_vsize())
assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize_bip141"], tx.get_vsize())
assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], tx.get_weight())

# Mine a block to clear the gbt cache again.
Expand Down
15 changes: 10 additions & 5 deletions test/functional/mempool_accept.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ def run_test(self):
raw_tx_0 = tx.serialize().hex()
txid_0 = tx.rehash()
self.check_mempool_result(
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee}}],
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(),
'vsize_bip141': tx.get_vsize(), 'fees': {'base': fee}}],
rawtxs=[raw_tx_0],
)

Expand All @@ -118,7 +119,8 @@ def run_test(self):
tx = tx_from_hex(raw_tx_final)
fee_expected = Decimal('50.0') - output_amount
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee_expected}}],
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(),
'vsize_bip141': tx.get_vsize(), 'fees': {'base': fee_expected}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
Expand All @@ -140,7 +142,8 @@ def run_test(self):
raw_tx_0 = tx.serialize().hex()
txid_0 = tx.rehash()
self.check_mempool_result(
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(),
'vsize_bip141': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],
rawtxs=[raw_tx_0],
)

Expand Down Expand Up @@ -197,7 +200,8 @@ def run_test(self):
raw_tx_reference = tx.serialize().hex()
# Reference tx should be valid on itself
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(),
'vsize_bip141': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
Expand Down Expand Up @@ -367,7 +371,8 @@ def run_test(self):
tx.vout[0] = CTxOut(COIN - 1000, DUMMY_MIN_OP_RETURN_SCRIPT)
assert_equal(len(tx.serialize_without_witness()), MIN_STANDARD_TX_NONWITNESS_SIZE)
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}],
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(),
'vsize_bip141': tx.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
Expand Down
1 change: 1 addition & 0 deletions test/functional/mempool_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def run_test(self):
descendant_vsize = 0

assert_equal(ancestor_vsize, sum([mempool[tx]['vsize'] for tx in mempool]))
assert_equal(ancestor_vsize, sum([mempool[tx]['vsize_bip141'] for tx in mempool]))
ancestor_count = DEFAULT_ANCESTOR_LIMIT
assert_equal(ancestor_fees, sum([mempool[tx]['fees']['base'] for tx in mempool]))

Expand Down
5 changes: 4 additions & 1 deletion test/functional/mempool_sigoplimit.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,15 @@ def test_sigops_limit(self, bytes_per_sigop, num_sigops):
res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
assert_equal(res['allowed'], True)
assert_equal(res['vsize'], sigop_equivalent_vsize)
assert_equal(res['vsize_bip141'], sigop_equivalent_vsize)

# increase the tx's vsize to be right above the sigop-limit equivalent size
# => tx's vsize in mempool should also grow accordingly
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad+1)])
res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
assert_equal(res['allowed'], True)
assert_equal(res['vsize'], sigop_equivalent_vsize+1)
assert_equal(res['vsize'], sigop_equivalent_vsize + 1)
assert_equal(res['vsize_bip141'], tx.get_vsize())

# decrease the tx's vsize to be right below the sigop-limit equivalent size
# => tx's vsize in mempool should stick at the sigop-limit equivalent
Expand All @@ -107,6 +109,7 @@ def test_sigops_limit(self, bytes_per_sigop, num_sigops):
res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
assert_equal(res['allowed'], True)
assert_equal(res['vsize'], sigop_equivalent_vsize)
assert_equal(res['vsize_bip141'], tx.get_vsize())

# check that the ancestor and descendant size calculations in the mempool
# also use the same max(sigop_equivalent_vsize, serialized_vsize) logic
Expand Down
3 changes: 3 additions & 0 deletions test/functional/mining_prioritisetransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,14 @@ def run_test(self):
# create more transactions.
mempool = self.nodes[0].getrawmempool(True)
sizes = [0, 0, 0]
sizes_bip141 = [0, 0, 0]
for i in range(3):
for j in txids[i]:
assert j in mempool
sizes[i] += mempool[j]['vsize']
sizes_bip141[i] += mempool[j]['vsize_bip141']
assert sizes[i] > MAX_BLOCK_WEIGHT // 4 # Fail => raise utxo_count
assert sizes_bip141[i] > MAX_BLOCK_WEIGHT // 4 # Fail => raise utxo_count

assert_equal(self.nodes[0].getprioritisedtransactions(), {})
# add a fee delta to something in the cheapest bucket and make sure it gets mined
Expand Down
2 changes: 2 additions & 0 deletions test/functional/p2p_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ def test_standardness_v0(self):
'wtxid': tx3.getwtxid(),
'allowed': True,
'vsize': tx3.get_vsize(),
'vsize_bip141': tx3.get_vsize(),
'fees': {
'base': Decimal('0.00001000'),
},
Expand All @@ -650,6 +651,7 @@ def test_standardness_v0(self):
'wtxid': tx3.getwtxid(),
'allowed': True,
'vsize': tx3.get_vsize(),
'vsize_bip141': tx3.get_vsize(),
'fees': {
'base': Decimal('0.00011000'),
},
Expand Down
4 changes: 4 additions & 0 deletions test/functional/rpc_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def test_rbf(self):
assert_equal(testres_replaceable["wtxid"], replaceable_tx["wtxid"])
assert testres_replaceable["allowed"]
assert_equal(testres_replaceable["vsize"], replaceable_tx["tx"].get_vsize())
assert_equal(testres_replaceable["vsize_bip141"], replaceable_tx["tx"].get_vsize())
assert_equal(testres_replaceable["fees"]["base"], fee)
assert_fee_amount(fee, replaceable_tx["tx"].get_vsize(), testres_replaceable["fees"]["effective-feerate"])
assert_equal(testres_replaceable["fees"]["effective-includes"], [replaceable_tx["wtxid"]])
Expand Down Expand Up @@ -280,9 +281,11 @@ def assert_equal_package_results(self, node, testmempoolaccept_result, submitpac
# No "allowed" if the tx was already in the mempool
if "allowed" in testres_tx and testres_tx["allowed"]:
assert_equal(submitres_tx["vsize"], testres_tx["vsize"])
assert_equal(submitres_tx["vsize_bip141"], testres_tx["vsize_bip141"])
assert_equal(submitres_tx["fees"]["base"], testres_tx["fees"]["base"])
entry_info = node.getmempoolentry(submitres_tx["txid"])
assert_equal(submitres_tx["vsize"], entry_info["vsize"])
assert_equal(submitres_tx["vsize_bip141"], entry_info["vsize_bip141"])
assert_equal(submitres_tx["fees"]["base"], entry_info["fees"]["base"])

def test_submit_child_with_parents(self, num_parents, partial_submit):
Expand Down Expand Up @@ -312,6 +315,7 @@ def test_submit_child_with_parents(self, num_parents, partial_submit):
tx_result = submitpackage_result["tx-results"][wtxid]
assert_equal(tx_result["txid"], tx.rehash())
assert_equal(tx_result["vsize"], tx.get_vsize())
assert_equal(tx_result["vsize_bip141"], tx.get_vsize())
assert_equal(tx_result["fees"]["base"], DEFAULT_FEE)
if wtxid not in presubmitted_wtxids:
assert_fee_amount(DEFAULT_FEE, tx.get_vsize(), tx_result["fees"]["effective-feerate"])
Expand Down
7 changes: 7 additions & 0 deletions test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size
def get_vsize(self, txn):
return self.nodes[0].decoderawtransaction(txn)['vsize']

def get_vsize_bip141(self, txn):
return self.nodes[0].decoderawtransaction(txn)['vsize_bip141']

def run_test(self):

# Check that there's no UTXO on none of the nodes
Expand Down Expand Up @@ -487,10 +490,12 @@ def run_test(self):
# Test passing fee_rate as an integer
txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=fee_rate_sat_vb)
tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
tx_size_bip141 = self.get_vsize_bip141(self.nodes[2].gettransaction(txid)['hex'])
self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))
postbalance = self.nodes[2].getbalance()
fee = prebalance - postbalance - Decimal(amount)
assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
assert_fee_amount(fee, tx_size_bip141, Decimal(fee_rate_btc_kvb))

prebalance = self.nodes[2].getbalance()
amount = Decimal("0.001")
Expand All @@ -499,10 +504,12 @@ def run_test(self):
# Test passing fee_rate as a string
txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=str(fee_rate_sat_vb))
tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
tx_size_bip141 = self.get_vsize_bip141(self.nodes[2].gettransaction(txid)['hex'])
self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))
postbalance = self.nodes[2].getbalance()
fee = prebalance - postbalance - amount
assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
assert_fee_amount(fee, tx_size_bip141, Decimal(fee_rate_btc_kvb))

# Test setting explicit fee rate just below the minimum.
self.log.info("Test sendtoaddress raises 'fee rate too low' if fee_rate of 0.99999999 is passed")
Expand Down
4 changes: 4 additions & 0 deletions test/functional/wallet_bumpfee.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ def test_notmine_bumpfee(self, rbf_node, peer_node, dest_address):
entry = rbf_node.getmempoolentry(rbfid)
old_fee = entry["fees"]["base"]
old_feerate = int(old_fee / entry["vsize"] * Decimal(1e8))
old_feerate_bip141 = int(old_fee / entry["vsize_bip141"] * Decimal(1e8))
assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet",
rbf_node.bumpfee, rbfid)

Expand All @@ -415,6 +416,9 @@ def finish_psbtbumpfee(psbt):
psbt = rbf_node.psbtbumpfee(txid=rbfid, fee_rate=old_feerate + 10)
finish_psbtbumpfee(psbt["psbt"])

psbt = rbf_node.psbtbumpfee(txid=rbfid, fee_rate=old_feerate_bip141 + 10)
finish_psbtbumpfee(psbt["psbt"])

self.clear_mempool()


Expand Down
Loading