From 8ecb6816781c7c7f423b501cbb2de3abd7250119 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 7 Jun 2024 11:22:44 +0200 Subject: [PATCH 1/9] Introduce Mining interface Start out with a single method isTestChain() that's used by the getblocktemplate RPC. --- doc/developer-notes.md | 5 +++-- src/Makefile.am | 1 + src/init.cpp | 2 ++ src/init/bitcoin-node.cpp | 1 + src/init/bitcoin-qt.cpp | 2 ++ src/init/bitcoind.cpp | 2 ++ src/interfaces/init.h | 2 ++ src/interfaces/mining.h | 35 +++++++++++++++++++++++++++++++++++ src/node/context.cpp | 1 + src/node/context.h | 2 ++ src/node/interfaces.cpp | 18 ++++++++++++++++++ src/rpc/mining.cpp | 5 ++++- src/rpc/server_util.cpp | 8 ++++++++ src/rpc/server_util.h | 4 ++++ 14 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 src/interfaces/mining.h diff --git a/doc/developer-notes.md b/doc/developer-notes.md index eb2bb41aa41fd..d9d5b392c5e93 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -1457,8 +1457,9 @@ independent (node, wallet, GUI), are defined in there are [`interfaces::Chain`](../src/interfaces/chain.h), used by wallet to access the node's latest chain state, [`interfaces::Node`](../src/interfaces/node.h), used by the GUI to control the -node, and [`interfaces::Wallet`](../src/interfaces/wallet.h), used by the GUI -to control an individual wallet. There are also more specialized interface +node, [`interfaces::Wallet`](../src/interfaces/wallet.h), used by the GUI +to control an individual wallet and [`interfaces::Mining`](../src/interfaces/mining.h), +used by RPC to generate block templates. There are also more specialized interface types like [`interfaces::Handler`](../src/interfaces/handler.h) [`interfaces::ChainClient`](../src/interfaces/chain.h) passed to and from various interface methods. diff --git a/src/Makefile.am b/src/Makefile.am index 4a1973aa87620..f95a6fa1232e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -177,6 +177,7 @@ BITCOIN_CORE_H = \ interfaces/handler.h \ interfaces/init.h \ interfaces/ipc.h \ + interfaces/mining.h \ interfaces/node.h \ interfaces/wallet.h \ kernel/blockmanager_opts.h \ diff --git a/src/init.cpp b/src/init.cpp index 5bb82dc320945..0c57dfea7f042 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1117,6 +1118,7 @@ bool AppInitLockDataDirectory() bool AppInitInterfaces(NodeContext& node) { node.chain = node.init->makeChain(); + node.mining = node.init->makeMining(); return true; } diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp index 97b8dc1161442..00a3822791174 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -30,6 +30,7 @@ class BitcoinNodeInit : public interfaces::Init } std::unique_ptr makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr makeChain() override { return interfaces::MakeChain(m_node); } + std::unique_ptr makeMining() override { return interfaces::MakeMining(m_node); } std::unique_ptr makeWalletLoader(interfaces::Chain& chain) override { return MakeWalletLoader(chain, *Assert(m_node.args)); diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp index 3003a8fde106d..5209c72973176 100644 --- a/src/init/bitcoin-qt.cpp +++ b/src/init/bitcoin-qt.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ class BitcoinQtInit : public interfaces::Init } std::unique_ptr makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr makeChain() override { return interfaces::MakeChain(m_node); } + std::unique_ptr makeMining() override { return interfaces::MakeMining(m_node); } std::unique_ptr makeWalletLoader(interfaces::Chain& chain) override { return MakeWalletLoader(chain, *Assert(m_node.args)); diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp index b5df764017233..48be8831d25df 100644 --- a/src/init/bitcoind.cpp +++ b/src/init/bitcoind.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ class BitcoindInit : public interfaces::Init } std::unique_ptr makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr makeChain() override { return interfaces::MakeChain(m_node); } + std::unique_ptr makeMining() override { return interfaces::MakeMining(m_node); } std::unique_ptr makeWalletLoader(interfaces::Chain& chain) override { return MakeWalletLoader(chain, *Assert(m_node.args)); diff --git a/src/interfaces/init.h b/src/interfaces/init.h index addc45aa2668f..094ead399dc67 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ class Init virtual ~Init() = default; virtual std::unique_ptr makeNode() { return nullptr; } virtual std::unique_ptr makeChain() { return nullptr; } + virtual std::unique_ptr makeMining() { return nullptr; } virtual std::unique_ptr makeWalletLoader(Chain& chain) { return nullptr; } virtual std::unique_ptr makeEcho() { return nullptr; } virtual Ipc* ipc() { return nullptr; } diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h new file mode 100644 index 0000000000000..afcd8d1cda51f --- /dev/null +++ b/src/interfaces/mining.h @@ -0,0 +1,35 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_INTERFACES_MINING_H +#define BITCOIN_INTERFACES_MINING_H + +namespace node { +struct NodeContext; +} // namespace node + +namespace interfaces { + +//! Interface giving clients (RPC, Stratum v2 Template Provider in the future) +//! ability to create block templates. + +class Mining +{ +public: + virtual ~Mining() {} + + //! If this chain is exclusively used for testing + virtual bool isTestChain() = 0; + + //! Get internal node context. Useful for RPC and testing, + //! but not accessible across processes. + virtual node::NodeContext* context() { return nullptr; } +}; + +//! Return implementation of Mining interface. +std::unique_ptr MakeMining(node::NodeContext& node); + +} // namespace interfaces + +#endif // BITCOIN_INTERFACES_MINING_H diff --git a/src/node/context.cpp b/src/node/context.cpp index da05fde6eeade..75dfaee866d8a 100644 --- a/src/node/context.cpp +++ b/src/node/context.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/src/node/context.h b/src/node/context.h index 77838ea99b9bf..a664fad80be87 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -27,6 +27,7 @@ class PeerManager; namespace interfaces { class Chain; class ChainClient; +class Mining; class Init; class WalletLoader; } // namespace interfaces @@ -74,6 +75,7 @@ struct NodeContext { std::vector> chain_clients; //! Reference to chain client that should used to load or create wallets //! opened by the gui. + std::unique_ptr mining; interfaces::WalletLoader* wallet_loader{nullptr}; std::unique_ptr scheduler; std::function rpc_interruption_point = [] {}; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 2b36f4ceae38b..e44cb51873965 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ using interfaces::Chain; using interfaces::FoundBlock; using interfaces::Handler; using interfaces::MakeSignalHandler; +using interfaces::Mining; using interfaces::Node; using interfaces::WalletLoader; using util::Join; @@ -831,10 +833,26 @@ class ChainImpl : public Chain ValidationSignals& validation_signals() { return *Assert(m_node.validation_signals); } NodeContext& m_node; }; + +class MinerImpl : public Mining +{ +public: + explicit MinerImpl(NodeContext& node) : m_node(node) {} + + bool isTestChain() override + { + return chainman().GetParams().IsTestChain(); + } + + NodeContext* context() override { return &m_node; } + ChainstateManager& chainman() { return *Assert(m_node.chainman); } + NodeContext& m_node; +}; } // namespace } // namespace node namespace interfaces { std::unique_ptr MakeNode(node::NodeContext& context) { return std::make_unique(context); } std::unique_ptr MakeChain(node::NodeContext& context) { return std::make_unique(context); } +std::unique_ptr MakeMining(node::NodeContext& context) { return std::make_unique(context); } } // namespace interfaces diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 0f6853ef37dd8..0e9d4829d2164 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ using node::BlockAssembler; using node::CBlockTemplate; +using interfaces::Mining; using node::NodeContext; using node::RegenerateCommitments; using node::UpdateTime; @@ -724,7 +726,8 @@ static RPCHelpMan getblocktemplate() if (strMode != "template") throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - if (!chainman.GetParams().IsTestChain()) { + Mining& miner = EnsureMining(node); + if (!miner.isTestChain()) { const CConnman& connman = EnsureConnman(node); if (connman.GetNodeCount(ConnectionDirection::Both) == 0) { throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp index efd4a43c288c2..0387cbb8e25ac 100644 --- a/src/rpc/server_util.cpp +++ b/src/rpc/server_util.cpp @@ -101,6 +101,14 @@ CConnman& EnsureConnman(const NodeContext& node) return *node.connman; } +interfaces::Mining& EnsureMining(const NodeContext& node) +{ + if (!node.mining) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node miner not found"); + } + return *node.mining; +} + PeerManager& EnsurePeerman(const NodeContext& node) { if (!node.peerman) { diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h index a4a53166b4b61..1e6fb7e6a666e 100644 --- a/src/rpc/server_util.h +++ b/src/rpc/server_util.h @@ -18,6 +18,9 @@ class BanMan; namespace node { struct NodeContext; } // namespace node +namespace interfaces { +class Mining; +} // namespace interfaces node::NodeContext& EnsureAnyNodeContext(const std::any& context); CTxMemPool& EnsureMemPool(const node::NodeContext& node); @@ -31,6 +34,7 @@ ChainstateManager& EnsureAnyChainman(const std::any& context); CBlockPolicyEstimator& EnsureFeeEstimator(const node::NodeContext& node); CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); CConnman& EnsureConnman(const node::NodeContext& node); +interfaces::Mining& EnsureMining(const node::NodeContext& node); PeerManager& EnsurePeerman(const node::NodeContext& node); AddrMan& EnsureAddrman(const node::NodeContext& node); AddrMan& EnsureAnyAddrman(const std::any& context); From d8a3496b5ad27bea4c79ea0344f595cc1b95f0d3 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 10 Jun 2024 17:58:13 +0200 Subject: [PATCH 2/9] rpc: call TestBlockValidity via miner interface --- src/interfaces/mining.h | 15 +++++++++++++++ src/node/interfaces.cpp | 7 +++++++ src/rpc/mining.cpp | 14 ++++++++------ test/functional/rpc_generate.py | 2 +- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index afcd8d1cda51f..603d8475d383e 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -9,6 +9,9 @@ namespace node { struct NodeContext; } // namespace node +class BlockValidationState; +class CBlock; + namespace interfaces { //! Interface giving clients (RPC, Stratum v2 Template Provider in the future) @@ -22,6 +25,18 @@ class Mining //! If this chain is exclusively used for testing virtual bool isTestChain() = 0; + /** + * Check a block is completely valid from start to finish. + * Only works on top of our current best block. + * Does not check proof-of-work. + * + * @param[out] state details of why a block failed to validate + * @param[in] block the block to validate + * @param[in] check_merkle_root call CheckMerkleRoot() + * @returns false if any of the checks fail + */ + virtual bool testBlockValidity(BlockValidationState& state, const CBlock& block, bool check_merkle_root = true) = 0; + //! Get internal node context. Useful for RPC and testing, //! but not accessible across processes. virtual node::NodeContext* context() { return nullptr; } diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index e44cb51873965..a528afcff0604 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -844,6 +845,12 @@ class MinerImpl : public Mining return chainman().GetParams().IsTestChain(); } + bool testBlockValidity(BlockValidationState& state, const CBlock& block, bool check_merkle_root) override + { + LOCK(::cs_main); + return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, chainman().ActiveChain().Tip(), /*fCheckPOW=*/false, check_merkle_root); + } + NodeContext* context() override { return &m_node; } ChainstateManager& chainman() { return *Assert(m_node.chainman); } NodeContext& m_node; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 0e9d4829d2164..a9968b6e5fa74 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -339,6 +339,7 @@ static RPCHelpMan generateblock() } NodeContext& node = EnsureAnyNodeContext(request.context); + Mining& miner = EnsureMining(node); const CTxMemPool& mempool = EnsureMemPool(node); std::vector txs; @@ -389,8 +390,8 @@ static RPCHelpMan generateblock() LOCK(cs_main); BlockValidationState state; - if (!TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { - throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString())); + if (!miner.testBlockValidity(state, block, /*check_merkle_root=*/false)) { + throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("testBlockValidity failed: %s", state.ToString())); } } @@ -664,6 +665,7 @@ static RPCHelpMan getblocktemplate() { NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); + Mining& miner = EnsureMining(node); LOCK(cs_main); std::string strMode = "template"; @@ -706,11 +708,12 @@ static RPCHelpMan getblocktemplate() } CBlockIndex* const pindexPrev = active_chain.Tip(); - // TestBlockValidity only supports blocks built on the current Tip - if (block.hashPrevBlock != pindexPrev->GetBlockHash()) + // testBlockValidity only supports blocks built on the current Tip + if (block.hashPrevBlock != pindexPrev->GetBlockHash()) { return "inconclusive-not-best-prevblk"; + } BlockValidationState state; - TestBlockValidity(state, chainman.GetParams(), active_chainstate, block, pindexPrev, false, true); + miner.testBlockValidity(state, block); return BIP22ValidationResult(state); } @@ -726,7 +729,6 @@ static RPCHelpMan getblocktemplate() if (strMode != "template") throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - Mining& miner = EnsureMining(node); if (!miner.isTestChain()) { const CConnman& connman = EnsureConnman(node); if (connman.GetNodeCount(ConnectionDirection::Both) == 0) { diff --git a/test/functional/rpc_generate.py b/test/functional/rpc_generate.py index 20f62079fd683..3e250925e78bb 100755 --- a/test/functional/rpc_generate.py +++ b/test/functional/rpc_generate.py @@ -87,7 +87,7 @@ def test_generateblock(self): txid1 = miniwallet.send_self_transfer(from_node=node)['txid'] utxo1 = miniwallet.get_utxo(txid=txid1) rawtx2 = miniwallet.create_self_transfer(utxo_to_spend=utxo1)['hex'] - assert_raises_rpc_error(-25, 'TestBlockValidity failed: bad-txns-inputs-missingorspent', self.generateblock, node, address, [rawtx2, txid1]) + assert_raises_rpc_error(-25, 'testBlockValidity failed: bad-txns-inputs-missingorspent', self.generateblock, node, address, [rawtx2, txid1]) self.log.info('Fail to generate block with txid not in mempool') missing_txid = '0000000000000000000000000000000000000000000000000000000000000000' From 404b01c436122b951e9e06ed26d79dba4651685e Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Thu, 30 May 2024 15:55:02 +0200 Subject: [PATCH 3/9] rpc: getblocktemplate getTipHash() via Miner interface --- src/interfaces/mining.h | 5 +++++ src/node/interfaces.cpp | 8 ++++++++ src/rpc/mining.cpp | 12 +++++------- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 603d8475d383e..8ed273252baf9 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_INTERFACES_MINING_H #define BITCOIN_INTERFACES_MINING_H +#include + namespace node { struct NodeContext; } // namespace node @@ -25,6 +27,9 @@ class Mining //! If this chain is exclusively used for testing virtual bool isTestChain() = 0; + //! Returns the hash for the tip of this chain, 0 if none + virtual uint256 getTipHash() = 0; + /** * Check a block is completely valid from start to finish. * Only works on top of our current best block. diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index a528afcff0604..bd200e8d2942e 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -845,6 +845,14 @@ class MinerImpl : public Mining return chainman().GetParams().IsTestChain(); } + uint256 getTipHash() override + { + LOCK(::cs_main); + CBlockIndex* tip{chainman().ActiveChain().Tip()}; + if (!tip) return uint256{0}; + return tip->GetBlockHash(); + } + bool testBlockValidity(BlockValidationState& state, const CBlock& block, bool check_merkle_root) override { LOCK(::cs_main); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index a9968b6e5fa74..9762d7648d663 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -672,7 +672,6 @@ static RPCHelpMan getblocktemplate() UniValue lpval = NullUniValue; std::set setClientRules; Chainstate& active_chainstate = chainman.ActiveChainstate(); - CChain& active_chain = active_chainstate.m_chain; if (!request.params[0].isNull()) { const UniValue& oparam = request.params[0].get_obj(); @@ -707,9 +706,8 @@ static RPCHelpMan getblocktemplate() return "duplicate-inconclusive"; } - CBlockIndex* const pindexPrev = active_chain.Tip(); // testBlockValidity only supports blocks built on the current Tip - if (block.hashPrevBlock != pindexPrev->GetBlockHash()) { + if (block.hashPrevBlock != miner.getTipHash()) { return "inconclusive-not-best-prevblk"; } BlockValidationState state; @@ -761,7 +759,7 @@ static RPCHelpMan getblocktemplate() else { // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier - hashWatchedChain = active_chain.Tip()->GetBlockHash(); + hashWatchedChain = miner.getTipHash(); nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; } @@ -806,7 +804,7 @@ static RPCHelpMan getblocktemplate() static CBlockIndex* pindexPrev; static int64_t time_start; static std::unique_ptr pblocktemplate; - if (pindexPrev != active_chain.Tip() || + if (!pindexPrev || pindexPrev->GetBlockHash() != miner.getTipHash() || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on @@ -814,7 +812,7 @@ static RPCHelpMan getblocktemplate() // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); - CBlockIndex* pindexPrevNew = active_chain.Tip(); + CBlockIndex* pindexPrevNew = chainman.m_blockman.LookupBlockIndex(miner.getTipHash()); time_start = GetTime(); // Create new block @@ -946,7 +944,7 @@ static RPCHelpMan getblocktemplate() 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("longpollid", active_chain.Tip()->GetBlockHash().GetHex() + ToString(nTransactionsUpdatedLast)); + result.pushKV("longpollid", miner.getTipHash().GetHex() + ToString(nTransactionsUpdatedLast)); result.pushKV("target", hashTarget.GetHex()); result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); result.pushKV("mutable", std::move(aMutable)); From 4bf2e361da1964f7c278b4939967a0e5afde20b0 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 10 Jun 2024 17:03:33 +0200 Subject: [PATCH 4/9] rpc: call CreateNewBlock via miner interface --- src/interfaces/mining.h | 11 +++++++++++ src/node/interfaces.cpp | 7 +++++++ src/rpc/mining.cpp | 24 ++++++++++++------------ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 8ed273252baf9..9494fd75bf212 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -8,11 +8,13 @@ #include namespace node { +struct CBlockTemplate; struct NodeContext; } // namespace node class BlockValidationState; class CBlock; +class CScript; namespace interfaces { @@ -30,6 +32,15 @@ class Mining //! Returns the hash for the tip of this chain, 0 if none virtual uint256 getTipHash() = 0; + /** + * Construct a new block template + * + * @param[in] script_pub_key the coinbase output + * @param[in] use_mempool set false to omit mempool transactions + * @returns a block template + */ + virtual std::unique_ptr createNewBlock(const CScript& script_pub_key, bool use_mempool = true) = 0; + /** * Check a block is completely valid from start to finish. * Only works on top of our current best block. diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index bd200e8d2942e..215dbf5e17016 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -859,6 +860,12 @@ class MinerImpl : public Mining return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, chainman().ActiveChain().Tip(), /*fCheckPOW=*/false, check_merkle_root); } + std::unique_ptr createNewBlock(const CScript& script_pub_key, bool use_mempool) override + { + LOCK(::cs_main); + return BlockAssembler{chainman().ActiveChainstate(), use_mempool ? context()->mempool.get() : nullptr}.CreateNewBlock(script_pub_key); + } + NodeContext* context() override { return &m_node; } ChainstateManager& chainman() { return *Assert(m_node.chainman); } NodeContext& m_node; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9762d7648d663..a65413de1edfb 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -156,11 +156,11 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& return true; } -static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries) +static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries) { UniValue blockHashes(UniValue::VARR); while (nGenerate > 0 && !chainman.m_interrupt) { - std::unique_ptr pblocktemplate(BlockAssembler{chainman.ActiveChainstate(), &mempool}.CreateNewBlock(coinbase_script)); + std::unique_ptr pblocktemplate(miner.createNewBlock(coinbase_script)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); @@ -241,10 +241,10 @@ static RPCHelpMan generatetodescriptor() } NodeContext& node = EnsureAnyNodeContext(request.context); - const CTxMemPool& mempool = EnsureMemPool(node); + Mining& miner = EnsureMining(node); ChainstateManager& chainman = EnsureChainman(node); - return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries); + return generateBlocks(chainman, miner, coinbase_script, num_blocks, max_tries); }, }; } @@ -287,12 +287,12 @@ static RPCHelpMan generatetoaddress() } NodeContext& node = EnsureAnyNodeContext(request.context); - const CTxMemPool& mempool = EnsureMemPool(node); + Mining& miner = EnsureMining(node); ChainstateManager& chainman = EnsureChainman(node); CScript coinbase_script = GetScriptForDestination(destination); - return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries); + return generateBlocks(chainman, miner, coinbase_script, num_blocks, max_tries); }, }; } @@ -373,7 +373,7 @@ static RPCHelpMan generateblock() { LOCK(cs_main); - std::unique_ptr blocktemplate(BlockAssembler{chainman.ActiveChainstate(), nullptr}.CreateNewBlock(coinbase_script)); + std::unique_ptr blocktemplate{miner.createNewBlock(coinbase_script, /*use_mempool=*/false)}; if (!blocktemplate) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); } @@ -671,7 +671,6 @@ static RPCHelpMan getblocktemplate() std::string strMode = "template"; UniValue lpval = NullUniValue; std::set setClientRules; - Chainstate& active_chainstate = chainman.ActiveChainstate(); if (!request.params[0].isNull()) { const UniValue& oparam = request.params[0].get_obj(); @@ -810,18 +809,19 @@ static RPCHelpMan getblocktemplate() // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = nullptr; - // Store the pindexBest used before CreateNewBlock, to avoid races + // Store the pindexBest used before createNewBlock, to avoid races nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrevNew = chainman.m_blockman.LookupBlockIndex(miner.getTipHash()); time_start = GetTime(); // Create new block CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler{active_chainstate, &mempool}.CreateNewBlock(scriptDummy); - if (!pblocktemplate) + pblocktemplate = miner.createNewBlock(scriptDummy); + if (!pblocktemplate) { 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; } CHECK_NONFATAL(pindexPrev); From 64ebb0f97178687517c2060bf6b9931064607888 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 10 Jun 2024 17:27:12 +0200 Subject: [PATCH 5/9] Always pass options to BlockAssembler constructor This makes the options argument for BlockAssembler constructor mandatory, dropping implicit use of ArgsManager. The caller i.e. the Mining interface implementation now handles this. In a future Stratum v2 change the Options object needs to be mofified after arguments have been processed. Specifically the pool communicates how many extra bytes it needs for its own outputs (payouts, extra commitments, etc). This will need to be substracted from what the user set as -blockmaxweight. Such a change can be implemented in createNewBlock, after ApplyArgsManOptions. --- src/node/interfaces.cpp | 6 +++++- src/node/miner.cpp | 9 --------- src/node/miner.h | 1 - src/test/blockfilter_index_tests.cpp | 3 ++- src/test/peerman_tests.cpp | 3 ++- src/test/util/setup_common.cpp | 3 ++- src/test/validation_block_tests.cpp | 6 ++++-- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 215dbf5e17016..356a01b286fd3 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -75,6 +75,7 @@ using interfaces::MakeSignalHandler; using interfaces::Mining; using interfaces::Node; using interfaces::WalletLoader; +using node::BlockAssembler; using util::Join; namespace node { @@ -862,8 +863,11 @@ class MinerImpl : public Mining std::unique_ptr createNewBlock(const CScript& script_pub_key, bool use_mempool) override { + BlockAssembler::Options options; + ApplyArgsManOptions(gArgs, options); + LOCK(::cs_main); - return BlockAssembler{chainman().ActiveChainstate(), use_mempool ? context()->mempool.get() : nullptr}.CreateNewBlock(script_pub_key); + return BlockAssembler{chainman().ActiveChainstate(), use_mempool ? context()->mempool.get() : nullptr, options}.CreateNewBlock(script_pub_key); } NodeContext* context() override { return &m_node; } diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 87f40e993f2e5..03c6d74deba72 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -80,15 +80,6 @@ void ApplyArgsManOptions(const ArgsManager& args, BlockAssembler::Options& optio if (const auto parsed{ParseMoney(*blockmintxfee)}) options.blockMinFeeRate = CFeeRate{*parsed}; } } -static BlockAssembler::Options ConfiguredOptions() -{ - BlockAssembler::Options options; - ApplyArgsManOptions(gArgs, options); - return options; -} - -BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool) - : BlockAssembler(chainstate, mempool, ConfiguredOptions()) {} void BlockAssembler::resetBlock() { diff --git a/src/node/miner.h b/src/node/miner.h index 06a917228dccc..c3178a7532b45 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -161,7 +161,6 @@ class BlockAssembler bool test_block_validity{true}; }; - explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool); explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index d44d84af93239..067a32d6a40e4 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -67,7 +67,8 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev, const std::vector& txns, const CScript& scriptPubKey) { - std::unique_ptr pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get()}.CreateNewBlock(scriptPubKey); + BlockAssembler::Options options; + std::unique_ptr pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock(scriptPubKey); CBlock& block = pblocktemplate->block; block.hashPrevBlock = prev->GetBlockHash(); block.nTime = prev->nTime + 1; diff --git a/src/test/peerman_tests.cpp b/src/test/peerman_tests.cpp index 397b1d8c2d522..6de373eef2338 100644 --- a/src/test/peerman_tests.cpp +++ b/src/test/peerman_tests.cpp @@ -20,7 +20,8 @@ static void mineBlock(const node::NodeContext& node, std::chrono::seconds block_ { auto curr_time = GetTime(); SetMockTime(block_time); // update time so the block is created with it - CBlock block = node::BlockAssembler{node.chainman->ActiveChainstate(), nullptr}.CreateNewBlock(CScript() << OP_TRUE)->block; + node::BlockAssembler::Options options; + CBlock block = node::BlockAssembler{node.chainman->ActiveChainstate(), nullptr, options}.CreateNewBlock(CScript() << OP_TRUE)->block; while (!CheckProofOfWork(block.GetHash(), block.nBits, node.chainman->GetConsensus())) ++block.nNonce; block.fChecked = true; // little speedup SetMockTime(curr_time); // process block at current time diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 79e33eacec07d..cc7b2d65463f6 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -374,7 +374,8 @@ CBlock TestChain100Setup::CreateBlock( const CScript& scriptPubKey, Chainstate& chainstate) { - CBlock block = BlockAssembler{chainstate, nullptr}.CreateNewBlock(scriptPubKey)->block; + BlockAssembler::Options options; + CBlock block = BlockAssembler{chainstate, nullptr, options}.CreateNewBlock(scriptPubKey)->block; Assert(block.vtx.size() == 1); for (const CMutableTransaction& tx : txns) { diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 69f4e305ab611..588ac60498c1f 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -65,7 +65,8 @@ std::shared_ptr MinerTestingSetup::Block(const uint256& prev_hash) static int i = 0; static uint64_t time = Params().GenesisBlock().nTime; - auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get()}.CreateNewBlock(CScript{} << i++ << OP_TRUE); + BlockAssembler::Options options; + auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock(CScript{} << i++ << OP_TRUE); auto pblock = std::make_shared(ptemplate->block); pblock->hashPrevBlock = prev_hash; pblock->nTime = ++time; @@ -329,7 +330,8 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index) LOCK(Assert(m_node.chainman)->GetMutex()); CScript pubKey; pubKey << 1 << OP_TRUE; - auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get()}.CreateNewBlock(pubKey); + BlockAssembler::Options options; + auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock(pubKey); CBlock pblock = ptemplate->block; CTxOut witness; From 9e228351e761d8d24413bbc4ac1610b4f3dec2bf Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Thu, 30 May 2024 16:46:29 +0200 Subject: [PATCH 6/9] rpc: getTransactionsUpdated via miner interface --- src/interfaces/mining.h | 4 ++++ src/node/interfaces.cpp | 5 +++++ src/rpc/mining.cpp | 7 +++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 9494fd75bf212..3ebc48dffa5ff 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -41,6 +41,10 @@ class Mining */ virtual std::unique_ptr createNewBlock(const CScript& script_pub_key, bool use_mempool = true) = 0; + //! Return the number of transaction updates in the mempool, + //! used to decide whether to make a new block template. + virtual unsigned int getTransactionsUpdated() = 0; + /** * Check a block is completely valid from start to finish. * Only works on top of our current best block. diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 356a01b286fd3..91ee8585971a1 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -855,6 +855,11 @@ class MinerImpl : public Mining return tip->GetBlockHash(); } + unsigned int getTransactionsUpdated() override + { + return context()->mempool->GetTransactionsUpdated(); + } + bool testBlockValidity(BlockValidationState& state, const CBlock& block, bool check_merkle_root) override { LOCK(::cs_main); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index a65413de1edfb..3cca6a53fad47 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -738,7 +738,6 @@ static RPCHelpMan getblocktemplate() } static unsigned int nTransactionsUpdatedLast; - const CTxMemPool& mempool = EnsureMemPool(node); if (!lpval.isNull()) { @@ -774,7 +773,7 @@ static RPCHelpMan getblocktemplate() { // Timeout: Check transactions for update // without holding the mempool lock to avoid deadlocks - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) + if (miner.getTransactionsUpdated() != nTransactionsUpdatedLastLP) break; checktxtime += std::chrono::seconds(10); } @@ -804,13 +803,13 @@ static RPCHelpMan getblocktemplate() static int64_t time_start; static std::unique_ptr pblocktemplate; if (!pindexPrev || pindexPrev->GetBlockHash() != miner.getTipHash() || - (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5)) + (miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = nullptr; // Store the pindexBest used before createNewBlock, to avoid races - nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + nTransactionsUpdatedLast = miner.getTransactionsUpdated(); CBlockIndex* pindexPrevNew = chainman.m_blockman.LookupBlockIndex(miner.getTipHash()); time_start = GetTime(); From 7b4d3249ced93ec5986500e43b324005ed89502f Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Thu, 30 May 2024 17:06:59 +0200 Subject: [PATCH 7/9] rpc: call processNewBlock via miner interface --- src/interfaces/mining.h | 8 ++++++++ src/node/interfaces.cpp | 5 +++++ src/rpc/mining.cpp | 13 ++++++++----- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 3ebc48dffa5ff..cd092397f3ea7 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -40,6 +40,14 @@ class Mining * @returns a block template */ virtual std::unique_ptr createNewBlock(const CScript& script_pub_key, bool use_mempool = true) = 0; + /** + * Processes new block. A valid new block is automatically relayed to peers. + * + * @param[in] block The block we want to process. + * @param[out] new_block A boolean which is set to indicate if the block was first received via this call + * @returns If the block was processed, independently of block validity + */ + virtual bool processNewBlock(const std::shared_ptr& block, bool* new_block) = 0; //! Return the number of transaction updates in the mempool, //! used to decide whether to make a new block template. diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 91ee8585971a1..2633ff1b98876 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -855,6 +855,11 @@ class MinerImpl : public Mining return tip->GetBlockHash(); } + bool processNewBlock(const std::shared_ptr& block, bool* new_block) override + { + return chainman().ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/new_block); + } + unsigned int getTransactionsUpdated() override { return context()->mempool->GetTransactionsUpdated(); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 3cca6a53fad47..e404cea90ffc7 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -129,7 +129,7 @@ static RPCHelpMan getnetworkhashps() }; } -static bool GenerateBlock(ChainstateManager& chainman, 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); @@ -149,7 +149,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& if (!process_new_block) return true; - if (!chainman.ProcessNewBlock(block_out, /*force_processing=*/true, /*min_pow_checked=*/true, nullptr)) { + if (!miner.processNewBlock(block_out, nullptr)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); } @@ -165,7 +165,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); std::shared_ptr block_out; - if (!GenerateBlock(chainman, pblocktemplate->block, nMaxTries, block_out, /*process_new_block=*/true)) { + if (!GenerateBlock(chainman, miner, pblocktemplate->block, nMaxTries, block_out, /*process_new_block=*/true)) { break; } @@ -398,7 +398,7 @@ static RPCHelpMan generateblock() std::shared_ptr block_out; uint64_t max_tries{DEFAULT_MAX_TRIES}; - if (!GenerateBlock(chainman, block, max_tries, block_out, process_new_block) || !block_out) { + if (!GenerateBlock(chainman, miner, block, max_tries, block_out, process_new_block) || !block_out) { throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block."); } @@ -1049,10 +1049,13 @@ static RPCHelpMan submitblock() } } + NodeContext& node = EnsureAnyNodeContext(request.context); + Mining& miner = EnsureMining(node); + bool new_block; auto sc = std::make_shared(block.GetHash()); CHECK_NONFATAL(chainman.m_options.signals)->RegisterSharedValidationInterface(sc); - bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block); + bool accepted = miner.processNewBlock(blockptr, /*new_block=*/&new_block); CHECK_NONFATAL(chainman.m_options.signals)->UnregisterSharedValidationInterface(sc); if (!new_block && accepted) { return "duplicate"; From dda0b0834faf7be7e8938bf63e7bb01cd54a416a Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 7 Jun 2024 11:51:18 +0200 Subject: [PATCH 8/9] rpc: minize getTipHash() calls in gbt Set tip at the start of the function and only update it for a long poll. Additionally have getTipHash return an optional, so the caller can explicitly check that a tip exists. --- src/interfaces/mining.h | 5 +++-- src/node/interfaces.cpp | 4 ++-- src/rpc/mining.cpp | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index cd092397f3ea7..6e47333fd5612 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_INTERFACES_MINING_H #define BITCOIN_INTERFACES_MINING_H +#include #include namespace node { @@ -29,8 +30,8 @@ class Mining //! If this chain is exclusively used for testing virtual bool isTestChain() = 0; - //! Returns the hash for the tip of this chain, 0 if none - virtual uint256 getTipHash() = 0; + //! Returns the hash for the tip of this chain + virtual std::optional getTipHash() = 0; /** * Construct a new block template diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 2633ff1b98876..68c1c598cdd61 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -847,11 +847,11 @@ class MinerImpl : public Mining return chainman().GetParams().IsTestChain(); } - uint256 getTipHash() override + std::optional getTipHash() override { LOCK(::cs_main); CBlockIndex* tip{chainman().ActiveChain().Tip()}; - if (!tip) return uint256{0}; + if (!tip) return {}; return tip->GetBlockHash(); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index e404cea90ffc7..9324ba4a1cf33 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -667,6 +667,9 @@ static RPCHelpMan getblocktemplate() ChainstateManager& chainman = EnsureChainman(node); Mining& miner = EnsureMining(node); LOCK(cs_main); + std::optional maybe_tip{miner.getTipHash()}; + CHECK_NONFATAL(maybe_tip); + uint256 tip{maybe_tip.value()}; std::string strMode = "template"; UniValue lpval = NullUniValue; @@ -706,7 +709,7 @@ static RPCHelpMan getblocktemplate() } // testBlockValidity only supports blocks built on the current Tip - if (block.hashPrevBlock != miner.getTipHash()) { + if (block.hashPrevBlock != tip) { return "inconclusive-not-best-prevblk"; } BlockValidationState state; @@ -757,7 +760,7 @@ static RPCHelpMan getblocktemplate() else { // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier - hashWatchedChain = miner.getTipHash(); + hashWatchedChain = tip; nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; } @@ -781,6 +784,10 @@ static RPCHelpMan getblocktemplate() } ENTER_CRITICAL_SECTION(cs_main); + std::optional maybe_tip{miner.getTipHash()}; + CHECK_NONFATAL(maybe_tip); + tip = maybe_tip.value(); + if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? @@ -802,7 +809,7 @@ static RPCHelpMan getblocktemplate() static CBlockIndex* pindexPrev; static int64_t time_start; static std::unique_ptr pblocktemplate; - if (!pindexPrev || pindexPrev->GetBlockHash() != miner.getTipHash() || + if (!pindexPrev || pindexPrev->GetBlockHash() != tip || (miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on @@ -810,7 +817,7 @@ static RPCHelpMan getblocktemplate() // Store the pindexBest used before createNewBlock, to avoid races nTransactionsUpdatedLast = miner.getTransactionsUpdated(); - CBlockIndex* pindexPrevNew = chainman.m_blockman.LookupBlockIndex(miner.getTipHash()); + CBlockIndex* pindexPrevNew = chainman.m_blockman.LookupBlockIndex(tip); time_start = GetTime(); // Create new block @@ -943,7 +950,7 @@ static RPCHelpMan getblocktemplate() 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("longpollid", miner.getTipHash().GetHex() + ToString(nTransactionsUpdatedLast)); + result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast)); result.pushKV("target", hashTarget.GetHex()); result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); result.pushKV("mutable", std::move(aMutable)); From a9716c53f05082d6d89ebea51a46d4404efb12d7 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 18 Jun 2024 21:07:51 +0200 Subject: [PATCH 9/9] rpc: call IsInitialBlockDownload via miner interface --- src/interfaces/mining.h | 3 +++ src/node/interfaces.cpp | 5 +++++ src/rpc/mining.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 6e47333fd5612..b96881f67c487 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -30,6 +30,9 @@ class Mining //! If this chain is exclusively used for testing virtual bool isTestChain() = 0; + //! Returns whether IBD is still in progress. + virtual bool isInitialBlockDownload() = 0; + //! Returns the hash for the tip of this chain virtual std::optional getTipHash() = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 68c1c598cdd61..e0bab6e22e80d 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -847,6 +847,11 @@ class MinerImpl : public Mining return chainman().GetParams().IsTestChain(); } + bool isInitialBlockDownload() override + { + return chainman().IsInitialBlockDownload(); + } + std::optional getTipHash() override { LOCK(::cs_main); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9324ba4a1cf33..2b93c189657d6 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -735,7 +735,7 @@ static RPCHelpMan getblocktemplate() throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); } - if (chainman.IsInitialBlockDownload()) { + if (miner.isInitialBlockDownload()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); } }