Skip to content

Commit dd87b6d

Browse files
committed
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.
1 parent cf0120f commit dd87b6d

File tree

3 files changed

+112
-46
lines changed

3 files changed

+112
-46
lines changed

src/interfaces/mining.h

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,45 @@
55
#ifndef BITCOIN_INTERFACES_MINING_H
66
#define BITCOIN_INTERFACES_MINING_H
77

8-
#include <node/types.h>
9-
#include <uint256.h>
8+
#include <consensus/amount.h> // for CAmount
9+
#include <node/types.h> // for BlockCreateOptions
10+
#include <primitives/block.h> // for CBlock, CBlockHeader
11+
#include <primitives/transaction.h> // for CTransactionRef
12+
#include <stdint.h> // for int64_t
13+
#include <uint256.h> // for uint256
1014

11-
#include <memory>
12-
#include <optional>
15+
#include <memory> // for unique_ptr, shared_ptr
16+
#include <optional> // for optional
17+
#include <vector> // for vector
1318

1419
namespace node {
15-
struct CBlockTemplate;
1620
struct NodeContext;
1721
} // namespace node
1822

1923
class BlockValidationState;
20-
class CBlock;
2124
class CScript;
2225

2326
namespace interfaces {
2427

28+
//! Block template interface
29+
class BlockTemplate
30+
{
31+
public:
32+
virtual ~BlockTemplate() = default;
33+
34+
virtual CBlockHeader getBlockHeader() = 0;
35+
virtual CBlock getBlock() = 0;
36+
37+
virtual std::vector<CAmount> getTxFees() = 0;
38+
virtual std::vector<int64_t> getTxSigops() = 0;
39+
40+
virtual CTransactionRef getCoinbaseTx() = 0;
41+
virtual std::vector<unsigned char> getCoinbaseCommitment() = 0;
42+
virtual int getWitnessCommitmentIndex() = 0;
43+
};
44+
2545
//! Interface giving clients (RPC, Stratum v2 Template Provider in the future)
2646
//! ability to create block templates.
27-
2847
class Mining
2948
{
3049
public:
@@ -39,14 +58,14 @@ class Mining
3958
//! Returns the hash for the tip of this chain
4059
virtual std::optional<uint256> getTipHash() = 0;
4160

42-
/**
61+
/**
4362
* Construct a new block template
4463
*
4564
* @param[in] script_pub_key the coinbase output
4665
* @param[in] options options for creating the block
4766
* @returns a block template
4867
*/
49-
virtual std::unique_ptr<node::CBlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options={}) = 0;
68+
virtual std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options = {}) = 0;
5069

5170
/**
5271
* Processes new block. A valid new block is automatically relayed to peers.

src/node/interfaces.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767

6868
#include <boost/signals2/signal.hpp>
6969

70+
using interfaces::BlockTemplate;
7071
using interfaces::BlockTip;
7172
using interfaces::Chain;
7273
using interfaces::FoundBlock;
@@ -863,6 +864,52 @@ class ChainImpl : public Chain
863864
NodeContext& m_node;
864865
};
865866

867+
class BlockTemplateImpl : public BlockTemplate
868+
{
869+
public:
870+
explicit BlockTemplateImpl(std::unique_ptr<CBlockTemplate> block_template) : m_block_template(std::move(block_template))
871+
{
872+
assert(m_block_template);
873+
}
874+
875+
CBlockHeader getBlockHeader() override
876+
{
877+
return m_block_template->block;
878+
}
879+
880+
CBlock getBlock() override
881+
{
882+
return m_block_template->block;
883+
}
884+
885+
std::vector<CAmount> getTxFees() override
886+
{
887+
return m_block_template->vTxFees;
888+
}
889+
890+
std::vector<int64_t> getTxSigops() override
891+
{
892+
return m_block_template->vTxSigOpsCost;
893+
}
894+
895+
CTransactionRef getCoinbaseTx() override
896+
{
897+
return m_block_template->block.vtx[0];
898+
}
899+
900+
std::vector<unsigned char> getCoinbaseCommitment() override
901+
{
902+
return m_block_template->vchCoinbaseCommitment;
903+
}
904+
905+
int getWitnessCommitmentIndex() override
906+
{
907+
return GetWitnessCommitmentIndex(m_block_template->block);
908+
}
909+
910+
const std::unique_ptr<CBlockTemplate> m_block_template;
911+
};
912+
866913
class MinerImpl : public Mining
867914
{
868915
public:
@@ -909,11 +956,11 @@ class MinerImpl : public Mining
909956
return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, tip, /*fCheckPOW=*/false, check_merkle_root);
910957
}
911958

