diff --git a/doc/release-notes-33819.md b/doc/release-notes-33819.md index 6c1cebbde77..79ed1f70749 100644 --- a/doc/release-notes-33819.md +++ b/doc/release-notes-33819.md @@ -1,5 +1,8 @@ Mining IPC ---------- -- The `getCoinbaseTx()` method is renamed to `getCoinbaseRawTx()`. +- The `getCoinbaseTx()` method is renamed to `getCoinbaseRawTx()` and deprecated. IPC clients do not use the function name, so they're not affected. (#33819) +- Adds `getCoinbaseTx()` which clients should use instead of `getCoinbaseRawTx()`. It + contains all fields required to construct a coinbase transaction, and omits the + dummy output which Bitcoin Core uses internally. (#33819) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 97f90735156..07ec5f9c3e0 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -44,16 +44,26 @@ public: /** * Return serialized dummy coinbase transaction. + * + * @note deprecated: use getCoinbaseTx() */ virtual CTransactionRef getCoinbaseRawTx() = 0; + /** Return fields needed to construct a coinbase transaction */ + virtual node::CoinbaseTx getCoinbaseTx() = 0; + /** * Return scriptPubKey with SegWit OP_RETURN. + * + * @note deprecated: use getCoinbaseTx() */ virtual std::vector getCoinbaseCommitment() = 0; /** * Return which output in the dummy coinbase contains the SegWit OP_RETURN. + * + * @note deprecated. Scan outputs from getCoinbaseTx() outputs field for the + * SegWit marker. */ virtual int getWitnessCommitmentIndex() = 0; diff --git a/src/ipc/capnp/mining.capnp b/src/ipc/capnp/mining.capnp index 85cff64d919..76cf6673207 100644 --- a/src/ipc/capnp/mining.capnp +++ b/src/ipc/capnp/mining.capnp @@ -28,6 +28,7 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") { getTxFees @3 (context: Proxy.Context) -> (result: List(Int64)); getTxSigops @4 (context: Proxy.Context) -> (result: List(Int64)); getCoinbaseRawTx @5 (context: Proxy.Context) -> (result: Data); + getCoinbaseTx @12 (context: Proxy.Context) -> (result: CoinbaseTx); getCoinbaseCommitment @6 (context: Proxy.Context) -> (result: Data); getWitnessCommitmentIndex @7 (context: Proxy.Context) -> (result: Int32); getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data)); @@ -51,3 +52,13 @@ struct BlockCheckOptions $Proxy.wrap("node::BlockCheckOptions") { checkMerkleRoot @0 :Bool $Proxy.name("check_merkle_root"); checkPow @1 :Bool $Proxy.name("check_pow"); } + +struct CoinbaseTx $Proxy.wrap("node::CoinbaseTx") { + version @0 :UInt32 $Proxy.name("version"); + sequence @1 :UInt32 $Proxy.name("sequence"); + scriptSigPrefix @2 :Data $Proxy.name("script_sig_prefix"); + witness @3 :Data $Proxy.name("witness"); + blockRewardRemaining @4 :Int64 $Proxy.name("block_reward_remaining"); + requiredOutputs @5 :List(Data) $Proxy.name("required_outputs"); + lockTime @6 :UInt32 $Proxy.name("lock_time"); +} diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 8235e1311cb..b62bd1bfd7e 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -84,6 +84,7 @@ using interfaces::WalletLoader; using kernel::ChainstateRole; using node::BlockAssembler; using node::BlockWaitOptions; +using node::CoinbaseTx; using util::Join; namespace node { @@ -893,6 +894,11 @@ public: return m_block_template->block.vtx[0]; } + CoinbaseTx getCoinbaseTx() override + { + return m_block_template->m_coinbase_tx; + } + std::vector getCoinbaseCommitment() override { return m_block_template->vchCoinbaseCommitment; diff --git a/src/node/miner.cpp b/src/node/miner.cpp index a137f5d4a78..13458ca94d6 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -158,18 +158,51 @@ std::unique_ptr 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(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(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 WaitTipChanged(ChainstateManager& chainman, KernelNotifi // avoid deadlocks. return GetTip(chainman); } + } // namespace node diff --git a/src/node/miner.h b/src/node/miner.h index 02272242b41..f4bebb51d33 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -50,6 +50,11 @@ struct CBlockTemplate /* A vector of package fee rates, ordered by the sequence in which * packages are selected for inclusion in the block template.*/ std::vector m_package_feerates; + /* + * Template containing all coinbase transaction fields that are set by our + * miner code. + */ + CoinbaseTx m_coinbase_tx; }; /** Generate a new block, without valid proof-of-work */ @@ -157,6 +162,7 @@ std::optional GetTip(ChainstateManager& chainman); /* Waits for the connected tip to change until timeout has elapsed. During node initialization, this will wait until the tip is connected (regardless of `timeout`). * Returns the current tip, or nullopt if the node is shutting down. */ std::optional WaitTipChanged(ChainstateManager& chainman, KernelNotifications& kernel_notifications, const uint256& current_tip, MillisecondsDouble& timeout); + } // namespace node #endif // BITCOIN_NODE_MINER_H diff --git a/src/node/types.h b/src/node/types.h index 6c2687626c9..fbc30d79418 100644 --- a/src/node/types.h +++ b/src/node/types.h @@ -16,10 +16,13 @@ #include #include #include +#include #include +#include #include