Merge bitcoin/bitcoin#33819: mining: getCoinbase() returns struct instead of raw tx

48f57bb35b mining: add new getCoinbaseTx() returning a struct (Sjors Provoost)
d59b4cdb57 mining: rename getCoinbaseTx() to ..RawTx() (Sjors Provoost)

Pull request description:

  The first commit renames `getCoinbaseTx()` to `getCoinbaseRawTx()` to reflect that it returns a serialised transaction. This does not impact IPC clients, because they do not use the function name.

  The second commit then introduces a replacement `getCoinbase()` that provides a struct with everything clients need to construct a coinbase. This avoids clients having to parse and manipulate our dummy transaction.

  Deprecate but don't remove `getCoinbaseRawTx()`, `getCoinbaseCommitment()` and `getWitnessCommitmentIndex()`.

  After this change we can drop these deprecated methods, which in turn would allow us to clear the dummy transaction from the `getBlock()` result. But that is left for a followup to keep this PR focussed. See https://github.com/Sjors/bitcoin/pull/106 for an approach.

  Expand the `interface_ipc.py` functional test to document its usage.

  Can be tested using:
  - https://github.com/stratum-mining/sv2-tp/pull/59

ACKs for top commit:
  ryanofsky:
    Code review ACK 48f57bb35b. Just rebased and addressed comments and dropped coinbase tx "template" suffix, which is a nice change
  ismaelsadeeq:
    code review ACK 48f57bb35b
  vasild:
    ACK 48f57bb35b

Tree-SHA512: c4f1d752777fb3086a1a0b7b8b06e4205dbe2f3adb41f218855ad1dee952adccc263cf82acd3bf9300cc83c2c64cebd2b27f66a69beee32d325b9a85e3643b0d
This commit is contained in:
Ryan Ofsky
2026-01-13 07:33:08 -05:00
9 changed files with 239 additions and 10 deletions

View File

@@ -158,18 +158,51 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
// Create coinbase transaction.
CMutableTransaction coinbaseTx;
// Construct coinbase transaction struct in parallel
CoinbaseTx& coinbase_tx{pblocktemplate->m_coinbase_tx};
coinbase_tx.version = coinbaseTx.version;
coinbaseTx.vin.resize(1);
coinbaseTx.vin[0].prevout.SetNull();
coinbaseTx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; // Make sure timelock is enforced.
coinbase_tx.sequence = coinbaseTx.vin[0].nSequence;
// Add an output that spends the full coinbase reward.
coinbaseTx.vout.resize(1);
coinbaseTx.vout[0].scriptPubKey = m_options.coinbase_output_script;
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
// Block subsidy + fees
const CAmount block_reward{nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus())};
coinbaseTx.vout[0].nValue = block_reward;
coinbase_tx.block_reward_remaining = block_reward;
// Start the coinbase scriptSig with the block height as required by BIP34.
// The trailing OP_0 (historically an extranonce) is optional padding and
// could be removed without a consensus change. Mining clients are expected
// to append extra data to this prefix, so increasing its length would reduce
// the space they can use and may break existing clients.
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
coinbase_tx.script_sig_prefix = coinbaseTx.vin[0].scriptSig;
Assert(nHeight > 0);
coinbaseTx.nLockTime = static_cast<uint32_t>(nHeight - 1);
coinbase_tx.lock_time = coinbaseTx.nLockTime;
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
const CTransactionRef& final_coinbase{pblock->vtx[0]};
if (final_coinbase->HasWitness()) {
const auto& witness_stack{final_coinbase->vin[0].scriptWitness.stack};
// Consensus requires the coinbase witness stack to have exactly one
// element of 32 bytes.
Assert(witness_stack.size() == 1 && witness_stack[0].size() == 32);
coinbase_tx.witness = uint256(witness_stack[0]);
}
if (const int witness_index = GetWitnessCommitmentIndex(*pblock); witness_index != NO_WITNESS_COMMITMENT) {
Assert(witness_index >= 0 && static_cast<size_t>(witness_index) < final_coinbase->vout.size());
coinbase_tx.required_outputs.push_back(final_coinbase->vout[witness_index]);
}
LogInfo("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
// Fill in header
@@ -440,4 +473,5 @@ std::optional<BlockRef> WaitTipChanged(ChainstateManager& chainman, KernelNotifi
// avoid deadlocks.
return GetTip(chainman);
}
} // namespace node