diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 92c7bcaed73..289483142ba 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -432,7 +432,9 @@ static RPCHelpMan getdifficulty() { return RPCHelpMan{"getdifficulty", "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", - {}, + { + {"next", RPCArg::Type::BOOL, RPCArg::Default{false}, "difficulty for the next block, if found now"}, + }, RPCResult{ RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."}, RPCExamples{ @@ -442,8 +444,21 @@ static RPCHelpMan getdifficulty() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { ChainstateManager& chainman = EnsureAnyChainman(request.context); - LOCK(cs_main); - return GetDifficulty(*CHECK_NONFATAL(chainman.ActiveChain().Tip())); + CBlockIndex& tip{*CHECK_NONFATAL(WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip()))}; + + bool next{false}; + if (!request.params[0].isNull()) { + next = request.params[0].get_bool(); + } + + if (next) { + CBlockIndex next_index; + NextEmptyBlockIndex(tip, chainman.GetConsensus(), next_index); + return GetDifficulty(next_index); + } else { + return GetDifficulty(tip); + } + }, }; } @@ -452,7 +467,9 @@ static RPCHelpMan gettarget() { return RPCHelpMan{"gettarget", "\nReturns the proof-of-work target.\n", - {}, + { + {"next", RPCArg::Type::BOOL, RPCArg::Default{false}, "target for the next block, if found now"}, + }, RPCResult{ RPCResult::Type::STR_HEX, "", "the proof-of-work target."}, RPCExamples{ @@ -463,7 +480,19 @@ static RPCHelpMan gettarget() { ChainstateManager& chainman = EnsureAnyChainman(request.context); CBlockIndex& tip{*CHECK_NONFATAL(WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip()))}; - return GetTarget(tip, chainman.GetParams().GetConsensus().powLimit).GetHex(); + + bool next{false}; + if (!request.params[0].isNull()) { + next = request.params[0].get_bool(); + } + + if (next) { + CBlockIndex next_index; + NextEmptyBlockIndex(tip, chainman.GetConsensus(), next_index); + return GetTarget(next_index, chainman.GetConsensus().powLimit).GetHex(); + } else { + return GetTarget(tip, chainman.GetConsensus().powLimit).GetHex(); + } }, }; } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 1b711e3c5b1..1805aa3c260 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -114,6 +114,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblock", 1, "verbose" }, { "getblockheader", 1, "verbose" }, { "getchaintxstats", 0, "nblocks" }, + { "getdifficulty", 0, "next" }, + { "gettarget", 0, "next" }, { "gettransaction", 1, "include_watchonly" }, { "gettransaction", 2, "verbose" }, { "getrawtransaction", 1, "verbosity" }, diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp index 0387cbb8e25..0edd7527a95 100644 --- a/src/rpc/server_util.cpp +++ b/src/rpc/server_util.cpp @@ -4,10 +4,13 @@ #include +#include #include #include #include +#include #include +#include #include #include #include @@ -17,6 +20,7 @@ #include using node::NodeContext; +using node::UpdateTime; NodeContext& EnsureAnyNodeContext(const std::any& context) { @@ -129,3 +133,18 @@ AddrMan& EnsureAnyAddrman(const std::any& context) { return EnsureAddrman(EnsureAnyNodeContext(context)); } + +void NextEmptyBlockIndex(CBlockIndex& tip, const Consensus::Params& consensusParams, CBlockIndex& next_index) +{ + CBlockHeader next_header{}; + next_header.hashPrevBlock = tip.GetBlockHash(); + UpdateTime(&next_header, consensusParams, &tip); + next_header.nBits = GetNextWorkRequired(&tip, &next_header, consensusParams); + next_header.nNonce = 0; + + next_index.pprev = &tip; + next_index.nTime = next_header.nTime; + next_index.nBits = next_header.nBits; + next_index.nNonce = next_header.nNonce; + next_index.nHeight = tip.nHeight + 1; +} diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h index 1e6fb7e6a66..6fb5e1c9b35 100644 --- a/src/rpc/server_util.h +++ b/src/rpc/server_util.h @@ -7,8 +7,11 @@ #include +#include + class AddrMan; class ArgsManager; +class CBlockIndex; class CBlockPolicyEstimator; class CConnman; class CTxMemPool; @@ -39,4 +42,7 @@ PeerManager& EnsurePeerman(const node::NodeContext& node); AddrMan& EnsureAddrman(const node::NodeContext& node); AddrMan& EnsureAnyAddrman(const std::any& context); +/** Return an empty block index on top of the tip, with height, time and nBits set */ +void NextEmptyBlockIndex(CBlockIndex& tip, const Consensus::Params& consensusParams, CBlockIndex& next_index); + #endif // BITCOIN_RPC_SERVER_UTIL_H diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 6e75b3ddd1f..2081a382d65 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -444,11 +444,14 @@ class BlockchainTest(BitcoinTestFramework): # 1 hash in 2 should be valid, so difficulty should be 1/2**31 # binary => decimal => binary math is why we do this check assert abs(difficulty * 2**31 - 1) < 0.0001 + self.log.info("Next difficulty should be the same as the current (no difficulty adjustment)") + assert_equal(self.nodes[0].getdifficulty(next=True), difficulty) def _test_gettarget(self): self.log.info("Test gettarget") target = self.nodes[0].gettarget() assert_equal(target, target_str(REGTEST_TARGET)) + assert_equal(self.nodes[0].gettarget(next=True), target_str(REGTEST_TARGET)) def _test_getnetworkhashps(self): self.log.info("Test getnetworkhashps")