912-
std::unique_ptr<CBlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
959+
std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
913960
{
914961
BlockAssembler::Options assemble_options{options};
915962
ApplyArgsManOptions(*Assert(m_node.args), assemble_options);
916-
return BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key);
963+
return std::make_unique<BlockTemplateImpl>(BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key));
917964
}
918965

919966
NodeContext* context() override { return &m_node; }

src/rpc/mining.cpp

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
#include <memory>
4646
#include <stdint.h>
4747

48-
using node::BlockAssembler;
49-
using node::CBlockTemplate;
48+
using interfaces::BlockTemplate;
5049
using interfaces::Mining;
50+
using node::BlockAssembler;
5151
using node::NodeContext;
5252
using node::RegenerateCommitments;
5353
using node::UpdateTime;
@@ -130,7 +130,7 @@ static RPCHelpMan getnetworkhashps()
130130
};
131131
}
132132

133-
static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& block, uint64_t& max_tries, std::shared_ptr<const CBlock>& block_out, bool process_new_block)
133+
static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock&& block, uint64_t& max_tries, std::shared_ptr<const CBlock>& block_out, bool process_new_block)
134134
{
135135
block_out.reset();
136136
block.hashMerkleRoot = BlockMerkleRoot(block);
@@ -146,7 +146,7 @@ static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& bl
146146
return true;
147147
}
148148

149-
block_out = std::make_shared<const CBlock>(block);
149+
block_out = std::make_shared<const CBlock>(std::move(block));
150150

151151
if (!process_new_block) return true;
152152

