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 eb2bb41aa4..d9d5b392c5 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 4a1973aa87..f95a6fa123 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 5bb82dc320..0c57dfea7f 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 97b8dc1161..00a3822791 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 3003a8fde1..5209c72973 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 b5df764017..48be8831d2 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 addc45aa26..094ead399d 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 0000000000..afcd8d1cda --- /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 da05fde6ee..75dfaee866 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 77838ea99b..a664fad80b 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 2b36f4ceae..e44cb51873 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 0f6853ef37..0e9d4829d2 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 efd4a43c28..0387cbb8e2 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 a4a53166b4..1e6fb7e6a6 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 afcd8d1cda..603d8475d3 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 e44cb51873..a528afcff0 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 0e9d4829d2..a9968b6e5f 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 20f62079fd..3e250925e7 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 603d8475d3..8ed273252b 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 a528afcff0..bd200e8d29 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 a9968b6e5f..9762d7648d 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 8ed273252b..9494fd75bf 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 bd200e8d29..215dbf5e17 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 9762d7648d..a65413de1e 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 215dbf5e17..356a01b286 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 87f40e993f..03c6d74deb 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 06a917228d..c3178a7532 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 d44d84af93..067a32d6a4 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 397b1d8c2d..6de373eef2 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 79e33eacec..cc7b2d6546 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 69f4e305ab..588ac60498 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 9494fd75bf..3ebc48dffa 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 356a01b286..91ee858597 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 a65413de1e..3cca6a53fa 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 3ebc48dffa..cd092397f3 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 91ee858597..2633ff1b98 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 3cca6a53fa..e404cea90f 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 cd092397f3..6e47333fd5 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 2633ff1b98..68c1c598cd 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 e404cea90f..9324ba4a1c 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 6e47333fd5..b96881f67c 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 68c1c598cd..e0bab6e22e 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 9324ba4a1c..2b93c18965 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..."); } }