Compare commits

...

15 commits

Author SHA1 Message Date
Sjors Provoost
934336620a
Merge d925a9bd7a into 433412fd84 2025-01-08 13:19:47 +01:00
Sjors Provoost
d925a9bd7a
doc: add release notes
Co-Authored-By: tdb3 <106488469+tdb3@users.noreply.github.com>
2025-01-08 13:14:18 +01:00
tdb3
7dc2f06990
test: use DIFF_1_N_BITS in tool_signet_miner 2025-01-08 13:14:17 +01:00
Sjors Provoost
d3564be226
test: check testnet4 difficulty adjustment 2025-01-08 13:14:17 +01:00
Sjors Provoost
a9ce7d1774
rpc: add next to getmininginfo 2025-01-08 13:14:17 +01:00
Sjors Provoost
4463b0e04c
rpc: add target and bits to getchainstates 2025-01-08 13:14:17 +01:00
Sjors Provoost
590d3ab92c
rpc: add target and bits to getblockchaininfo 2025-01-08 13:14:17 +01:00
Sjors Provoost
1502b98b8b
rpc: add next to getdifficulty and gettarget
Obtain the difficulty / target for the next block without having to call getblocktemplate.
2025-01-08 13:14:17 +01:00
Sjors Provoost
f06f55090e
rpc: add target to getmininginfo result 2025-01-08 13:14:16 +01:00
Sjors Provoost
a0145ce476
Add target to getblock(header) in RPC and REST 2025-01-08 13:13:12 +01:00
Sjors Provoost
ef9e86039a
rpc: gettarget 2025-01-08 13:13:12 +01:00
tdb3
ddd2ede17a
test: use REGTEST_N_BITS in feature_block 2025-01-08 13:13:12 +01:00
Sjors Provoost
2401931542
rpc: add nBits to getmininginfo
Also expands nBits test coverage.
2025-01-08 13:13:08 +01:00
Sjors Provoost
3e976d4171
build: move pow and chain to bitcoin_common
The next commit needs pow.cpp in rpc/util.cpp.
2025-01-03 10:10:55 +01:00
Sjors Provoost
6accee1844
consensus: add DeriveTarget() to pow.h
Split CheckProofOfWorkImpl() to introduce a helper function
DeriveTarget() which converts the nBits value to the target.

The function takes pow_limit as an argument so later commits can
avoid having to pass ChainstateManager through the call stack.

Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
2025-01-03 10:07:38 +01:00
25 changed files with 2354 additions and 35 deletions

View file

@ -0,0 +1,14 @@
Updated RPCs
---
- `getmininginfo` now returns `nBits` and the current target in the `target` field. It also returns a `next` object which specifies the `height`, `nBits`, `difficulty`, and `target` for the next block.
- `getdifficulty` can now return the difficulty for the next block (rather than the current tip) when calling with the boolean `next` argument set to true.
- `getblock` and `getblockheader` now return the current target in the `target` field
- `getblockchaininfo` and `getchainstates` now return `nBits` and the current target in the `target` field
New RPCs
---
- `gettarget` can be used to return the current target (for tip) or target for the next block (with the `next` argument)
REST interface
---
- `GET /rest/block/<BLOCK-HASH>.json` and `GET /rest/headers/<BLOCK-HASH>.json` now return the current target in the `target` field

View file

@ -109,6 +109,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
addresstype.cpp
base58.cpp
bech32.cpp
chain.cpp
chainparams.cpp
chainparamsbase.cpp
coins.cpp
@ -142,6 +143,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
outputtype.cpp
policy/feerate.cpp
policy/policy.cpp
pow.cpp
protocol.cpp
psbt.cpp
rpc/external_signer.cpp
@ -200,7 +202,6 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
bip324.cpp
blockencodings.cpp
blockfilter.cpp
chain.cpp
consensus/tx_verify.cpp
dbwrapper.cpp
deploymentstatus.cpp
@ -262,7 +263,6 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
policy/rbf.cpp
policy/settings.cpp
policy/truc_policy.cpp
pow.cpp
rest.cpp
rpc/blockchain.cpp
rpc/fees.cpp

View file

@ -48,8 +48,9 @@ struct TestBlockAndIndex {
static void BlockToJsonVerbose(benchmark::Bench& bench)
{
TestBlockAndIndex data;
const uint256 pow_limit{data.testing_setup->m_node.chainman->GetParams().GetConsensus().powLimit};
bench.run([&] {
auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, data.blockindex, data.blockindex, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, data.blockindex, data.blockindex, TxVerbosity::SHOW_DETAILS_AND_PREVOUT, pow_limit);
ankerl::nanobench::doNotOptimizeAway(univalue);
});
}
@ -59,7 +60,8 @@ BENCHMARK(BlockToJsonVerbose, benchmark::PriorityLevel::HIGH);
static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
{
TestBlockAndIndex data;
auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, data.blockindex, data.blockindex, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
const uint256 pow_limit{data.testing_setup->m_node.chainman->GetParams().GetConsensus().powLimit};
auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, data.blockindex, data.blockindex, TxVerbosity::SHOW_DETAILS_AND_PREVOUT, pow_limit);
bench.run([&] {
auto str = univalue.write();
ankerl::nanobench::doNotOptimizeAway(str);

View file

@ -143,7 +143,7 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&
return CheckProofOfWorkImpl(hash, nBits, params);
}
bool CheckProofOfWorkImpl(uint256 hash, unsigned int nBits, const Consensus::Params& params)
std::optional<arith_uint256> DeriveTarget(unsigned int nBits, const uint256 pow_limit)
{
bool fNegative;
bool fOverflow;
@ -152,8 +152,16 @@ bool CheckProofOfWorkImpl(uint256 hash, unsigned int nBits, const Consensus::Par
bnTarget.SetCompact(nBits, &fNegative, &fOverflow);
// Check range
if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
return false;
if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(pow_limit))
return {};
return bnTarget;
}
bool CheckProofOfWorkImpl(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
auto bnTarget{DeriveTarget(nBits, params.powLimit)};
if (!bnTarget) return false;
// Check proof of work matches claimed amount
if (UintToArith256(hash) > bnTarget)

View file

@ -13,6 +13,18 @@
class CBlockHeader;
class CBlockIndex;
class uint256;
class arith_uint256;
/**
* Convert nBits value to target.
*
* @param[in] nBits compact representation of the target
* @param[in] pow_limit PoW limit (consensus parameter)
*
* @return the proof-of-work target or nullopt if the nBits value
* is invalid (due to overflow or exceeding pow_limit)
*/
std::optional<arith_uint256> DeriveTarget(unsigned int nBits, const uint256 pow_limit);
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&);
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);

View file

