Make RandomX block templates activation-aware
This commit is contained in:
parent
3b26ac9397
commit
00158bc780
7 changed files with 104 additions and 12 deletions
|
|
@ -592,6 +592,8 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse
|
||||||
consensus.BIP65Height = int{height};
|
consensus.BIP65Height = int{height};
|
||||||
} else if (name == "csv") {
|
} else if (name == "csv") {
|
||||||
consensus.CSVHeight = int{height};
|
consensus.CSVHeight = int{height};
|
||||||
|
} else if (name == "randomx") {
|
||||||
|
consensus.randomXActivationHeight = int{height};
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
|
throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
|
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int algo)
|
||||||
{
|
{
|
||||||
int64_t nOldTime = pblock->nTime;
|
int64_t nOldTime = pblock->nTime;
|
||||||
int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
|
int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
|
||||||
|
|
@ -36,7 +36,7 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
|
||||||
|
|
||||||
// Updating time can change work required on testnet:
|
// Updating time can change work required on testnet:
|
||||||
if (consensusParams.fPowAllowMinDifficultyBlocks)
|
if (consensusParams.fPowAllowMinDifficultyBlocks)
|
||||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams, ALGO_SHA256D);
|
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams, algo);
|
||||||
|
|
||||||
return nNewTime - nOldTime;
|
return nNewTime - nOldTime;
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +172,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
||||||
// Fill in header
|
// Fill in header
|
||||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||||
pblock->SetAlgo(algo);
|
pblock->SetAlgo(algo);
|
||||||
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
|
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev, algo);
|
||||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus(), algo);
|
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus(), algo);
|
||||||
pblock->nNonce = 0;
|
pblock->nNonce = 0;
|
||||||
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
|
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ void RegenerateCommitments(CBlock& block, const CBlockIndex* pindexPrev, const C
|
||||||
|
|
||||||
/** Modify the extranonce in a block */
|
/** Modify the extranonce in a block */
|
||||||
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
||||||
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
|
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int algo);
|
||||||
|
|
||||||
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
|
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
|
||||||
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
|
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
|
||||||
|
|
|
||||||
|
|
@ -639,7 +639,12 @@ static RPCHelpMan getblocktemplate()
|
||||||
if (strAlgo == "sha256d") {
|
if (strAlgo == "sha256d") {
|
||||||
algo = ALGO_SHA256D;
|
algo = ALGO_SHA256D;
|
||||||
} else if (strAlgo == "randomx") {
|
} else if (strAlgo == "randomx") {
|
||||||
|
const CBlockIndex* tip = chainman.ActiveChain().Tip();
|
||||||
|
if (tip == nullptr ||
|
||||||
|
tip->nHeight + 1 < Params().GetConsensus().randomXActivationHeight) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "RandomX is not active yet");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "RandomX is not active yet");
|
||||||
|
}
|
||||||
|
algo = ALGO_RANDOMX;
|
||||||
} else {
|
} else {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown proof-of-work algorithm");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown proof-of-work algorithm");
|
||||||
}
|
}
|
||||||
|
|
@ -809,7 +814,7 @@ static RPCHelpMan getblocktemplate()
|
||||||
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
|
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
|
||||||
|
|
||||||
// Update nTime
|
// Update nTime
|
||||||
UpdateTime(pblock, consensusParams, pindexPrev);
|
UpdateTime(pblock, consensusParams, pindexPrev, algo);
|
||||||
pblock->nNonce = 0;
|
pblock->nNonce = 0;
|
||||||
|
|
||||||
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
|
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
|
||||||
|
|
|
||||||
|
|
@ -1751,7 +1751,8 @@ static bool CheckContextualRandomXProofOfWork(
|
||||||
const CBlockHeader& block,
|
const CBlockHeader& block,
|
||||||
BlockValidationState& state,
|
BlockValidationState& state,
|
||||||
const Consensus::Params& consensusParams,
|
const Consensus::Params& consensusParams,
|
||||||
const CBlockIndex* pindexPrev);
|
const CBlockIndex* pindexPrev,
|
||||||
|
bool fCheckPOW);
|
||||||
|
|
||||||
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
|
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
|
||||||
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
||||||
|
|
@ -1793,7 +1794,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
||||||
if (!fJustCheck &&
|
if (!fJustCheck &&
|
||||||
block.GetHash() != m_params.GetConsensus().hashGenesisBlock &&
|
block.GetHash() != m_params.GetConsensus().hashGenesisBlock &&
|
||||||
block.GetAlgo() == ALGO_RANDOMX &&
|
block.GetAlgo() == ALGO_RANDOMX &&
|
||||||
!CheckContextualRandomXProofOfWork(block, state, m_params.GetConsensus(), pindex->pprev)) {
|
!CheckContextualRandomXProofOfWork(block, state, m_params.GetConsensus(), pindex->pprev, true)) {
|
||||||
return error("%s: contextual RandomX proof of work check failed: %s", __func__, state.ToString());
|
return error("%s: contextual RandomX proof of work check failed: %s", __func__, state.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3160,7 +3161,8 @@ static bool CheckContextualRandomXProofOfWork(
|
||||||
const CBlockHeader& block,
|
const CBlockHeader& block,
|
||||||
BlockValidationState& state,
|
BlockValidationState& state,
|
||||||
const Consensus::Params& consensusParams,
|
const Consensus::Params& consensusParams,
|
||||||
const CBlockIndex* pindexPrev)
|
const CBlockIndex* pindexPrev,
|
||||||
|
bool fCheckPOW)
|
||||||
{
|
{
|
||||||
assert(pindexPrev != nullptr);
|
assert(pindexPrev != nullptr);
|
||||||
assert(block.GetAlgo() == ALGO_RANDOMX);
|
assert(block.GetAlgo() == ALGO_RANDOMX);
|
||||||
|
|
@ -3185,6 +3187,9 @@ static bool CheckContextualRandomXProofOfWork(
|
||||||
"incorrect RandomX proof of work difficulty");
|
"incorrect RandomX proof of work difficulty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fCheckPOW)
|
||||||
|
return true;
|
||||||
|
|
||||||
return CheckRandomXProofOfWork(
|
return CheckRandomXProofOfWork(
|
||||||
block,
|
block,
|
||||||
state,
|
state,
|
||||||
|
|
@ -3197,6 +3202,16 @@ static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& st
|
||||||
if (!fCheckPOW)
|
if (!fCheckPOW)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Genesis predates the multi-algo version-bit encoding. Preserve its
|
||||||
|
// historical SHA256D proof-of-work validation without treating unknown
|
||||||
|
// algorithm version bits as valid for any other block.
|
||||||
|
if (block.GetHash() == consensusParams.hashGenesisBlock) {
|
||||||
|
if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
|
||||||
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "high-hash", "proof of work failed");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const int algo = block.GetAlgo();
|
const int algo = block.GetAlgo();
|
||||||
|
|
||||||
// SHA256D proof of work is context-free.
|
// SHA256D proof of work is context-free.
|
||||||
|
|
@ -3357,7 +3372,7 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
|
||||||
* in ConnectBlock().
|
* in ConnectBlock().
|
||||||
* Note that -reindex-chainstate skips the validation that happens here!
|
* Note that -reindex-chainstate skips the validation that happens here!
|
||||||
*/
|
*/
|
||||||
static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime, bool fCheckPOW = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
assert(pindexPrev != nullptr);
|
assert(pindexPrev != nullptr);
|
||||||
const int nHeight = pindexPrev->nHeight + 1;
|
const int nHeight = pindexPrev->nHeight + 1;
|
||||||
|
|
@ -3367,7 +3382,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
||||||
const int algo = block.GetAlgo();
|
const int algo = block.GetAlgo();
|
||||||
|
|
||||||
if (algo == ALGO_RANDOMX) {
|
if (algo == ALGO_RANDOMX) {
|
||||||
if (!CheckContextualRandomXProofOfWork(block, state, consensusParams, pindexPrev))
|
if (!CheckContextualRandomXProofOfWork(block, state, consensusParams, pindexPrev, fCheckPOW))
|
||||||
return false;
|
return false;
|
||||||
} else if (algo == ALGO_SHA256D) {
|
} else if (algo == ALGO_SHA256D) {
|
||||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams, ALGO_SHA256D))
|
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams, ALGO_SHA256D))
|
||||||
|
|
@ -3751,7 +3766,7 @@ bool TestBlockValidity(BlockValidationState& state,
|
||||||
indexDummy.phashBlock = &block_hash;
|
indexDummy.phashBlock = &block_hash;
|
||||||
|
|
||||||
// NOTE: CheckBlockHeader is called by CheckBlock
|
// NOTE: CheckBlockHeader is called by CheckBlock
|
||||||
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
|
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime(), fCheckPOW))
|
||||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
||||||
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
||||||
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
||||||
|
|
|
||||||
69
test/functional/mining_randomx_gbt.py
Executable file
69
test/functional/mining_randomx_gbt.py
Executable file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2026 The Vexta Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
"""Test activation-aware RandomX getblocktemplate handling."""
|
||||||
|
|
||||||
|
from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS
|
||||||
|
from test_framework.test_framework import DigiByteTestFramework
|
||||||
|
from test_framework.util import assert_equal, assert_raises_rpc_error
|
||||||
|
|
||||||
|
|
||||||
|
BLOCK_VERSION_ALGO = 15 << 8
|
||||||
|
BLOCK_VERSION_SHA256D = 2 << 8
|
||||||
|
BLOCK_VERSION_RANDOMX = 4 << 8
|
||||||
|
|
||||||
|
|
||||||
|
class RandomXGetBlockTemplateTest(DigiByteTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.num_nodes = 1
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.supports_cli = False
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
node = self.nodes[0]
|
||||||
|
|
||||||
|
self.log.info("RandomX GBT is rejected before activation")
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-8,
|
||||||
|
"RandomX is not active yet",
|
||||||
|
node.getblocktemplate,
|
||||||
|
NORMAL_GBT_REQUEST_PARAMS,
|
||||||
|
"randomx",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info("Default GBT remains SHA256D before activation")
|
||||||
|
sha_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
|
||||||
|
assert_equal(sha_template["height"], 1)
|
||||||
|
assert_equal(
|
||||||
|
sha_template["version"] & BLOCK_VERSION_ALGO,
|
||||||
|
BLOCK_VERSION_SHA256D,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info("Activate RandomX at block 1 on regtest only")
|
||||||
|
self.restart_node(0, extra_args=["-testactivationheight=randomx@1"])
|
||||||
|
node = self.nodes[0]
|
||||||
|
|
||||||
|
self.log.info("RandomX GBT is available exactly at activation height")
|
||||||
|
randomx_template = node.getblocktemplate(
|
||||||
|
NORMAL_GBT_REQUEST_PARAMS,
|
||||||
|
"randomx",
|
||||||
|
)
|
||||||
|
assert_equal(randomx_template["height"], 1)
|
||||||
|
assert_equal(
|
||||||
|
randomx_template["version"] & BLOCK_VERSION_ALGO,
|
||||||
|
BLOCK_VERSION_RANDOMX,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info("Default GBT remains SHA256D after RandomX activation")
|
||||||
|
sha_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
|
||||||
|
assert_equal(sha_template["height"], 1)
|
||||||
|
assert_equal(
|
||||||
|
sha_template["version"] & BLOCK_VERSION_ALGO,
|
||||||
|
BLOCK_VERSION_SHA256D,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
RandomXGetBlockTemplateTest().main()
|
||||||
|
|
@ -94,6 +94,7 @@ BASE_SCRIPTS = [
|
||||||
'wallet_backup.py --descriptors',
|
'wallet_backup.py --descriptors',
|
||||||
# vv Tests less than 5m vv
|
# vv Tests less than 5m vv
|
||||||
'mining_getblocktemplate_longpoll.py',
|
'mining_getblocktemplate_longpoll.py',
|
||||||
|
'mining_randomx_gbt.py',
|
||||||
'feature_maxuploadtarget.py',
|
'feature_maxuploadtarget.py',
|
||||||
'feature_block.py',
|
'feature_block.py',
|
||||||
'rpc_fundrawtransaction.py --legacy-wallet',
|
'rpc_fundrawtransaction.py --legacy-wallet',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue