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 eb2bb41aa41..d9d5b392c5e 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 4a1973aa876..f95a6fa1232 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 5bb82dc3209..0c57dfea7f0 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 97b8dc11614..00a38227911 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -30,6 +30,7 @@ public: } 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 3003a8fde10..5209c729731 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 @@ public: } 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 b5df7640172..48be8831d25 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 @@ public: } 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 addc45aa266..094ead399dc 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ public: 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 00000000000..afcd8d1cda5 --- /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 da05fde6eea..75dfaee866d 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 77838ea99b9..a664fad80be 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 2b36f4ceae3..e44cb518739 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 @@ public: 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 0f6853ef37d..0e9d4829d21 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 efd4a43c288..0387cbb8e25 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 a4a53166b4b..1e6fb7e6a66 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 afcd8d1cda5..603d8475d38 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 @@ public: //! 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 e44cb518739..a528afcff06 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 @@ public: 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 0e9d4829d21..a9968b6e5fa 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 20f62079fd6..3e250925e78 100755 --- a/test/functional/rpc_generate.py +++ b/test/functional/rpc_generate.py @@ -87,7 +87,7 @@ class RPCGenerateTest(BitcoinTestFramework): 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 603d8475d38..8ed273252ba 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 @@ public: //! 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 a528afcff06..bd200e8d294 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -845,6 +845,14 @@ public: 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 a9968b6e5fa..9762d7648d6 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 8ed273252ba..9494fd75bf2 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 @@ public: //! 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 bd200e8d294..215dbf5e170 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 @@ public: 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 9762d7648d6..a65413de1ed 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 215dbf5e170..356a01b286f 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 @@ public: 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 87f40e993f2..03c6d74deba 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 06a917228dc..c3178a7532b 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -161,7 +161,6 @@ public: 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 d44d84af932..067a32d6a40 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 397b1d8c2d5..6de373eef23 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 79e33eacec0..cc7b2d65463 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 69f4e305ab6..588ac60498c 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 9494fd75bf2..3ebc48dffa5 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -41,6 +41,10 @@ public: */ 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 356a01b286f..91ee8585971 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -855,6 +855,11 @@ public: 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 a65413de1ed..3cca6a53fad 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 3ebc48dffa5..cd092397f3e 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -40,6 +40,14 @@ public: * @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 91ee8585971..2633ff1b988 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -855,6 +855,11 @@ public: 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 3cca6a53fad..e404cea90ff 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 cd092397f3e..6e47333fd56 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 @@ public: //! 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 2633ff1b988..68c1c598cdd 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -847,11 +847,11 @@ public: 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 e404cea90ff..9324ba4a1cf 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 6e47333fd56..b96881f67c4 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -30,6 +30,9 @@ public: //! 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 68c1c598cdd..e0bab6e22e8 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -847,6 +847,11 @@ public: 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 9324ba4a1cf..2b93c189657 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..."); } }