@@ -161,12 +161,11 @@ static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const
161161
{
162162
UniValue blockHashes(UniValue::VARR);
163163
while (nGenerate > 0 && !chainman.m_interrupt) {
164-
std::unique_ptr<CBlockTemplate> pblocktemplate(miner.createNewBlock(coinbase_script));
165-
if (!pblocktemplate.get())
166-
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
164+
std::unique_ptr<BlockTemplate> block_template(miner.createNewBlock(coinbase_script));
165+
CHECK_NONFATAL(block_template);
167166

168167
std::shared_ptr<const CBlock> block_out;
169-
if (!GenerateBlock(chainman, miner, pblocktemplate->block, nMaxTries, block_out, /*process_new_block=*/true)) {
168+
if (!GenerateBlock(chainman, miner, block_template->getBlock(), nMaxTries, block_out, /*process_new_block=*/true)) {
170169
break;
171170
}
172171

@@ -371,11 +370,10 @@ static RPCHelpMan generateblock()
371370

372371
ChainstateManager& chainman = EnsureChainman(node);
373372
{
374-
std::unique_ptr<CBlockTemplate> blocktemplate{miner.createNewBlock(coinbase_script, {.use_mempool = false})};
375-
if (!blocktemplate) {
376-
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
377-
}
378-
block = blocktemplate->block;
373+
std::unique_ptr<BlockTemplate> block_template{miner.createNewBlock(coinbase_script, {.use_mempool = false})};
374+
CHECK_NONFATAL(block_template);
375+
376+
block = block_template->getBlock();
379377
}
380378

381379
CHECK_NONFATAL(block.vtx.size() == 1);
@@ -394,7 +392,7 @@ static RPCHelpMan generateblock()
394392
std::shared_ptr<const CBlock> block_out;
395393
uint64_t max_tries{DEFAULT_MAX_TRIES};
396394

397-
if (!GenerateBlock(chainman, miner, block, max_tries, block_out, process_new_block) || !block_out) {
395+
if (!GenerateBlock(chainman, miner, std::move(block), max_tries, block_out, process_new_block) || !block_out) {
398396
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
399397
}
400398

@@ -800,7 +798,7 @@ static RPCHelpMan getblocktemplate()
800798
// Update block
801799
static CBlockIndex* pindexPrev;
802800
static int64_t time_start;
803-
static std::unique_ptr<CBlockTemplate> pblocktemplate;
801+
static std::unique_ptr<BlockTemplate> block_template;
804802
if (!pindexPrev || pindexPrev->GetBlockHash() != tip ||
805803
(miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
806804
{
@@ -814,20 +812,19 @@ static RPCHelpMan getblocktemplate()
814812

815813
// Create new block
816814
CScript scriptDummy = CScript() << OP_TRUE;
817-
pblocktemplate = miner.createNewBlock(scriptDummy);
818-
if (!pblocktemplate) {
819-
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
820-
}
815+
block_template = miner.createNewBlock(scriptDummy);
816+
CHECK_NONFATAL(block_template);
817+
821818

822819
// Need to update only after we know createNewBlock succeeded
823820
pindexPrev = pindexPrevNew;
824821
}
825822
CHECK_NONFATAL(pindexPrev);
826-
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
823+
CBlock block{block_template->getBlock()};
827824

828825
// Update nTime
829-
UpdateTime(pblock, consensusParams, pindexPrev);
830-
pblock->nNonce = 0;
826+
UpdateTime(&block, consensusParams, pindexPrev);
827+
block.nNonce = 0;
831828

832829
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
833830
const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT);
@@ -836,8 +833,11 @@ static RPCHelpMan getblocktemplate()
836833

837834
UniValue transactions(UniValue::VARR);
838835
std::map<uint256, int64_t> setTxIndex;
836+
std::vector<CAmount> tx_fees{block_template->getTxFees()};
837+
std::vector<CAmount> tx_sigops{block_template->getTxSigops()};
838+
839839
int i = 0;
840-
for (const auto& it : pblock->vtx) {
840+
for (const auto& it : block.vtx) {
841841
const CTransaction& tx = *it;
842842
uint256 txHash = tx.GetHash();
843843
setTxIndex[txHash] = i++;
@@ -860,8 +860,8 @@ static RPCHelpMan getblocktemplate()
860860
entry.pushKV("depends", std::move(deps));
861861

862862
int index_in_template = i - 1;
863-
entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]);
864-
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
863+
entry.pushKV("fee", tx_fees.at(index_in_template));
864+
int64_t nTxSigOps{tx_sigops.at(index_in_template)};
865865
if (fPreSegWit) {
866866
CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
867867
nTxSigOps /= WITNESS_SCALE_FACTOR;
@@ -874,7 +874,7 @@ static RPCHelpMan getblocktemplate()
874874

875875
UniValue aux(UniValue::VOBJ);
876876

877-
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
877+
arith_uint256 hashTarget = arith_uint256().SetCompact(block.nBits);
878878

879879
UniValue aMutable(UniValue::VARR);
880880
aMutable.push_back("time");
@@ -904,7 +904,7 @@ static RPCHelpMan getblocktemplate()
904904
break;
905905
case ThresholdState::LOCKED_IN:
906906
// Ensure bit is set in block version
907-
pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
907+
block.nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
908908
[[fallthrough]];
909909
case ThresholdState::STARTED:
910910
{
@@ -913,7 +913,7 @@ static RPCHelpMan getblocktemplate()
913913
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
914914
if (!vbinfo.gbt_force) {
915915
// If the client doesn't support this, don't indicate it in the [default] version
916-
pblock->nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
916+
block.nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
917917
}
918918
}
919919
break;
@@ -933,15 +933,15 @@ static RPCHelpMan getblocktemplate()
933933
}
934934
}
935935
}
936-
result.pushKV("version", pblock->nVersion);
936+
result.pushKV("version", block.nVersion);
937937
result.pushKV("rules", std::move(aRules));
938938
result.pushKV("vbavailable", std::move(vbavailable));
939939
result.pushKV("vbrequired", int(0));
940940

941-
result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
941+
result.pushKV("previousblockhash", block.hashPrevBlock.GetHex());
942942
result.pushKV("transactions", std::move(transactions));
943943
result.pushKV("coinbaseaux", std::move(aux));
944-
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
944+
result.pushKV("coinbasevalue", (int64_t)block.vtx[0]->vout[0].nValue);
945945
result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast));
946946
result.pushKV("target", hashTarget.GetHex());
947947
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
@@ -960,16 +960,16 @@ static RPCHelpMan getblocktemplate()
960960
if (!fPreSegWit) {
961961
result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT);
962962
}
963-
result.pushKV("curtime", pblock->GetBlockTime());
964-
result.pushKV("bits", strprintf("%08x", pblock->nBits));
963+
result.pushKV("curtime", block.GetBlockTime());
964+
result.pushKV("bits", strprintf("%08x", block.nBits));
965965
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
966966

967967
if (consensusParams.signet_blocks) {
968968
result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge));
969969
}
970970

971-
if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
972-
result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment));
971+
if (!block_template->getCoinbaseCommitment().empty()) {
972+
result.pushKV("default_witness_commitment", HexStr(block_template->getCoinbaseCommitment()));
973973
}
974974

975975
return result;

0 commit comments

Comments
 (0)