mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-09 11:27:28 -03:00
Compare commits
12 commits
223e8bcb99
...
d925a9bd7a
Author | SHA1 | Date | |
---|---|---|---|
|
d925a9bd7a | ||
|
7dc2f06990 | ||
|
d3564be226 | ||
|
a9ce7d1774 | ||
|
4463b0e04c | ||
|
590d3ab92c | ||
|
1502b98b8b | ||
|
f06f55090e | ||
|
a0145ce476 | ||
|
ef9e86039a | ||
|
ddd2ede17a | ||
|
2401931542 |
22 changed files with 2329 additions and 30 deletions
14
doc/release-notes-31583.md
Normal file
14
doc/release-notes-31583.md
Normal 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
|
|
@ -48,8 +48,9 @@ struct TestBlockAndIndex {
|
||||||
static void BlockToJsonVerbose(benchmark::Bench& bench)
|
static void BlockToJsonVerbose(benchmark::Bench& bench)
|
||||||
{
|
{
|
||||||
TestBlockAndIndex data;
|
TestBlockAndIndex data;
|
||||||
|
const uint256 pow_limit{data.testing_setup->m_node.chainman->GetParams().GetConsensus().powLimit};
|
||||||
bench.run([&] {
|
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);
|
ankerl::nanobench::doNotOptimizeAway(univalue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -59,7 +60,8 @@ BENCHMARK(BlockToJsonVerbose, benchmark::PriorityLevel::HIGH);
|
||||||
static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
|
static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
|
||||||
{
|
{
|
||||||
TestBlockAndIndex data;
|
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([&] {
|
bench.run([&] {
|
||||||
auto str = univalue.write();
|
auto str = univalue.write();
|
||||||
ankerl::nanobench::doNotOptimizeAway(str);
|
ankerl::nanobench::doNotOptimizeAway(str);
|
||||||
|
|
10
src/rest.cpp
10
src/rest.cpp
|
@ -225,10 +225,10 @@ static bool rest_headers(const std::any& context,
|
||||||
const CBlockIndex* tip = nullptr;
|
const CBlockIndex* tip = nullptr;
|
||||||
std::vector<const CBlockIndex*> headers;
|
std::vector<const CBlockIndex*> headers;
|
||||||
headers.reserve(*parsed_count);
|
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);
|
LOCK(cs_main);
|
||||||
CChain& active_chain = chainman.ActiveChain();
|
CChain& active_chain = chainman.ActiveChain();
|
||||||
tip = active_chain.Tip();
|
tip = active_chain.Tip();
|
||||||
|
@ -268,7 +268,7 @@ static bool rest_headers(const std::any& context,
|
||||||
case RESTResponseFormat::JSON: {
|
case RESTResponseFormat::JSON: {
|
||||||
UniValue jsonHeaders(UniValue::VARR);
|
UniValue jsonHeaders(UniValue::VARR);
|
||||||
for (const CBlockIndex *pindex : headers) {
|
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";
|
std::string strJSON = jsonHeaders.write() + "\n";
|
||||||
req->WriteHeader("Content-Type", "application/json");
|
req->WriteHeader("Content-Type", "application/json");
|
||||||
|
@ -341,7 +341,7 @@ static bool rest_block(const std::any& context,
|
||||||
CBlock block{};
|
CBlock block{};
|
||||||
DataStream block_stream{block_data};
|
DataStream block_stream{block_data};
|
||||||
block_stream >> TX_WITH_WITNESS(block);
|
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";
|
std::string strJSON = objBlock.write() + "\n";
|
||||||
req->WriteHeader("Content-Type", "application/json");
|
req->WriteHeader("Content-Type", "application/json");
|
||||||
req->WriteReply(HTTP_OK, strJSON);
|
req->WriteReply(HTTP_OK, strJSON);
|
||||||
|
|
|
@ -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!
|
// Serialize passed information without accessing chain state of the active chain!
|
||||||
AssertLockNotHeld(cs_main); // For performance reasons
|
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("mediantime", blockindex.GetMedianTimePast());
|
||||||
result.pushKV("nonce", blockindex.nNonce);
|
result.pushKV("nonce", blockindex.nNonce);
|
||||||
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
|
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
|
||||||
|
result.pushKV("target", GetTarget(tip, pow_limit).GetHex());
|
||||||
result.pushKV("difficulty", GetDifficulty(blockindex));
|
result.pushKV("difficulty", GetDifficulty(blockindex));
|
||||||
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
|
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
|
||||||
result.pushKV("nTx", blockindex.nTx);
|
result.pushKV("nTx", blockindex.nTx);
|
||||||
|
@ -175,9 +176,9 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
|
||||||
return result;
|
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("strippedsize", (int)::GetSerializeSize(TX_NO_WITNESS(block)));
|
||||||
result.pushKV("size", (int)::GetSerializeSize(TX_WITH_WITNESS(block)));
|
result.pushKV("size", (int)::GetSerializeSize(TX_WITH_WITNESS(block)));
|
||||||
|
@ -431,7 +432,9 @@ static RPCHelpMan getdifficulty()
|
||||||
{
|
{
|
||||||
return RPCHelpMan{"getdifficulty",
|
return RPCHelpMan{"getdifficulty",
|
||||||
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
|
"\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{
|
||||||
RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
|
RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
|
||||||
RPCExamples{
|
RPCExamples{
|
||||||
|
@ -441,8 +444,55 @@ static RPCHelpMan getdifficulty()
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
{
|
{
|
||||||
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
LOCK(cs_main);
|
CBlockIndex& tip{*CHECK_NONFATAL(WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip()))};
|
||||||
return GetDifficulty(*CHECK_NONFATAL(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, "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_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
|
||||||
{RPCResult::Type::NUM, "nonce", "The nonce"},
|
{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::NUM, "difficulty", "The difficulty"},
|
||||||
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
|
{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"},
|
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
|
||||||
|
@ -577,8 +628,8 @@ static RPCHelpMan getblockheader()
|
||||||
|
|
||||||
const CBlockIndex* pblockindex;
|
const CBlockIndex* pblockindex;
|
||||||
const CBlockIndex* tip;
|
const CBlockIndex* tip;
|
||||||
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
{
|
{
|
||||||
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
|
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
|
||||||
tip = chainman.ActiveChain().Tip();
|
tip = chainman.ActiveChain().Tip();
|
||||||
|
@ -596,7 +647,7 @@ static RPCHelpMan getblockheader()
|
||||||
return strHex;
|
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, "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_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
|
||||||
{RPCResult::Type::NUM, "nonce", "The nonce"},
|
{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::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::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"},
|
{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;
|
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, "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::NUM, "headers", "the current number of headers we have validated"},
|
||||||
{RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
|
{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, "difficulty", "the current difficulty"},
|
||||||
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
|
{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_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
|
||||||
|
@ -1334,6 +1388,8 @@ RPCHelpMan getblockchaininfo()
|
||||||
obj.pushKV("blocks", height);
|
obj.pushKV("blocks", height);
|
||||||
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
|
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
|
||||||
obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
|
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("difficulty", GetDifficulty(tip));
|
||||||
obj.pushKV("time", tip.GetBlockTime());
|
obj.pushKV("time", tip.GetBlockTime());
|
||||||
obj.pushKV("mediantime", tip.GetMedianTimePast());
|
obj.pushKV("mediantime", tip.GetMedianTimePast());
|
||||||
|
@ -3301,6 +3357,8 @@ static RPCHelpMan loadtxoutset()
|
||||||
const std::vector<RPCResult> RPCHelpForChainstate{
|
const std::vector<RPCResult> RPCHelpForChainstate{
|
||||||
{RPCResult::Type::NUM, "blocks", "number of blocks in this chainstate"},
|
{RPCResult::Type::NUM, "blocks", "number of blocks in this chainstate"},
|
||||||
{RPCResult::Type::STR_HEX, "bestblockhash", "blockhash of the tip"},
|
{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, "difficulty", "difficulty of the tip"},
|
||||||
{RPCResult::Type::NUM, "verificationprogress", "progress towards the network 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"},
|
{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("blocks", (int)chain.Height());
|
||||||
data.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
|
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("difficulty", GetDifficulty(*tip));
|
||||||
data.pushKV("verificationprogress", chainman.GuessVerificationProgress(tip));
|
data.pushKV("verificationprogress", chainman.GuessVerificationProgress(tip));
|
||||||
data.pushKV("coins_db_cache_bytes", cs.m_coinsdb_cache_size_bytes);
|
data.pushKV("coins_db_cache_bytes", cs.m_coinsdb_cache_size_bytes);
|
||||||
|
@ -3382,6 +3442,7 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
|
||||||
{"blockchain", &getblockheader},
|
{"blockchain", &getblockheader},
|
||||||
{"blockchain", &getchaintips},
|
{"blockchain", &getchaintips},
|
||||||
{"blockchain", &getdifficulty},
|
{"blockchain", &getdifficulty},
|
||||||
|
{"blockchain", &gettarget},
|
||||||
{"blockchain", &getdeploymentinfo},
|
{"blockchain", &getdeploymentinfo},
|
||||||
{"blockchain", &gettxout},
|
{"blockchain", &gettxout},
|
||||||
{"blockchain", &gettxoutsetinfo},
|
{"blockchain", &gettxoutsetinfo},
|
||||||
|
|
|
@ -36,10 +36,10 @@ static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
|
||||||
double GetDifficulty(const CBlockIndex& blockindex);
|
double GetDifficulty(const CBlockIndex& blockindex);
|
||||||
|
|
||||||
/** Block description to JSON */
|
/** 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 */
|
/** 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 */
|
/** 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);
|
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
|
||||||
|
|
|
@ -114,6 +114,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "getblock", 1, "verbose" },
|
{ "getblock", 1, "verbose" },
|
||||||
{ "getblockheader", 1, "verbose" },
|
{ "getblockheader", 1, "verbose" },
|
||||||
{ "getchaintxstats", 0, "nblocks" },
|
{ "getchaintxstats", 0, "nblocks" },
|
||||||
|
{ "getdifficulty", 0, "next" },
|
||||||
|
{ "gettarget", 0, "next" },
|
||||||
{ "gettransaction", 1, "include_watchonly" },
|
{ "gettransaction", 1, "include_watchonly" },
|
||||||
{ "gettransaction", 2, "verbose" },
|
{ "gettransaction", 2, "verbose" },
|
||||||
{ "getrawtransaction", 1, "verbosity" },
|
{ "getrawtransaction", 1, "verbosity" },
|
||||||
|
|
|
@ -421,11 +421,20 @@ static RPCHelpMan getmininginfo()
|
||||||
{RPCResult::Type::NUM, "blocks", "The current block"},
|
{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, "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::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::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, "networkhashps", "The network hashes per second"},
|
||||||
{RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
|
{RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
|
||||||
{RPCResult::Type::STR, "chain", "current network name (" LIST_CHAIN_NAMES ")"},
|
{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::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") ?
|
(IsDeprecatedRPCEnabled("warnings") ?
|
||||||
RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
|
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)",
|
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);
|
ChainstateManager& chainman = EnsureChainman(node);
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
const CChain& active_chain = chainman.ActiveChain();
|
const CChain& active_chain = chainman.ActiveChain();
|
||||||
|
CBlockIndex& tip{*CHECK_NONFATAL(active_chain.Tip())};
|
||||||
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
obj.pushKV("blocks", active_chain.Height());
|
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_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);
|
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("networkhashps", getnetworkhashps().HandleRequest(request));
|
||||||
obj.pushKV("pooledtx", (uint64_t)mempool.size());
|
obj.pushKV("pooledtx", (uint64_t)mempool.size());
|
||||||
obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
|
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) {
|
if (chainman.GetParams().GetChainType() == ChainType::SIGNET) {
|
||||||
const std::vector<uint8_t>& signet_challenge =
|
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("signet_challenge", HexStr(signet_challenge));
|
||||||
}
|
}
|
||||||
obj.pushKV("warnings", node::GetWarningsForRpc(*CHECK_NONFATAL(node.warnings), IsDeprecatedRPCEnabled("warnings")));
|
obj.pushKV("warnings", node::GetWarningsForRpc(*CHECK_NONFATAL(node.warnings), IsDeprecatedRPCEnabled("warnings")));
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
|
|
||||||
#include <rpc/server_util.h>
|
#include <rpc/server_util.h>
|
||||||
|
|
||||||
|
#include <chain.h>
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
#include <net_processing.h>
|
#include <net_processing.h>
|
||||||
#include <node/context.h>
|
#include <node/context.h>
|
||||||
|
#include <node/miner.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
|
#include <pow.h>
|
||||||
#include <rpc/protocol.h>
|
#include <rpc/protocol.h>
|
||||||
#include <rpc/request.h>
|
#include <rpc/request.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
@ -17,6 +20,7 @@
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|
||||||
using node::NodeContext;
|
using node::NodeContext;
|
||||||
|
using node::UpdateTime;
|
||||||
|
|
||||||
NodeContext& EnsureAnyNodeContext(const std::any& context)
|
NodeContext& EnsureAnyNodeContext(const std::any& context)
|
||||||
{
|
{
|
||||||
|
@ -129,3 +133,18 @@ AddrMan& EnsureAnyAddrman(const std::any& context)
|
||||||
{
|
{
|
||||||
return EnsureAddrman(EnsureAnyNodeContext(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;
|
||||||
|
}
|
||||||
|
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|
||||||
|
#include <consensus/params.h>
|
||||||
|
|
||||||
class AddrMan;
|
class AddrMan;
|
||||||
class ArgsManager;
|
class ArgsManager;
|
||||||
|
class CBlockIndex;
|
||||||
class CBlockPolicyEstimator;
|
class CBlockPolicyEstimator;
|
||||||
class CConnman;
|
class CConnman;
|
||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
|
@ -39,4 +42,7 @@ PeerManager& EnsurePeerman(const node::NodeContext& node);
|
||||||
AddrMan& EnsureAddrman(const node::NodeContext& node);
|
AddrMan& EnsureAddrman(const node::NodeContext& node);
|
||||||
AddrMan& EnsureAnyAddrman(const std::any& context);
|
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
|
#endif // BITCOIN_RPC_SERVER_UTIL_H
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <chain.h>
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
#include <common/messages.h>
|
#include <common/messages.h>
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <node/types.h>
|
#include <node/types.h>
|
||||||
#include <outputtype.h>
|
#include <outputtype.h>
|
||||||
|
#include <pow.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <script/descriptor.h>
|
#include <script/descriptor.h>
|
||||||
#include <script/interpreter.h>
|
#include <script/interpreter.h>
|
||||||
|
@ -1418,3 +1420,9 @@ std::vector<RPCResult> ScriptPubKeyDoc() {
|
||||||
{RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"},
|
{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);
|
||||||
|
}
|
||||||
|
|
|
@ -516,4 +516,14 @@ void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj);
|
||||||
|
|
||||||
std::vector<RPCResult> ScriptPubKeyDoc();
|
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
|
#endif // BITCOIN_RPC_UTIL_H
|
||||||
|
|
|
@ -147,6 +147,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
|
||||||
"getorphantxs",
|
"getorphantxs",
|
||||||
"getpeerinfo",
|
"getpeerinfo",
|
||||||
"getprioritisedtransactions",
|
"getprioritisedtransactions",
|
||||||
|
"gettarget",
|
||||||
"getrawaddrman",
|
"getrawaddrman",
|
||||||
"getrawmempool",
|
"getrawmempool",
|
||||||
"getrawtransaction",
|
"getrawtransaction",
|
||||||
|
|
2015
test/functional/data/testnet4.hex
Normal file
2015
test/functional/data/testnet4.hex
Normal file
File diff suppressed because one or more lines are too long
|
@ -38,6 +38,12 @@ from test_framework.wallet import (
|
||||||
getnewdestination,
|
getnewdestination,
|
||||||
MiniWallet,
|
MiniWallet,
|
||||||
)
|
)
|
||||||
|
from test_framework.blocktools import (
|
||||||
|
REGTEST_N_BITS,
|
||||||
|
REGTEST_TARGET,
|
||||||
|
nbits_str,
|
||||||
|
target_str,
|
||||||
|
)
|
||||||
|
|
||||||
START_HEIGHT = 199
|
START_HEIGHT = 199
|
||||||
SNAPSHOT_BASE_HEIGHT = 299
|
SNAPSHOT_BASE_HEIGHT = 299
|
||||||
|
@ -229,6 +235,12 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||||
assert_equal(normal['blocks'], START_HEIGHT + 99)
|
assert_equal(normal['blocks'], START_HEIGHT + 99)
|
||||||
assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT)
|
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
|
# Now lets sync the nodes and wait for the background validation to finish
|
||||||
self.connect_nodes(0, 3)
|
self.connect_nodes(0, 3)
|
||||||
self.sync_blocks(nodes=(n0, n3))
|
self.sync_blocks(nodes=(n0, n3))
|
||||||
|
|
|
@ -12,6 +12,7 @@ from test_framework.blocktools import (
|
||||||
create_tx_with_script,
|
create_tx_with_script,
|
||||||
get_legacy_sigopcount_block,
|
get_legacy_sigopcount_block,
|
||||||
MAX_BLOCK_SIGOPS,
|
MAX_BLOCK_SIGOPS,
|
||||||
|
REGTEST_N_BITS,
|
||||||
)
|
)
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
CBlock,
|
CBlock,
|
||||||
|
@ -590,7 +591,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||||
b44 = CBlock()
|
b44 = CBlock()
|
||||||
b44.nTime = self.tip.nTime + 1
|
b44.nTime = self.tip.nTime + 1
|
||||||
b44.hashPrevBlock = self.tip.sha256
|
b44.hashPrevBlock = self.tip.sha256
|
||||||
b44.nBits = 0x207fffff
|
b44.nBits = REGTEST_N_BITS
|
||||||
b44.vtx.append(coinbase)
|
b44.vtx.append(coinbase)
|
||||||
tx = self.create_and_sign_transaction(out[14], 1)
|
tx = self.create_and_sign_transaction(out[14], 1)
|
||||||
b44.vtx.append(tx)
|
b44.vtx.append(tx)
|
||||||
|
@ -606,7 +607,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||||
b45 = CBlock()
|
b45 = CBlock()
|
||||||
b45.nTime = self.tip.nTime + 1
|
b45.nTime = self.tip.nTime + 1
|
||||||
b45.hashPrevBlock = self.tip.sha256
|
b45.hashPrevBlock = self.tip.sha256
|
||||||
b45.nBits = 0x207fffff
|
b45.nBits = REGTEST_N_BITS
|
||||||
b45.vtx.append(non_coinbase)
|
b45.vtx.append(non_coinbase)
|
||||||
b45.hashMerkleRoot = b45.calc_merkle_root()
|
b45.hashMerkleRoot = b45.calc_merkle_root()
|
||||||
b45.solve()
|
b45.solve()
|
||||||
|
@ -620,7 +621,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||||
b46 = CBlock()
|
b46 = CBlock()
|
||||||
b46.nTime = b44.nTime + 1
|
b46.nTime = b44.nTime + 1
|
||||||
b46.hashPrevBlock = b44.sha256
|
b46.hashPrevBlock = b44.sha256
|
||||||
b46.nBits = 0x207fffff
|
b46.nBits = REGTEST_N_BITS
|
||||||
b46.vtx = []
|
b46.vtx = []
|
||||||
b46.hashMerkleRoot = 0
|
b46.hashMerkleRoot = 0
|
||||||
b46.solve()
|
b46.solve()
|
||||||
|
|
|
@ -289,7 +289,7 @@ class RESTTest (BitcoinTestFramework):
|
||||||
|
|
||||||
# Compare with normal RPC block response
|
# Compare with normal RPC block response
|
||||||
rpc_block_json = self.nodes[0].getblock(bb_hash)
|
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])
|
assert_equal(json_obj[0][key], rpc_block_json[key])
|
||||||
|
|
||||||
# See if we can get 5 headers in one response
|
# See if we can get 5 headers in one response
|
||||||
|
|
|
@ -16,6 +16,10 @@ from test_framework.blocktools import (
|
||||||
get_witness_script,
|
get_witness_script,
|
||||||
NORMAL_GBT_REQUEST_PARAMS,
|
NORMAL_GBT_REQUEST_PARAMS,
|
||||||
TIME_GENESIS_BLOCK,
|
TIME_GENESIS_BLOCK,
|
||||||
|
REGTEST_N_BITS,
|
||||||
|
REGTEST_TARGET,
|
||||||
|
nbits_str,
|
||||||
|
target_str,
|
||||||
)
|
)
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
BLOCK_HEADER_SIZE,
|
BLOCK_HEADER_SIZE,
|
||||||
|
@ -206,7 +210,15 @@ class MiningTest(BitcoinTestFramework):
|
||||||
assert_equal(mining_info['chain'], self.chain)
|
assert_equal(mining_info['chain'], self.chain)
|
||||||
assert 'currentblocktx' not in mining_info
|
assert 'currentblocktx' not in mining_info
|
||||||
assert 'currentblockweight' 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['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['networkhashps'], Decimal('0.003333333333333334'))
|
||||||
assert_equal(mining_info['pooledtx'], 0)
|
assert_equal(mining_info['pooledtx'], 0)
|
||||||
|
|
||||||
|
|
73
test/functional/mining_testnet.py
Executable file
73
test/functional/mining_testnet.py
Executable 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()
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/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
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
"""Test RPCs related to blockchainstate.
|
"""Test RPCs related to blockchainstate.
|
||||||
|
@ -11,6 +11,7 @@ Test the following RPCs:
|
||||||
- gettxoutsetinfo
|
- gettxoutsetinfo
|
||||||
- getblockheader
|
- getblockheader
|
||||||
- getdifficulty
|
- getdifficulty
|
||||||
|
- gettarget
|
||||||
- getnetworkhashps
|
- getnetworkhashps
|
||||||
- waitforblockheight
|
- waitforblockheight
|
||||||
- getblock
|
- getblock
|
||||||
|
@ -30,9 +31,13 @@ import textwrap
|
||||||
from test_framework.blocktools import (
|
from test_framework.blocktools import (
|
||||||
MAX_FUTURE_BLOCK_TIME,
|
MAX_FUTURE_BLOCK_TIME,
|
||||||
TIME_GENESIS_BLOCK,
|
TIME_GENESIS_BLOCK,
|
||||||
|
REGTEST_N_BITS,
|
||||||
|
REGTEST_TARGET,
|
||||||
create_block,
|
create_block,
|
||||||
create_coinbase,
|
create_coinbase,
|
||||||
create_tx_with_script,
|
create_tx_with_script,
|
||||||
|
nbits_str,
|
||||||
|
target_str,
|
||||||
)
|
)
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
CBlockHeader,
|
CBlockHeader,
|
||||||
|
@ -88,6 +93,7 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
self._test_gettxoutsetinfo()
|
self._test_gettxoutsetinfo()
|
||||||
self._test_getblockheader()
|
self._test_getblockheader()
|
||||||
self._test_getdifficulty()
|
self._test_getdifficulty()
|
||||||
|
self._test_gettarget()
|
||||||
self._test_getnetworkhashps()
|
self._test_getnetworkhashps()
|
||||||
self._test_stopatheight()
|
self._test_stopatheight()
|
||||||
self._test_waitforblock() # also tests waitfornewblock
|
self._test_waitforblock() # also tests waitfornewblock
|
||||||
|
@ -129,6 +135,7 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
|
|
||||||
keys = [
|
keys = [
|
||||||
'bestblockhash',
|
'bestblockhash',
|
||||||
|
'bits',
|
||||||
'blocks',
|
'blocks',
|
||||||
'chain',
|
'chain',
|
||||||
'chainwork',
|
'chainwork',
|
||||||
|
@ -138,6 +145,7 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
'mediantime',
|
'mediantime',
|
||||||
'pruned',
|
'pruned',
|
||||||
'size_on_disk',
|
'size_on_disk',
|
||||||
|
'target',
|
||||||
'time',
|
'time',
|
||||||
'verificationprogress',
|
'verificationprogress',
|
||||||
'warnings',
|
'warnings',
|
||||||
|
@ -194,6 +202,9 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
assert_equal(res['prune_target_size'], 576716800)
|
assert_equal(res['prune_target_size'], 576716800)
|
||||||
assert_greater_than(res['size_on_disk'], 0)
|
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):
|
def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash, status_next):
|
||||||
assert height >= 144 and height <= 287
|
assert height >= 144 and height <= 287
|
||||||
|
|
||||||
|
@ -412,7 +423,8 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
assert_is_hash_string(header['hash'])
|
assert_is_hash_string(header['hash'])
|
||||||
assert_is_hash_string(header['previousblockhash'])
|
assert_is_hash_string(header['previousblockhash'])
|
||||||
assert_is_hash_string(header['merkleroot'])
|
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 isinstance(header['time'], int)
|
||||||
assert_equal(header['mediantime'], TIME_RANGE_MTP)
|
assert_equal(header['mediantime'], TIME_RANGE_MTP)
|
||||||
assert isinstance(header['nonce'], int)
|
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
|
# 1 hash in 2 should be valid, so difficulty should be 1/2**31
|
||||||
# binary => decimal => binary math is why we do this check
|
# binary => decimal => binary math is why we do this check
|
||||||
assert abs(difficulty * 2**31 - 1) < 0.0001
|
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):
|
def _test_getnetworkhashps(self):
|
||||||
self.log.info("Test getnetworkhashps")
|
self.log.info("Test getnetworkhashps")
|
||||||
|
|
|
@ -27,6 +27,7 @@ from .messages import (
|
||||||
hash256,
|
hash256,
|
||||||
ser_uint256,
|
ser_uint256,
|
||||||
tx_from_hex,
|
tx_from_hex,
|
||||||
|
uint256_from_compact,
|
||||||
uint256_from_str,
|
uint256_from_str,
|
||||||
WITNESS_SCALE_FACTOR,
|
WITNESS_SCALE_FACTOR,
|
||||||
)
|
)
|
||||||
|
@ -65,6 +66,23 @@ NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
|
||||||
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
|
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
|
||||||
MIN_BLOCKS_TO_KEEP = 288
|
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):
|
def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None):
|
||||||
"""Create a block (with regtest difficulty)."""
|
"""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:
|
if tmpl and tmpl.get('bits') is not None:
|
||||||
block.nBits = struct.unpack('>I', bytes.fromhex(tmpl['bits']))[0]
|
block.nBits = struct.unpack('>I', bytes.fromhex(tmpl['bits']))[0]
|
||||||
else:
|
else:
|
||||||
block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
|
block.nBits = REGTEST_N_BITS
|
||||||
if coinbase is None:
|
if coinbase is None:
|
||||||
coinbase = create_coinbase(height=tmpl['height'])
|
coinbase = create_coinbase(height=tmpl['height'])
|
||||||
block.vtx.append(coinbase)
|
block.vtx.append(coinbase)
|
||||||
|
|
|
@ -316,6 +316,7 @@ BASE_SCRIPTS = [
|
||||||
'wallet_upgradewallet.py --legacy-wallet',
|
'wallet_upgradewallet.py --legacy-wallet',
|
||||||
'wallet_crosschain.py',
|
'wallet_crosschain.py',
|
||||||
'mining_basic.py',
|
'mining_basic.py',
|
||||||
|
'mining_testnet.py',
|
||||||
'feature_signet.py',
|
'feature_signet.py',
|
||||||
'p2p_mutated_blocks.py',
|
'p2p_mutated_blocks.py',
|
||||||
'wallet_implicitsegwit.py --legacy-wallet',
|
'wallet_implicitsegwit.py --legacy-wallet',
|
||||||
|
|
|
@ -9,6 +9,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from test_framework.blocktools import DIFF_1_N_BITS
|
||||||
from test_framework.key import ECKey
|
from test_framework.key import ECKey
|
||||||
from test_framework.script_util import key_to_p2wpkh_script
|
from test_framework.script_util import key_to_p2wpkh_script
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
@ -55,7 +56,7 @@ class SignetMinerTest(BitcoinTestFramework):
|
||||||
'generate',
|
'generate',
|
||||||
f'--address={node.getnewaddress()}',
|
f'--address={node.getnewaddress()}',
|
||||||
f'--grind-cmd={self.options.bitcoinutil} grind',
|
f'--grind-cmd={self.options.bitcoinutil} grind',
|
||||||
'--nbits=1d00ffff',
|
f'--nbits={hex(DIFF_1_N_BITS)}',
|
||||||
f'--set-block-time={int(time.time())}',
|
f'--set-block-time={int(time.time())}',
|
||||||
'--poolnum=99',
|
'--poolnum=99',
|
||||||
], check=True, stderr=subprocess.STDOUT)
|
], check=True, stderr=subprocess.STDOUT)
|
||||||
|
|
Loading…
Reference in a new issue