Compare commits

...

3 Commits

Author SHA1 Message Date
Sjors Provoost
c7a75cb99b
Merge 226d81f8b708ea1c3a39ec58446e291fe7440fdd into cd8089c20baaaee329cbf7951195953a9f86d7cf 2025-03-16 17:15:50 +01:00
Sjors Provoost
226d81f8b7
mining: drop unused -nFees and sigops from CBlockTemplate
For the coinbase vTxFees used a dummy value of -nFees. This
value was never returned by the RPC or used in a test.

Similarly the fist vTxSigOpsCost entry was calculated from
the dummy coinbase transaction.

Drop both and add code comments to prevent confusion.
2025-03-13 11:16:57 +01:00
Sjors Provoost
53ad845fb9
test: check fees and sigops in getblocktemplate 2025-03-13 11:16:57 +01:00
5 changed files with 64 additions and 7 deletions

View File

@ -34,9 +34,12 @@ public:
virtual ~BlockTemplate() = default;
virtual CBlockHeader getBlockHeader() = 0;
// Block contains a dummy coinbase transaction that should not be used.
virtual CBlock getBlock() = 0;
// Fees per transaction, not including coinbase transaction.
virtual std::vector<CAmount> getTxFees() = 0;
// Sigop cost per transaction, not including coinbase transaction.
virtual std::vector<int64_t> getTxSigops() = 0;
virtual CTransactionRef getCoinbaseTx() = 0;

View File

@ -124,10 +124,9 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
pblocktemplate.reset(new CBlockTemplate());
CBlock* const pblock = &pblocktemplate->block; // pointer for convenience
// Add dummy coinbase tx as first transaction
// Add dummy coinbase tx as first transaction. It is skipped by the
// getblocktemplate RPC and mining interface consumers must not use it.
pblock->vtx.emplace_back();
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK(::cs_main);
CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
@ -165,7 +164,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
pblocktemplate->vTxFees[0] = -nFees;
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
@ -174,7 +172,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev,

View File

@ -37,7 +37,9 @@ static const bool DEFAULT_PRINT_MODIFIED_FEE = false;
struct CBlockTemplate
{
CBlock block;
// Fees per transaction, not including coinbase transaction (unlike CBlock::vtx).
std::vector<CAmount> vTxFees;
// Sigops per transaction, not including coinbase transaction (unlike CBlock::vtx).
std::vector<int64_t> vTxSigOpsCost;
std::vector<unsigned char> vchCoinbaseCommitment;
/* A vector of package fee rates, ordered by the sequence in which

View File

@ -893,7 +893,7 @@ static RPCHelpMan getblocktemplate()
}
entry.pushKV("depends", std::move(deps));
int index_in_template = i - 1;
int index_in_template = i - 2;
entry.pushKV("fee", tx_fees.at(index_in_template));
int64_t nTxSigOps{tx_sigops.at(index_in_template)};
if (fPreSegWit) {

View File

@ -40,7 +40,10 @@ from test_framework.util import (
assert_raises_rpc_error,
get_fee,
)
from test_framework.wallet import MiniWallet
from test_framework.wallet import (
MiniWallet,
MiniWalletMode,
)
DIFFICULTY_ADJUSTMENT_INTERVAL = 144
@ -93,6 +96,57 @@ class MiningTest(BitcoinTestFramework):
self.restart_node(0)
self.connect_nodes(0, 1)
def test_fees_and_sigops(self):
self.log.info("Test fees and sigops in getblocktemplate result")
node = self.nodes[0]
# Generate a coinbases with p2pk transactions for its sigops.
wallet_sigops = MiniWallet(node, mode=MiniWalletMode.RAW_P2PK)
self.generate(wallet_sigops, 1, sync_fun=self.no_op)
# Mature with regular coinbases to prevent inteference with other tests
self.generate(self.wallet, 100, sync_fun=self.no_op)
# Generate three transactions that must be mined in sequence
#
# tx_a (1 sat/vbyte)
# |
# |
# tx_b (2 sat/vbyte)
# |
# |
# tx_c (3 sat/vbyte)
#
tx_a = wallet_sigops.send_self_transfer(from_node=node,
fee_rate=Decimal("0.00001"))
tx_b = wallet_sigops.send_self_transfer(from_node=node,
fee_rate=Decimal("0.00002"),
utxo_to_spend=tx_a["new_utxo"])
tx_c = wallet_sigops.send_self_transfer(from_node=node,
fee_rate=Decimal("0.00003"),
utxo_to_spend=tx_b["new_utxo"])
# Generate transaction without sigops. It will go first because it pays
# higher fees (100 sat/vbyte) and descends from a different coinbase.
tx_d = self.wallet.send_self_transfer(from_node=node,
fee_rate=Decimal("0.00100"))
block_template_txs = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)['transactions']
block_template_fees = [tx['fee'] for tx in block_template_txs]
assert_equal(block_template_fees, [
tx_d["fee"] * COIN,
tx_a["fee"] * COIN,
tx_b["fee"] * COIN,
tx_c["fee"] * COIN
])
block_template_sigops = [tx['sigops'] for tx in block_template_txs]
assert_equal(block_template_sigops, [0, 4, 4, 4])
# Clear mempool
self.generate(self.wallet, 1, sync_fun=self.no_op)
def test_blockmintxfee_parameter(self):
self.log.info("Test -blockmintxfee setting")
self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
@ -533,6 +587,7 @@ class MiningTest(BitcoinTestFramework):
node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
self.test_fees_and_sigops()
self.test_blockmintxfee_parameter()
self.test_block_max_weight()
self.test_timewarp()