diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index cebe97edb7a..c73465bd2aa 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -5,26 +5,45 @@ #ifndef BITCOIN_INTERFACES_MINING_H #define BITCOIN_INTERFACES_MINING_H -#include -#include +#include // for CAmount +#include // for BlockCreateOptions +#include // for CBlock, CBlockHeader +#include // for CTransactionRef +#include // for int64_t +#include // for uint256 -#include -#include +#include // for unique_ptr, shared_ptr +#include // for optional +#include // for vector namespace node { -struct CBlockTemplate; struct NodeContext; } // namespace node class BlockValidationState; -class CBlock; class CScript; namespace interfaces { +//! Block template interface +class BlockTemplate +{ +public: + virtual ~BlockTemplate() = default; + + virtual CBlockHeader getBlockHeader() = 0; + virtual CBlock getBlock() = 0; + + virtual std::vector getTxFees() = 0; + virtual std::vector getTxSigops() = 0; + + virtual CTransactionRef getCoinbaseTx() = 0; + virtual std::vector getCoinbaseCommitment() = 0; + virtual int getWitnessCommitmentIndex() = 0; +}; + //! Interface giving clients (RPC, Stratum v2 Template Provider in the future) //! ability to create block templates. - class Mining { public: @@ -39,14 +58,14 @@ public: //! Returns the hash for the tip of this chain virtual std::optional getTipHash() = 0; - /** + /** * Construct a new block template * * @param[in] script_pub_key the coinbase output * @param[in] options options for creating the block * @returns a block template */ - virtual std::unique_ptr createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options={}) = 0; + virtual std::unique_ptr createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options = {}) = 0; /** * Processes new block. A valid new block is automatically relayed to peers. diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 4a031836430..510541dfda9 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -67,6 +67,7 @@ #include +using interfaces::BlockTemplate; using interfaces::BlockTip; using interfaces::Chain; using interfaces::FoundBlock; @@ -863,6 +864,52 @@ public: NodeContext& m_node; }; +class BlockTemplateImpl : public BlockTemplate +{ +public: + explicit BlockTemplateImpl(std::unique_ptr block_template) : m_block_template(std::move(block_template)) + { + assert(m_block_template); + } + + CBlockHeader getBlockHeader() override + { + return m_block_template->block; + } + + CBlock getBlock() override + { + return m_block_template->block; + } + + std::vector getTxFees() override + { + return m_block_template->vTxFees; + } + + std::vector getTxSigops() override + { + return m_block_template->vTxSigOpsCost; + } + + CTransactionRef getCoinbaseTx() override + { + return m_block_template->block.vtx[0]; + } + + std::vector getCoinbaseCommitment() override + { + return m_block_template->vchCoinbaseCommitment; + } + + int getWitnessCommitmentIndex() override + { + return GetWitnessCommitmentIndex(m_block_template->block); + } + + const std::unique_ptr m_block_template; +}; + class MinerImpl : public Mining { public: @@ -909,11 +956,11 @@ public: return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, tip, /*fCheckPOW=*/false, check_merkle_root); } - std::unique_ptr createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override + std::unique_ptr createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override { BlockAssembler::Options assemble_options{options}; ApplyArgsManOptions(*Assert(m_node.args), assemble_options); - return BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key); + return std::make_unique(BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key)); } NodeContext* context() override { return &m_node; } diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 97f6ac346a5..181ae2ef05a 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -113,10 +113,6 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc resetBlock(); pblocktemplate.reset(new CBlockTemplate()); - - if (!pblocktemplate.get()) { - return nullptr; - } CBlock* const pblock = &pblocktemplate->block; // pointer for convenience // Add dummy coinbase tx as first transaction diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 375352b18dd..b986c26e7a3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -45,9 +45,9 @@ #include #include -using node::BlockAssembler; -using node::CBlockTemplate; +using interfaces::BlockTemplate; using interfaces::Mining; +using node::BlockAssembler; using node::NodeContext; using node::RegenerateCommitments; using node::UpdateTime; @@ -130,7 +130,7 @@ static RPCHelpMan getnetworkhashps() }; } -static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& block, uint64_t& max_tries, std::shared_ptr& block_out, bool process_new_block) +static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock&& block, uint64_t& max_tries, std::shared_ptr& block_out, bool process_new_block) { block_out.reset(); block.hashMerkleRoot = BlockMerkleRoot(block); @@ -146,7 +146,7 @@ static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& bl return true; } - block_out = std::make_shared(block); + block_out = std::make_shared(std::move(block)); if (!process_new_block) return true; @@ -161,12 +161,11 @@ static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const { UniValue blockHashes(UniValue::VARR); while (nGenerate > 0 && !chainman.m_interrupt) { - std::unique_ptr pblocktemplate(miner.createNewBlock(coinbase_script)); - if (!pblocktemplate.get()) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); + std::unique_ptr block_template(miner.createNewBlock(coinbase_script)); + CHECK_NONFATAL(block_template); std::shared_ptr block_out; - if (!GenerateBlock(chainman, miner, pblocktemplate->block, nMaxTries, block_out, /*process_new_block=*/true)) { + if (!GenerateBlock(chainman, miner, block_template->getBlock(), nMaxTries, block_out, /*process_new_block=*/true)) { break; } @@ -371,11 +370,10 @@ static RPCHelpMan generateblock() ChainstateManager& chainman = EnsureChainman(node); { - std::unique_ptr blocktemplate{miner.createNewBlock(coinbase_script, {.use_mempool = false})}; - if (!blocktemplate) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); - } - block = blocktemplate->block; + std::unique_ptr block_template{miner.createNewBlock(coinbase_script, {.use_mempool = false})}; + CHECK_NONFATAL(block_template); + + block = block_template->getBlock(); } CHECK_NONFATAL(block.vtx.size() == 1); @@ -394,7 +392,7 @@ static RPCHelpMan generateblock() std::shared_ptr block_out; uint64_t max_tries{DEFAULT_MAX_TRIES}; - if (!GenerateBlock(chainman, miner, block, max_tries, block_out, process_new_block) || !block_out) { + if (!GenerateBlock(chainman, miner, std::move(block), max_tries, block_out, process_new_block) || !block_out) { throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block."); } @@ -800,7 +798,7 @@ static RPCHelpMan getblocktemplate() // Update block static CBlockIndex* pindexPrev; static int64_t time_start; - static std::unique_ptr pblocktemplate; + static std::unique_ptr block_template; if (!pindexPrev || pindexPrev->GetBlockHash() != tip || (miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5)) { @@ -814,20 +812,19 @@ static RPCHelpMan getblocktemplate() // Create new block CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = miner.createNewBlock(scriptDummy); - if (!pblocktemplate) { - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - } + block_template = miner.createNewBlock(scriptDummy); + CHECK_NONFATAL(block_template); + // Need to update only after we know createNewBlock succeeded pindexPrev = pindexPrevNew; } CHECK_NONFATAL(pindexPrev); - CBlock* pblock = &pblocktemplate->block; // pointer for convenience + CBlock block{block_template->getBlock()}; // Update nTime - UpdateTime(pblock, consensusParams, pindexPrev); - pblock->nNonce = 0; + UpdateTime(&block, consensusParams, pindexPrev); + block.nNonce = 0; // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT); @@ -836,8 +833,11 @@ static RPCHelpMan getblocktemplate() UniValue transactions(UniValue::VARR); std::map setTxIndex; + std::vector tx_fees{block_template->getTxFees()}; + std::vector tx_sigops{block_template->getTxSigops()}; + int i = 0; - for (const auto& it : pblock->vtx) { + for (const auto& it : block.vtx) { const CTransaction& tx = *it; uint256 txHash = tx.GetHash(); setTxIndex[txHash] = i++; @@ -860,8 +860,8 @@ static RPCHelpMan getblocktemplate() entry.pushKV("depends", std::move(deps)); int index_in_template = i - 1; - entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]); - int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template]; + entry.pushKV("fee", tx_fees.at(index_in_template)); + int64_t nTxSigOps{tx_sigops.at(index_in_template)}; if (fPreSegWit) { CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0); nTxSigOps /= WITNESS_SCALE_FACTOR; @@ -874,7 +874,7 @@ static RPCHelpMan getblocktemplate() UniValue aux(UniValue::VOBJ); - arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + arith_uint256 hashTarget = arith_uint256().SetCompact(block.nBits); UniValue aMutable(UniValue::VARR); aMutable.push_back("time"); @@ -904,7 +904,7 @@ static RPCHelpMan getblocktemplate() break; case ThresholdState::LOCKED_IN: // Ensure bit is set in block version - pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos); + block.nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos); [[fallthrough]]; case ThresholdState::STARTED: { @@ -913,7 +913,7 @@ static RPCHelpMan getblocktemplate() if (setClientRules.find(vbinfo.name) == setClientRules.end()) { if (!vbinfo.gbt_force) { // If the client doesn't support this, don't indicate it in the [default] version - pblock->nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos); + block.nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos); } } break; @@ -933,15 +933,15 @@ static RPCHelpMan getblocktemplate() } } } - result.pushKV("version", pblock->nVersion); + result.pushKV("version", block.nVersion); result.pushKV("rules", std::move(aRules)); result.pushKV("vbavailable", std::move(vbavailable)); result.pushKV("vbrequired", int(0)); - result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); + result.pushKV("previousblockhash", block.hashPrevBlock.GetHex()); result.pushKV("transactions", std::move(transactions)); result.pushKV("coinbaseaux", std::move(aux)); - result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue); + result.pushKV("coinbasevalue", (int64_t)block.vtx[0]->vout[0].nValue); result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast)); result.pushKV("target", hashTarget.GetHex()); result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); @@ -960,16 +960,16 @@ static RPCHelpMan getblocktemplate() if (!fPreSegWit) { result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT); } - result.pushKV("curtime", pblock->GetBlockTime()); - result.pushKV("bits", strprintf("%08x", pblock->nBits)); + result.pushKV("curtime", block.GetBlockTime()); + result.pushKV("bits", strprintf("%08x", block.nBits)); result.pushKV("height", (int64_t)(pindexPrev->nHeight+1)); if (consensusParams.signet_blocks) { result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge)); } - if (!pblocktemplate->vchCoinbaseCommitment.empty()) { - result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment)); + if (!block_template->getCoinbaseCommitment().empty()) { + result.pushKV("default_witness_commitment", HexStr(block_template->getCoinbaseCommitment())); } return result;