mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-19 18:09:47 +01:00
Have createNewBlock return BlockTemplate interface
An external program that uses the Mining interface may need quick access to some information in the block template, while it can wait a bit longer for the full raw transaction data. This would be the case for a Stratum v2 Template Provider which needs to send a NewTemplate message (which doesn't include transactions) as quickly as possible.
This commit is contained in:
parent
cf0120ff02
commit
dd87b6dff3
@ -5,26 +5,45 @@
|
|||||||
#ifndef BITCOIN_INTERFACES_MINING_H
|
#ifndef BITCOIN_INTERFACES_MINING_H
|
||||||
#define BITCOIN_INTERFACES_MINING_H
|
#define BITCOIN_INTERFACES_MINING_H
|
||||||
|
|
||||||
#include <node/types.h>
|
#include <consensus/amount.h> // for CAmount
|
||||||
#include <uint256.h>
|
#include <node/types.h> // for BlockCreateOptions
|
||||||
|
#include <primitives/block.h> // for CBlock, CBlockHeader
|
||||||
|
#include <primitives/transaction.h> // for CTransactionRef
|
||||||
|
#include <stdint.h> // for int64_t
|
||||||
|
#include <uint256.h> // for uint256
|
||||||
|
|
||||||
#include <memory>
|
#include <memory> // for unique_ptr, shared_ptr
|
||||||
#include <optional>
|
#include <optional> // for optional
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
struct CBlockTemplate;
|
|
||||||
struct NodeContext;
|
struct NodeContext;
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
class BlockValidationState;
|
class BlockValidationState;
|
||||||
class CBlock;
|
|
||||||
class CScript;
|
class CScript;
|
||||||
|
|
||||||
namespace interfaces {
|
namespace interfaces {
|
||||||
|
|
||||||
|
//! Block template interface
|
||||||
|
class BlockTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~BlockTemplate() = default;
|
||||||
|
|
||||||
|
virtual CBlockHeader getBlockHeader() = 0;
|
||||||
|
virtual CBlock getBlock() = 0;
|
||||||
|
|
||||||
|
virtual std::vector<CAmount> getTxFees() = 0;
|
||||||
|
virtual std::vector<int64_t> getTxSigops() = 0;
|
||||||
|
|
||||||
|
virtual CTransactionRef getCoinbaseTx() = 0;
|
||||||
|
virtual std::vector<unsigned char> getCoinbaseCommitment() = 0;
|
||||||
|
virtual int getWitnessCommitmentIndex() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
//! Interface giving clients (RPC, Stratum v2 Template Provider in the future)
|
//! Interface giving clients (RPC, Stratum v2 Template Provider in the future)
|
||||||
//! ability to create block templates.
|
//! ability to create block templates.
|
||||||
|
|
||||||
class Mining
|
class Mining
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -39,14 +58,14 @@ public:
|
|||||||
//! Returns the hash for the tip of this chain
|
//! Returns the hash for the tip of this chain
|
||||||
virtual std::optional<uint256> getTipHash() = 0;
|
virtual std::optional<uint256> getTipHash() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new block template
|
* Construct a new block template
|
||||||
*
|
*
|
||||||
* @param[in] script_pub_key the coinbase output
|
* @param[in] script_pub_key the coinbase output
|
||||||
* @param[in] options options for creating the block
|
* @param[in] options options for creating the block
|
||||||
* @returns a block template
|
* @returns a block template
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<node::CBlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options={}) = 0;
|
virtual std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options = {}) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes new block. A valid new block is automatically relayed to peers.
|
* Processes new block. A valid new block is automatically relayed to peers.
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
|
|
||||||
#include <boost/signals2/signal.hpp>
|
#include <boost/signals2/signal.hpp>
|
||||||
|
|
||||||
|
using interfaces::BlockTemplate;
|
||||||
using interfaces::BlockTip;
|
using interfaces::BlockTip;
|
||||||
using interfaces::Chain;
|
using interfaces::Chain;
|
||||||
using interfaces::FoundBlock;
|
using interfaces::FoundBlock;
|
||||||
@ -863,6 +864,52 @@ public:
|
|||||||
NodeContext& m_node;
|
NodeContext& m_node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BlockTemplateImpl : public BlockTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit BlockTemplateImpl(std::unique_ptr<CBlockTemplate> 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<CAmount> getTxFees() override
|
||||||
|
{
|
||||||
|
return m_block_template->vTxFees;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int64_t> getTxSigops() override
|
||||||
|
{
|
||||||
|
return m_block_template->vTxSigOpsCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTransactionRef getCoinbaseTx() override
|
||||||
|
{
|
||||||
|
return m_block_template->block.vtx[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> getCoinbaseCommitment() override
|
||||||
|
{
|
||||||
|
return m_block_template->vchCoinbaseCommitment;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getWitnessCommitmentIndex() override
|
||||||
|
{
|
||||||
|
return GetWitnessCommitmentIndex(m_block_template->block);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unique_ptr<CBlockTemplate> m_block_template;
|
||||||
|
};
|
||||||
|
|
||||||
class MinerImpl : public Mining
|
class MinerImpl : public Mining
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -909,11 +956,11 @@ public:
|
|||||||
return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, tip, /*fCheckPOW=*/false, check_merkle_root);
|
return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, tip, /*fCheckPOW=*/false, check_merkle_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CBlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
|
std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
|
||||||
{
|
{
|
||||||
BlockAssembler::Options assemble_options{options};
|
BlockAssembler::Options assemble_options{options};
|
||||||
ApplyArgsManOptions(*Assert(m_node.args), assemble_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<BlockTemplateImpl>(BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeContext* context() override { return &m_node; }
|
NodeContext* context() override { return &m_node; }
|
||||||
|
@ -45,9 +45,9 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
using node::BlockAssembler;
|
using interfaces::BlockTemplate;
|
||||||
using node::CBlockTemplate;
|
|
||||||
using interfaces::Mining;
|
using interfaces::Mining;
|
||||||
|
using node::BlockAssembler;
|
||||||
using node::NodeContext;
|
using node::NodeContext;
|
||||||
using node::RegenerateCommitments;
|
using node::RegenerateCommitments;
|
||||||
using node::UpdateTime;
|
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<const CBlock>& block_out, bool process_new_block)
|
static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock&& block, uint64_t& max_tries, std::shared_ptr<const CBlock>& block_out, bool process_new_block)
|
||||||
{
|
{
|
||||||
block_out.reset();
|
block_out.reset();
|
||||||
block.hashMerkleRoot = BlockMerkleRoot(block);
|
block.hashMerkleRoot = BlockMerkleRoot(block);
|
||||||
@ -146,7 +146,7 @@ static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& bl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_out = std::make_shared<const CBlock>(block);
|
block_out = std::make_shared<const CBlock>(std::move(block));
|
||||||
|
|
||||||
if (!process_new_block) return true;
|
if (!process_new_block) return true;
|
||||||
|
|
||||||
@ -161,12 +161,11 @@ static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const
|
|||||||
{
|
{
|
||||||
UniValue blockHashes(UniValue::VARR);
|
UniValue blockHashes(UniValue::VARR);
|
||||||
while (nGenerate > 0 && !chainman.m_interrupt) {
|
while (nGenerate > 0 && !chainman.m_interrupt) {
|
||||||
std::unique_ptr<CBlockTemplate> pblocktemplate(miner.createNewBlock(coinbase_script));
|
std::unique_ptr<BlockTemplate> block_template(miner.createNewBlock(coinbase_script));
|
||||||
if (!pblocktemplate.get())
|
CHECK_NONFATAL(block_template);
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
|
|
||||||
|
|
||||||
std::shared_ptr<const CBlock> block_out;
|
std::shared_ptr<const CBlock> 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,11 +370,10 @@ static RPCHelpMan generateblock()
|
|||||||
|
|
||||||
ChainstateManager& chainman = EnsureChainman(node);
|
ChainstateManager& chainman = EnsureChainman(node);
|
||||||
{
|
{
|
||||||
std::unique_ptr<CBlockTemplate> blocktemplate{miner.createNewBlock(coinbase_script, {.use_mempool = false})};
|
std::unique_ptr<BlockTemplate> block_template{miner.createNewBlock(coinbase_script, {.use_mempool = false})};
|
||||||
if (!blocktemplate) {
|
CHECK_NONFATAL(block_template);
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
|
|
||||||
}
|
block = block_template->getBlock();
|
||||||
block = blocktemplate->block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_NONFATAL(block.vtx.size() == 1);
|
CHECK_NONFATAL(block.vtx.size() == 1);
|
||||||
@ -394,7 +392,7 @@ static RPCHelpMan generateblock()
|
|||||||
std::shared_ptr<const CBlock> block_out;
|
std::shared_ptr<const CBlock> block_out;
|
||||||
uint64_t max_tries{DEFAULT_MAX_TRIES};
|
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.");
|
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,7 +798,7 @@ static RPCHelpMan getblocktemplate()
|
|||||||
// Update block
|
// Update block
|
||||||
static CBlockIndex* pindexPrev;
|
static CBlockIndex* pindexPrev;
|
||||||
static int64_t time_start;
|
static int64_t time_start;
|
||||||
static std::unique_ptr<CBlockTemplate> pblocktemplate;
|
static std::unique_ptr<BlockTemplate> block_template;
|
||||||
if (!pindexPrev || pindexPrev->GetBlockHash() != tip ||
|
if (!pindexPrev || pindexPrev->GetBlockHash() != tip ||
|
||||||
(miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
|
(miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
|
||||||
{
|
{
|
||||||
@ -814,20 +812,19 @@ static RPCHelpMan getblocktemplate()
|
|||||||
|
|
||||||
// Create new block
|
// Create new block
|
||||||
CScript scriptDummy = CScript() << OP_TRUE;
|
CScript scriptDummy = CScript() << OP_TRUE;
|
||||||
pblocktemplate = miner.createNewBlock(scriptDummy);
|
block_template = miner.createNewBlock(scriptDummy);
|
||||||
if (!pblocktemplate) {
|
CHECK_NONFATAL(block_template);
|
||||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to update only after we know createNewBlock succeeded
|
// Need to update only after we know createNewBlock succeeded
|
||||||
pindexPrev = pindexPrevNew;
|
pindexPrev = pindexPrevNew;
|
||||||
}
|
}
|
||||||
CHECK_NONFATAL(pindexPrev);
|
CHECK_NONFATAL(pindexPrev);
|
||||||
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
|
CBlock block{block_template->getBlock()};
|
||||||
|
|
||||||
// Update nTime
|
// Update nTime
|
||||||
UpdateTime(pblock, consensusParams, pindexPrev);
|
UpdateTime(&block, consensusParams, pindexPrev);
|
||||||
pblock->nNonce = 0;
|
block.nNonce = 0;
|
||||||
|
|
||||||
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
|
// 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);
|
const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT);
|
||||||
@ -836,8 +833,11 @@ static RPCHelpMan getblocktemplate()
|
|||||||
|
|
||||||
UniValue transactions(UniValue::VARR);
|
UniValue transactions(UniValue::VARR);
|
||||||
std::map<uint256, int64_t> setTxIndex;
|
std::map<uint256, int64_t> setTxIndex;
|
||||||
|
std::vector<CAmount> tx_fees{block_template->getTxFees()};
|
||||||
|
std::vector<CAmount> tx_sigops{block_template->getTxSigops()};
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto& it : pblock->vtx) {
|
for (const auto& it : block.vtx) {
|
||||||
const CTransaction& tx = *it;
|
const CTransaction& tx = *it;
|
||||||
uint256 txHash = tx.GetHash();
|
uint256 txHash = tx.GetHash();
|
||||||
setTxIndex[txHash] = i++;
|
setTxIndex[txHash] = i++;
|
||||||
@ -860,8 +860,8 @@ static RPCHelpMan getblocktemplate()
|
|||||||
entry.pushKV("depends", std::move(deps));
|
entry.pushKV("depends", std::move(deps));
|
||||||
|
|
||||||
int index_in_template = i - 1;
|
int index_in_template = i - 1;
|
||||||
entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]);
|
entry.pushKV("fee", tx_fees.at(index_in_template));
|
||||||
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
|
int64_t nTxSigOps{tx_sigops.at(index_in_template)};
|
||||||
if (fPreSegWit) {
|
if (fPreSegWit) {
|
||||||
CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
|
CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
|
||||||
nTxSigOps /= WITNESS_SCALE_FACTOR;
|
nTxSigOps /= WITNESS_SCALE_FACTOR;
|
||||||
@ -874,7 +874,7 @@ static RPCHelpMan getblocktemplate()
|
|||||||
|
|
||||||
UniValue aux(UniValue::VOBJ);
|
UniValue aux(UniValue::VOBJ);
|
||||||
|
|
||||||
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
|
arith_uint256 hashTarget = arith_uint256().SetCompact(block.nBits);
|
||||||
|
|
||||||
UniValue aMutable(UniValue::VARR);
|
UniValue aMutable(UniValue::VARR);
|
||||||
aMutable.push_back("time");
|
aMutable.push_back("time");
|
||||||
@ -904,7 +904,7 @@ static RPCHelpMan getblocktemplate()
|
|||||||
break;
|
break;
|
||||||
case ThresholdState::LOCKED_IN:
|
case ThresholdState::LOCKED_IN:
|
||||||
// Ensure bit is set in block version
|
// Ensure bit is set in block version
|
||||||
pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
|
block.nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case ThresholdState::STARTED:
|
case ThresholdState::STARTED:
|
||||||
{
|
{
|
||||||
@ -913,7 +913,7 @@ static RPCHelpMan getblocktemplate()
|
|||||||
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
|
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
|
||||||
if (!vbinfo.gbt_force) {
|
if (!vbinfo.gbt_force) {
|
||||||
// If the client doesn't support this, don't indicate it in the [default] version
|
// 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;
|
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("rules", std::move(aRules));
|
||||||
result.pushKV("vbavailable", std::move(vbavailable));
|
result.pushKV("vbavailable", std::move(vbavailable));
|
||||||
result.pushKV("vbrequired", int(0));
|
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("transactions", std::move(transactions));
|
||||||
result.pushKV("coinbaseaux", std::move(aux));
|
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("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast));
|
||||||
result.pushKV("target", hashTarget.GetHex());
|
result.pushKV("target", hashTarget.GetHex());
|
||||||
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
|
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
|
||||||
@ -960,16 +960,16 @@ static RPCHelpMan getblocktemplate()
|
|||||||
if (!fPreSegWit) {
|
if (!fPreSegWit) {
|
||||||
result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT);
|
result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT);
|
||||||
}
|
}
|
||||||
result.pushKV("curtime", pblock->GetBlockTime());
|
result.pushKV("curtime", block.GetBlockTime());
|
||||||
result.pushKV("bits", strprintf("%08x", pblock->nBits));
|
result.pushKV("bits", strprintf("%08x", block.nBits));
|
||||||
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
|
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
|
||||||
|
|
||||||
if (consensusParams.signet_blocks) {
|
if (consensusParams.signet_blocks) {
|
||||||
result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge));
|
result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
|
if (!block_template->getCoinbaseCommitment().empty()) {
|
||||||
result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment));
|
result.pushKV("default_witness_commitment", HexStr(block_template->getCoinbaseCommitment()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
Loading…
Reference in New Issue
Block a user