mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 14:37:42 -03:00
Merge bitcoin/bitcoin#31583: rpc: add target to getmininginfo field and show next block info
Some checks are pending
CI / ASan + LSan + UBSan + integer, no depends, USDT (push) Waiting to run
CI / test each commit (push) Waiting to run
CI / macOS 14 native, arm64, no depends, sqlite only, gui (push) Waiting to run
CI / macOS 14 native, arm64, fuzz (push) Waiting to run
CI / Win64 native, VS 2022 (push) Waiting to run
CI / Win64 native fuzz, VS 2022 (push) Waiting to run
Some checks are pending
CI / ASan + LSan + UBSan + integer, no depends, USDT (push) Waiting to run
CI / test each commit (push) Waiting to run
CI / macOS 14 native, arm64, no depends, sqlite only, gui (push) Waiting to run
CI / macOS 14 native, arm64, fuzz (push) Waiting to run
CI / Win64 native, VS 2022 (push) Waiting to run
CI / Win64 native fuzz, VS 2022 (push) Waiting to run
a4df12323c
doc: add release notes (Sjors Provoost)c75872ffdd
test: use DIFF_1_N_BITS in tool_signet_miner (tdb3)4131f322ac
test: check difficulty adjustment using alternate mainnet (Sjors Provoost)c4f68c12e2
Use OP_0 for BIP34 padding in signet and tests (Sjors Provoost)cf0a62878b
rpc: add next to getmininginfo (Sjors Provoost)2d18a078a2
rpc: add target and bits to getchainstates (Sjors Provoost)f153f57acc
rpc: add target and bits to getblockchaininfo (Sjors Provoost)baa504fdfa
rpc: add target to getmininginfo result (Sjors Provoost)2a7bfebd5e
Add target to getblock(header) in RPC and REST (Sjors Provoost)341f932516
rpc: add GetTarget helper (Sjors Provoost)d20d96fa41
test: use REGTEST_N_BITS in feature_block (tdb3)7ddbed4f9f
rpc: add nBits to getmininginfo (Sjors Provoost)ba7b9f3d7b
build: move pow and chain to bitcoin_common (Sjors Provoost)c4cc9e3e9d
consensus: add DeriveTarget() to pow.h (Sjors Provoost) Pull request description: **tl&dr for consensus-code only reviewers**: the first commit splits `CheckProofOfWorkImpl()` in order to create a `DeriveTarget()` helper. The rest of this PR does not touch consensus code. There are three ways to represent the proof-of-work in a block: 1. nBits 2. Difficulty 3. Target The latter notation is useful when you want to compare share work against either the pool target (to get paid) or network difficulty (found an actual block). E.g. for difficulty 1 which corresponds to an nBits value of `0x00ffff`: ``` share hash: f6b973257df982284715b0c7a20640dad709d22b0b1a58f2f88d35886ea5ac45 target: 7fffff0000000000000000000000000000000000000000000000000000000000 ``` It's immediately clear that the share is invalid because the hash is above the target. This type of logging is mostly done by the pool software. It's a nice extra convenience, but not very important. It impacts the following RPC calls: 1. `getmininginfo` displays the `target` for the tip block 2. `getblock` and `getblockheader` display the `target` for a specific block (ditto for their REST equivalents) The `getdifficulty` method is a bit useless in its current state, because what miners really want to know if the difficulty for the _next_ block. So I added a boolean argument `next` to `getdifficulty`. (These values are typically the same, except for the first block in a retarget period. On testnet3 / testnet4 they change when no block is found after 20 minutes). Similarly I added a `next` object to `getmininginfo` which shows `bit`, `difficulty` and `target` for the next block. In order to test the difficulty transition, an alternate mainnet chain with 2016 blocks was generated and used in `mining_mainnet.py`. The chain is deterministic except for its timestamp and nonce values, which are stored in `mainnet_alt.json`. As described at the top, this PR introduces a helper method `DeriveTarget()` which is split out from `CheckProofOfWorkImpl`. The proposed `checkblock` RPC in #31564 needs this helper method internally to figure out the consensus target. Finally, this PR moves `pow.cpp` and `chain.cpp` from `bitcoin_node` to `bitcoin_common`, in order to give `rpc/util.cpp` (which lives in `bitcoin_common`) access to `pow.h`. ACKs for top commit: ismaelsadeeq: re-ACKa4df12323c
tdb3: code review re ACKa4df12323c
ryanofsky: Code review ACKa4df12323c
. Only overall changes since last review were dropping new `gettarget` method and dropping changes to `getdifficulty`, but there were also various internal changes splitting and rearranging commits. Tree-SHA512: edef5633590379c4be007ac96fd1deda8a5b9562ca6ff19fe377cb552b5166f3890d158554c249ab8345977a06da5df07866c9f42ac43ee83dfe3830c61cd169
This commit is contained in:
commit
5acf12bafe
24 changed files with 4385 additions and 37 deletions
9
doc/release-notes-31583.md
Normal file
9
doc/release-notes-31583.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
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.
|
||||
- `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
|
||||
|
||||
REST interface
|
||||
---
|
||||
- `GET /rest/block/<BLOCK-HASH>.json` and `GET /rest/headers/<BLOCK-HASH>.json` now return the current target in the `target` field
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
14
src/pow.cpp
14
src/pow.cpp
|
@ -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)
|
||||
|
|
12
src/pow.h
12
src/pow.h
|
@ -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&);
|
||||
|
|
10
src/rest.cpp
10
src/rest.cpp
|
@ -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);
|
||||
|
|
|
@ -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)));
|
||||
|
@ -553,7 +554,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 +579,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 +598,7 @@ static RPCHelpMan getblockheader()
|
|||
return strHex;
|
||||
}
|
||||
|
||||
return blockheaderToJSON(*tip, *pblockindex);
|
||||
return blockheaderToJSON(*tip, *pblockindex, chainman.GetConsensus().powLimit);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -727,7 +729,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 +805,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 +1299,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 +1339,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 +3308,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 +3352,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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
{
|
||||
{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")));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
42
test/functional/data/README.md
Normal file
42
test/functional/data/README.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Various test vectors
|
||||
|
||||
## mainnet_alt.json
|
||||
|
||||
For easier testing the difficulty is maximally increased in the first (and only)
|
||||
retarget period, by producing blocks approximately 2 minutes apart.
|
||||
|
||||
The alternate mainnet chain was generated as follows:
|
||||
- use faketime to set node clock to 2 minutes after genesis block
|
||||
- mine a block using a CPU miner such as https://github.com/pooler/cpuminer
|
||||
- restart node with a faketime 2 minutes later
|
||||
|
||||
```sh
|
||||
for i in {1..2015}
|
||||
do
|
||||
faketime "`date -d @"$(( 1231006505 + $i * 120 ))" +'%Y-%m-%d %H:%M:%S'`" \
|
||||
bitcoind -connect=0 -nocheckpoints -stopatheight=$i
|
||||
done
|
||||
```
|
||||
|
||||
The CPU miner is kept running as follows:
|
||||
|
||||
```sh
|
||||
./minerd --coinbase-addr 1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r --no-stratum --algo sha256d --no-longpoll --scantime 3 --retry-pause 1
|
||||
```
|
||||
|
||||
The payout address is derived from first BIP32 test vector master key:
|
||||
|
||||
```
|
||||
pkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/44h/0h/0h/<0;1>/*)#fkjtr0yn
|
||||
```
|
||||
|
||||
It uses `pkh()` because `tr()` outputs at low heights are not spendable (`unexpected-witness`).
|
||||
|
||||
This makes each block determinisic except for its timestamp and nonce, which
|
||||
are stored in `mainnet_alt.json` and used to reconstruct the chain without
|
||||
having to redo the proof-of-work.
|
||||
|
||||
The timestamp was not kept constant because at difficulty 1 it's not sufficient
|
||||
to only grind the nonce. Grinding the extra_nonce or version field instead
|
||||
would have required additional (stratum) software. It would also make it more
|
||||
complicated to reconstruct the blocks in this test.
|
4036
test/functional/data/mainnet_alt.json
Normal file
4036
test/functional/data/mainnet_alt.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
105
test/functional/mining_mainnet.py
Executable file
105
test/functional/mining_mainnet.py
Executable file
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2025 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 an alternate mainnet
|
||||
|
||||
Test mining related RPCs that involve difficulty adjustment, which
|
||||
regtest doesn't have.
|
||||
|
||||
It uses an alternate mainnet chain. See data/README.md for how it was generated.
|
||||
|
||||
Mine one retarget period worth of blocks with a short interval in
|
||||
order to maximally raise the difficulty. Verify this using the getmininginfo RPC.
|
||||
|
||||
"""
|
||||
|
||||
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,
|
||||
create_coinbase,
|
||||
nbits_str,
|
||||
target_str
|
||||
)
|
||||
|
||||
from test_framework.messages import (
|
||||
CBlock,
|
||||
)
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
# See data/README.md
|
||||
COINBASE_SCRIPT_PUBKEY="76a914eadbac7f36c37e39361168b7aaee3cb24a25312d88ac"
|
||||
|
||||
class MiningMainnetTest(BitcoinTestFramework):
|
||||
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
self.setup_clean_chain = True
|
||||
self.chain = "" # main
|
||||
|
||||
def add_options(self, parser):
|
||||
parser.add_argument(
|
||||
'--datafile',
|
||||
default='data/mainnet_alt.json',
|
||||
help='Block data file (default: %(default)s)',
|
||||
)
|
||||
|
||||
self.add_wallet_options(parser)
|
||||
|
||||
def mine(self, height, prev_hash, blocks, node, fees=0):
|
||||
self.log.debug(f"height={height}")
|
||||
block = CBlock()
|
||||
block.nVersion = 0x20000000
|
||||
block.hashPrevBlock = int(prev_hash, 16)
|
||||
block.nTime = blocks['timestamps'][height - 1]
|
||||
block.nBits = DIFF_1_N_BITS
|
||||
block.nNonce = blocks['nonces'][height - 1]
|
||||
block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), retarget_period=2016)]
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.rehash()
|
||||
block_hex = block.serialize(with_witness=False).hex()
|
||||
self.log.debug(block_hex)
|
||||
assert_equal(node.submitblock(block_hex), None)
|
||||
prev_hash = node.getbestblockhash()
|
||||
assert_equal(prev_hash, block.hash)
|
||||
return prev_hash
|
||||
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
# Clear disk space warning
|
||||
node.stderr.seek(0)
|
||||
node.stderr.truncate()
|
||||
self.log.info("Load alternative mainnet blocks")
|
||||
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
|
||||
prev_hash = node.getbestblockhash()
|
||||
with open(path, encoding='utf-8') as f:
|
||||
blocks = json.load(f)
|
||||
n_blocks = len(blocks['timestamps'])
|
||||
assert_equal(n_blocks, 2015)
|
||||
for i in range(2015):
|
||||
prev_hash = self.mine(i + 1, prev_hash, blocks, node)
|
||||
|
||||
assert_equal(node.getblockcount(), 2015)
|
||||
|
||||
self.log.info("Check difficulty adjustment with getmininginfo")
|
||||
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))
|
||||
|
||||
if __name__ == '__main__':
|
||||
MiningMainnetTest(__file__).main()
|
|
@ -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.
|
||||
|
@ -30,9 +30,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,
|
||||
|
@ -129,6 +133,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
|
||||
keys = [
|
||||
'bestblockhash',
|
||||
'bits',
|
||||
'blocks',
|
||||
'chain',
|
||||
'chainwork',
|
||||
|
@ -138,6 +143,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
'mediantime',
|
||||
'pruned',
|
||||
'size_on_disk',
|
||||
'target',
|
||||
'time',
|
||||
'verificationprogress',
|
||||
'warnings',
|
||||
|
@ -194,6 +200,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 +421,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)
|
||||
|
|
|
@ -27,6 +27,7 @@ from .messages import (
|
|||
hash256,
|
||||
ser_uint256,
|
||||
tx_from_hex,
|
||||
uint256_from_compact,
|
||||
uint256_from_str,
|
||||
WITNESS_SCALE_FACTOR,
|
||||
)
|
||||
|
@ -34,7 +35,7 @@ from .script import (
|
|||
CScript,
|
||||
CScriptNum,
|
||||
CScriptOp,
|
||||
OP_1,
|
||||
OP_0,
|
||||
OP_RETURN,
|
||||
OP_TRUE,
|
||||
)
|
||||
|
@ -65,6 +66,25 @@ NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
|
|||
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
|
||||
MIN_BLOCKS_TO_KEEP = 288
|
||||
|
||||
REGTEST_RETARGET_PERIOD = 150
|
||||
|
||||
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 +97,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)
|
||||
|
@ -118,12 +138,12 @@ def add_witness_commitment(block, nonce=0):
|
|||
def script_BIP34_coinbase_height(height):
|
||||
if height <= 16:
|
||||
res = CScriptOp.encode_op_n(height)
|
||||
# Append dummy to increase scriptSig size above 2 (see bad-cb-length consensus rule)
|
||||
return CScript([res, OP_1])
|
||||
# Append dummy to increase scriptSig size to 2 (see bad-cb-length consensus rule)
|
||||
return CScript([res, OP_0])
|
||||
return CScript([CScriptNum(height)])
|
||||
|
||||
|
||||
def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50):
|
||||
def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50, retarget_period=REGTEST_RETARGET_PERIOD):
|
||||
"""Create a coinbase transaction.
|
||||
|
||||
If pubkey is passed in, the coinbase output will be a P2PK output;
|
||||
|
@ -136,7 +156,7 @@ def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_scr
|
|||
coinbaseoutput = CTxOut()
|
||||
coinbaseoutput.nValue = nValue * COIN
|
||||
if nValue == 50:
|
||||
halvings = int(height / 150) # regtest
|
||||
halvings = int(height / retarget_period)
|
||||
coinbaseoutput.nValue >>= halvings
|
||||
coinbaseoutput.nValue += fees
|
||||
if pubkey is not None:
|
||||
|
|
|
@ -316,6 +316,7 @@ BASE_SCRIPTS = [
|
|||
'wallet_upgradewallet.py --legacy-wallet',
|
||||
'wallet_crosschain.py',
|
||||
'mining_basic.py',
|
||||
'mining_mainnet.py',
|
||||
'feature_signet.py',
|
||||
'p2p_mutated_blocks.py',
|
||||
'wallet_implicitsegwit.py --legacy-wallet',
|
||||
|
|
|
@ -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={DIFF_1_N_BITS:08x}',
|
||||
f'--set-block-time={int(time.time())}',
|
||||
'--poolnum=99',
|
||||
], check=True, stderr=subprocess.STDOUT)
|
||||
|
|
Loading…
Add table
Reference in a new issue