@ -225,10 +225,10 @@ static bool rest_headers(const std::any& context,
const CBlockIndex* tip = nullptr;
std::vector<const CBlockIndex*> headers;
headers.reserve(*parsed_count);
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
ChainstateManager& chainman = *maybe_chainman;
{
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
CChain& active_chain = chainman.ActiveChain();
tip = active_chain.Tip();
@ -268,7 +268,7 @@ static bool rest_headers(const std::any& context,
case RESTResponseFormat::JSON: {
UniValue jsonHeaders(UniValue::VARR);
for (const CBlockIndex *pindex : headers) {
jsonHeaders.push_back(blockheaderToJSON(*tip, *pindex));
jsonHeaders.push_back(blockheaderToJSON(*tip, *pindex, chainman.GetConsensus().powLimit));
}
std::string strJSON = jsonHeaders.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@ -341,7 +341,7 @@ static bool rest_block(const std::any& context,
CBlock block{};
DataStream block_stream{block_data};
block_stream >> TX_WITH_WITNESS(block);
UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity);
UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity, chainman.GetConsensus().powLimit);
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);

View file

@ -146,7 +146,7 @@ static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateMan
}
}
UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex)
UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex, const uint256 pow_limit)
{
// Serialize passed information without accessing chain state of the active chain!
AssertLockNotHeld(cs_main); // For performance reasons
@ -164,6 +164,7 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
result.pushKV("mediantime", blockindex.GetMedianTimePast());
result.pushKV("nonce", blockindex.nNonce);
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
result.pushKV("target", GetTarget(tip, pow_limit).GetHex());
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
result.pushKV("nTx", blockindex.nTx);
@ -175,9 +176,9 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
return result;
}
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity)
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity, const uint256 pow_limit)
{
UniValue result = blockheaderToJSON(tip, blockindex);
UniValue result = blockheaderToJSON(tip, blockindex, pow_limit);
result.pushKV("strippedsize", (int)::GetSerializeSize(TX_NO_WITNESS(block)));
result.pushKV("size", (int)::GetSerializeSize(TX_WITH_WITNESS(block)));
@ -431,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{
@ -441,8 +444,55 @@ 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);
}
},
};
}
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{
HelpExampleCli("gettarget", "")
+ HelpExampleRpc("gettarget", "")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
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 GetTarget(next_index, chainman.GetConsensus().powLimit).GetHex();
} else {
return GetTarget(tip, chainman.GetConsensus().powLimit).GetHex();
}
},
};
}
@ -553,7 +603,8 @@ static RPCHelpMan getblockheader()
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM, "nonce", "The nonce"},
{RPCResult::Type::STR_HEX, "bits", "The bits"},
{RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
{RPCResult::Type::STR_HEX, "target", "The difficulty target"},
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
@ -577,8 +628,8 @@ static RPCHelpMan getblockheader()
const CBlockIndex* pblockindex;
const CBlockIndex* tip;
ChainstateManager& chainman = EnsureAnyChainman(request.context);
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
tip = chainman.ActiveChain().Tip();
@ -596,7 +647,7 @@ static RPCHelpMan getblockheader()
return strHex;
}
return blockheaderToJSON(*tip, *pblockindex);
return blockheaderToJSON(*tip, *pblockindex, chainman.GetConsensus().powLimit);
},
};
}
@ -727,7 +778,8 @@ static RPCHelpMan getblock()
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM, "nonce", "The nonce"},
{RPCResult::Type::STR_HEX, "bits", "The bits"},
{RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
{RPCResult::Type::STR_HEX, "target", "The difficulty target"},
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
@ -802,7 +854,7 @@ static RPCHelpMan getblock()
tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
}
return blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity);
return blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity, chainman.GetConsensus().powLimit);
},
};
}
@ -1296,6 +1348,8 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"},
{RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
{RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
{RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
{RPCResult::Type::STR_HEX, "target", "The difficulty target"},
{RPCResult::Type::NUM, "difficulty", "the current difficulty"},
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
@ -1334,6 +1388,8 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("blocks", height);
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
obj.pushKV("bits", strprintf("%08x", tip.nBits));
obj.pushKV("target", GetTarget(tip, chainman.GetConsensus().powLimit).GetHex());
obj.pushKV("difficulty", GetDifficulty(tip));
obj.pushKV("time", tip.GetBlockTime());
obj.pushKV("mediantime", tip.GetMedianTimePast());
@ -3301,6 +3357,8 @@ static RPCHelpMan loadtxoutset()
const std::vector<RPCResult> RPCHelpForChainstate{
{RPCResult::Type::NUM, "blocks", "number of blocks in this chainstate"},
{RPCResult::Type::STR_HEX, "bestblockhash", "blockhash of the tip"},
{RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
{RPCResult::Type::STR_HEX, "target", "The difficulty target"},
{RPCResult::Type::NUM, "difficulty", "difficulty of the tip"},
{RPCResult::Type::NUM, "verificationprogress", "progress towards the network tip"},
{RPCResult::Type::STR_HEX, "snapshot_blockhash", /*optional=*/true, "the base block of the snapshot this chainstate is based on, if any"},
@ -3343,6 +3401,8 @@ return RPCHelpMan{
data.pushKV("blocks", (int)chain.Height());
data.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
data.pushKV("bits", strprintf("%08x", tip->nBits));
data.pushKV("target", GetTarget(*tip, chainman.GetConsensus().powLimit).GetHex());
data.pushKV("difficulty", GetDifficulty(*tip));
data.pushKV("verificationprogress", chainman.GuessVerificationProgress(tip));
data.pushKV("coins_db_cache_bytes", cs.m_coinsdb_cache_size_bytes);
@ -3382,6 +3442,7 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
{"blockchain", &getblockheader},
{"blockchain", &getchaintips},
{"blockchain", &getdifficulty},
{"blockchain", &gettarget},
{"blockchain", &getdeploymentinfo},
{"blockchain", &gettxout},
{"blockchain", &gettxoutsetinfo},

View file

@ -36,10 +36,10 @@ static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
double GetDifficulty(const CBlockIndex& blockindex);
/** Block description to JSON */
UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main);
UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity, const uint256 pow_limit) LOCKS_EXCLUDED(cs_main);
/** Block header to JSON */
UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex) LOCKS_EXCLUDED(cs_main);
UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex, const uint256 pow_limit) LOCKS_EXCLUDED(cs_main);
/** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);

View file

@ -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" },

View file

@ -421,11 +421,20 @@ static RPCHelpMan getmininginfo()
{RPCResult::Type::NUM, "blocks", "The current block"},
{RPCResult::Type::NUM, "currentblockweight", /*optional=*/true, "The block weight of the last assembled block (only present if a block was ever assembled)"},
{RPCResult::Type::NUM, "currentblocktx", /*optional=*/true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"},
{RPCResult::Type::STR_HEX, "bits", "The current nBits, compact representation of the block difficulty target"},
{RPCResult::Type::NUM, "difficulty", "The current difficulty"},
{RPCResult::Type::STR_HEX, "target", "The current target"},
{RPCResult::Type::NUM, "networkhashps", "The network hashes per second"},
{RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
{RPCResult::Type::STR, "chain", "current network name (" LIST_CHAIN_NAMES ")"},
{RPCResult::Type::STR_HEX, "signet_challenge", /*optional=*/true, "The block challenge (aka. block script), in hexadecimal (only present if the current network is a signet)"},
{RPCResult::Type::OBJ, "next", "The next block, if found now",
{
{RPCResult::Type::NUM, "height", "The next height"},
{RPCResult::Type::STR_HEX, "bits", "The next target nBits"},
{RPCResult::Type::NUM, "difficulty", "The next difficulty"},
{RPCResult::Type::STR_HEX, "target", "The next target"}
}},
(IsDeprecatedRPCEnabled("warnings") ?
RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
@ -446,18 +455,32 @@ static RPCHelpMan getmininginfo()
ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
const CChain& active_chain = chainman.ActiveChain();
CBlockIndex& tip{*CHECK_NONFATAL(active_chain.Tip())};
UniValue obj(UniValue::VOBJ);
obj.pushKV("blocks", active_chain.Height());
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
obj.pushKV("difficulty", GetDifficulty(*CHECK_NONFATAL(active_chain.Tip())));
obj.pushKV("bits", strprintf("%08x", tip.nBits));
obj.pushKV("difficulty", GetDifficulty(tip));
obj.pushKV("target", GetTarget(tip, chainman.GetConsensus().powLimit).GetHex());
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
UniValue next(UniValue::VOBJ);
CBlockIndex next_index;
NextEmptyBlockIndex(tip, chainman.GetConsensus(), next_index);
next.pushKV("height", next_index.nHeight);
next.pushKV("bits", strprintf("%08x", next_index.nBits));
next.pushKV("difficulty", GetDifficulty(next_index));
next.pushKV("target", GetTarget(next_index, chainman.GetConsensus().powLimit).GetHex());
obj.pushKV("next", next);
if (chainman.GetParams().GetChainType() == ChainType::SIGNET) {
const std::vector<uint8_t>& signet_challenge =
chainman.GetParams().GetConsensus().signet_challenge;
chainman.GetConsensus().signet_challenge;
obj.pushKV("signet_challenge", HexStr(signet_challenge));
}
obj.pushKV("warnings", node::GetWarningsForRpc(*CHECK_NONFATAL(node.warnings), IsDeprecatedRPCEnabled("warnings")));

View file

@ -4,10 +4,13 @@
#include <rpc/server_util.h>
#include <chain.h>
#include <common/args.h>
#include <net_processing.h>
#include <node/context.h>
#include <node/miner.h>
#include <policy/fees.h>
#include <pow.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <txmempool.h>
@ -17,6 +20,7 @@
#include <any>
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;
}

View file

@ -7,8 +7,11 @@
#include <any>
#include <consensus/params.h>
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

View file

@ -4,6 +4,7 @@
#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <chain.h>
#include <clientversion.h>
#include <common/args.h>
#include <common/messages.h>
@ -13,6 +14,7 @@
#include <key_io.h>
#include <node/types.h>
#include <outputtype.h>
#include <pow.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/interpreter.h>
@ -1418,3 +1420,9 @@ std::vector<RPCResult> ScriptPubKeyDoc() {
{RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"},
};
}
uint256 GetTarget(const CBlockIndex& blockindex, const uint256 pow_limit)
{
arith_uint256 target{*CHECK_NONFATAL(DeriveTarget(blockindex.nBits, pow_limit))};
return ArithToUint256(target);
}

View file

@ -516,4 +516,14 @@ void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj);
std::vector<RPCResult> ScriptPubKeyDoc();
/***
* Get the target for a given block index.
*
* @param[in] blockindex the block
* @param[in] pow_limit PoW limit (consensus parameter)
*
* @return the target
*/
uint256 GetTarget(const CBlockIndex& blockindex, const uint256 pow_limit);
#endif // BITCOIN_RPC_UTIL_H

View file

@ -147,6 +147,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getorphantxs",
"getpeerinfo",
"getprioritisedtransactions",
"gettarget",
"getrawaddrman",
"getrawmempool",
"getrawtransaction",

File diff suppressed because one or more lines are too long

View file

@ -38,6 +38,12 @@ from test_framework.wallet import (
getnewdestination,
MiniWallet,
)
from test_framework.blocktools import (
REGTEST_N_BITS,
REGTEST_TARGET,
nbits_str,
target_str,
)
START_HEIGHT = 199
SNAPSHOT_BASE_HEIGHT = 299
@ -229,6 +235,12 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(normal['blocks'], START_HEIGHT + 99)
assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT)
# Both states should have the same nBits and target
assert_equal(normal['bits'], nbits_str(REGTEST_N_BITS))
assert_equal(normal['bits'], snapshot['bits'])
assert_equal(normal['target'], target_str(REGTEST_TARGET))
assert_equal(normal['target'], snapshot['target'])
# Now lets sync the nodes and wait for the background validation to finish
self.connect_nodes(0, 3)
self.sync_blocks(nodes=(n0, n3))

View file

@ -12,6 +12,7 @@ from test_framework.blocktools import (
create_tx_with_script,
get_legacy_sigopcount_block,
MAX_BLOCK_SIGOPS,
REGTEST_N_BITS,
)
from test_framework.messages import (
CBlock,
@ -590,7 +591,7 @@ class FullBlockTest(BitcoinTestFramework):
b44 = CBlock()
b44.nTime = self.tip.nTime + 1
b44.hashPrevBlock = self.tip.sha256
b44.nBits = 0x207fffff
b44.nBits = REGTEST_N_BITS
b44.vtx.append(coinbase)
tx = self.create_and_sign_transaction(out[14], 1)
b44.vtx.append(tx)
@ -606,7 +607,7 @@ class FullBlockTest(BitcoinTestFramework):
b45 = CBlock()
b45.nTime = self.tip.nTime + 1
b45.hashPrevBlock = self.tip.sha256
b45.nBits = 0x207fffff
b45.nBits = REGTEST_N_BITS
b45.vtx.append(non_coinbase)
b45.hashMerkleRoot = b45.calc_merkle_root()
b45.solve()
@ -620,7 +621,7 @@ class FullBlockTest(BitcoinTestFramework):
b46 = CBlock()
b46.nTime = b44.nTime + 1
b46.hashPrevBlock = b44.sha256
b46.nBits = 0x207fffff
b46.nBits = REGTEST_N_BITS
b46.vtx = []
b46.hashMerkleRoot = 0
b46.solve()

View file

@ -289,7 +289,7 @@ class RESTTest (BitcoinTestFramework):
# Compare with normal RPC block response
rpc_block_json = self.nodes[0].getblock(bb_hash)
for key in ['hash', 'confirmations', 'height', 'version', 'merkleroot', 'time', 'nonce', 'bits', 'difficulty', 'chainwork', 'previousblockhash']:
for key in ['hash', 'confirmations', 'height', 'version', 'merkleroot', 'time', 'nonce', 'bits', 'target', 'difficulty', 'chainwork', 'previousblockhash']:
assert_equal(json_obj[0][key], rpc_block_json[key])
# See if we can get 5 headers in one response

View file

@ -16,6 +16,10 @@ from test_framework.blocktools import (
get_witness_script,
NORMAL_GBT_REQUEST_PARAMS,
TIME_GENESIS_BLOCK,
REGTEST_N_BITS,
REGTEST_TARGET,
nbits_str,
target_str,
)
from test_framework.messages import (
BLOCK_HEADER_SIZE,
@ -206,7 +210,15 @@ class MiningTest(BitcoinTestFramework):
assert_equal(mining_info['chain'], self.chain)
assert 'currentblocktx' not in mining_info
assert 'currentblockweight' not in mining_info
assert_equal(mining_info['bits'], nbits_str(REGTEST_N_BITS))
assert_equal(mining_info['target'], target_str(REGTEST_TARGET))
assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10'))
assert_equal(mining_info['next'], {
'height': 201,
'target': target_str(REGTEST_TARGET),
'bits': nbits_str(REGTEST_N_BITS),
'difficulty': Decimal('4.656542373906925E-10')
})
assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
assert_equal(mining_info['pooledtx'], 0)

View file

@ -0,0 +1,73 @@
#!/usr/bin/env python3
# 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.
"""Test mining on testnet
Test mining related RPCs that involve difficulty adjustment, which
regtest doesn't have.
It uses the first retarget period of testnet4, generated as follows:
for i in {1..2015}
do
hash=`bitcoin-cli -testnet4 getblockhash $i`
block=`bitcoin-cli -testnet4 getblock $hash 0`
echo $block >> data/testnet4.hex
done
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
from test_framework.blocktools import (
DIFF_1_N_BITS,
DIFF_1_TARGET,
DIFF_4_N_BITS,
DIFF_4_TARGET,
nbits_str,
target_str
)
import os
class MiningTestnetTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.chain = "testnet4"
def add_options(self, parser):
parser.add_argument(
'--datafile',
default='data/testnet4.hex',
help='Test data file (default: %(default)s)',
)
def run_test(self):
node = self.nodes[0]
self.log.info("Load testnet4 blocks")
self.headers_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
with open(self.headers_file_path, encoding='utf-8') as blocks_data:
for block in blocks_data:
node.submitblock(block.strip())
assert_equal(node.getblockcount(), 2015)
mining_info = node.getmininginfo()
assert_equal(mining_info['difficulty'], 1)
assert_equal(mining_info['bits'], nbits_str(DIFF_1_N_BITS))
assert_equal(mining_info['target'], target_str(DIFF_1_TARGET))
assert_equal(mining_info['next']['height'], 2016)
assert_equal(mining_info['next']['difficulty'], 4)
assert_equal(mining_info['next']['bits'], nbits_str(DIFF_4_N_BITS))
assert_equal(mining_info['next']['target'], target_str(DIFF_4_TARGET))
assert_equal(node.getdifficulty(next=True), 4)
assert_equal(node.gettarget(next=True), target_str(DIFF_4_TARGET))
if __name__ == '__main__':
MiningTestnetTest(__file__).main()

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2022 The Bitcoin Core developers
# Copyright (c) 2014-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test RPCs related to blockchainstate.
@ -11,6 +11,7 @@ Test the following RPCs:
- gettxoutsetinfo
- getblockheader
- getdifficulty
- gettarget
- getnetworkhashps
- waitforblockheight
- getblock
@ -30,9 +31,13 @@ import textwrap
from test_framework.blocktools import (
MAX_FUTURE_BLOCK_TIME,
TIME_GENESIS_BLOCK,
REGTEST_N_BITS,
REGTEST_TARGET,
create_block,
create_coinbase,
create_tx_with_script,
nbits_str,
target_str,
)
from test_framework.messages import (
CBlockHeader,
@ -88,6 +93,7 @@ class BlockchainTest(BitcoinTestFramework):
self._test_gettxoutsetinfo()
self._test_getblockheader()
self._test_getdifficulty()
self._test_gettarget()
self._test_getnetworkhashps()
self._test_stopatheight()
self._test_waitforblock() # also tests waitfornewblock
@ -129,6 +135,7 @@ class BlockchainTest(BitcoinTestFramework):
keys = [
'bestblockhash',
'bits',
'blocks',
'chain',
'chainwork',
@ -138,6 +145,7 @@ class BlockchainTest(BitcoinTestFramework):
'mediantime',
'pruned',
'size_on_disk',
'target',
'time',
'verificationprogress',
'warnings',
@ -194,6 +202,9 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['prune_target_size'], 576716800)
assert_greater_than(res['size_on_disk'], 0)
assert_equal(res['bits'], nbits_str(REGTEST_N_BITS))
assert_equal(res['target'], target_str(REGTEST_TARGET))
def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash, status_next):
assert height >= 144 and height <= 287
@ -412,7 +423,8 @@ class BlockchainTest(BitcoinTestFramework):
assert_is_hash_string(header['hash'])
assert_is_hash_string(header['previousblockhash'])
assert_is_hash_string(header['merkleroot'])
assert_is_hash_string(header['bits'], length=None)
assert_equal(header['bits'], nbits_str(REGTEST_N_BITS))
assert_equal(header['target'], target_str(REGTEST_TARGET))
assert isinstance(header['time'], int)
assert_equal(header['mediantime'], TIME_RANGE_MTP)
assert isinstance(header['nonce'], int)
@ -437,6 +449,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")

View file

@ -27,6 +27,7 @@ from .messages import (
hash256,
ser_uint256,
tx_from_hex,
uint256_from_compact,
uint256_from_str,
WITNESS_SCALE_FACTOR,
)
@ -65,6 +66,23 @@ NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
MIN_BLOCKS_TO_KEEP = 288
REGTEST_N_BITS = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams"
REGTEST_TARGET = 0x7fffff0000000000000000000000000000000000000000000000000000000000
assert_equal(uint256_from_compact(REGTEST_N_BITS), REGTEST_TARGET)
DIFF_1_N_BITS = 0x1d00ffff
DIFF_1_TARGET = 0x00000000ffff0000000000000000000000000000000000000000000000000000
assert_equal(uint256_from_compact(DIFF_1_N_BITS), DIFF_1_TARGET)
DIFF_4_N_BITS = 0x1c3fffc0
DIFF_4_TARGET = int(DIFF_1_TARGET / 4)
assert_equal(uint256_from_compact(DIFF_4_N_BITS), DIFF_4_TARGET)
def nbits_str(nbits):
return f"{nbits:08x}"
def target_str(target):
return f"{target:064x}"
def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None):
"""Create a block (with regtest difficulty)."""
@ -77,7 +95,7 @@ def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl
if tmpl and tmpl.get('bits') is not None:
block.nBits = struct.unpack('>I', bytes.fromhex(tmpl['bits']))[0]
else:
block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
block.nBits = REGTEST_N_BITS
if coinbase is None:
coinbase = create_coinbase(height=tmpl['height'])
block.vtx.append(coinbase)

View file

@ -316,6 +316,7 @@ BASE_SCRIPTS = [
'wallet_upgradewallet.py --legacy-wallet',
'wallet_crosschain.py',
'mining_basic.py',
'mining_testnet.py',
'feature_signet.py',
'p2p_mutated_blocks.py',
'wallet_implicitsegwit.py --legacy-wallet',

View file

@ -9,6 +9,7 @@ import subprocess
import sys
import time
from test_framework.blocktools import DIFF_1_N_BITS
from test_framework.key import ECKey
from test_framework.script_util import key_to_p2wpkh_script
from test_framework.test_framework import BitcoinTestFramework
@ -55,7 +56,7 @@ class SignetMinerTest(BitcoinTestFramework):
'generate',
f'--address={node.getnewaddress()}',
f'--grind-cmd={self.options.bitcoinutil} grind',
'--nbits=1d00ffff',
f'--nbits={hex(DIFF_1_N_BITS)}',
f'--set-block-time={int(time.time())}',
'--poolnum=99',
], check=True, stderr=subprocess.STDOUT)