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};
|
||||
} else if (name == "csv") {
|
||||
consensus.CSVHeight = int{height};
|
||||
} else if (name == "randomx") {
|
||||
consensus.randomXActivationHeight = int{height};
|
||||
} else {
|
||||
throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include <algorithm>
|
||||
#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 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:
|
||||
if (consensusParams.fPowAllowMinDifficultyBlocks)
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams, ALGO_SHA256D);
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams, algo);
|
||||
|
||||
return nNewTime - nOldTime;
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|||
// Fill in header
|
||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||
pblock->SetAlgo(algo);
|
||||
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
|
||||
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev, algo);
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus(), algo);
|
||||
pblock->nNonce = 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 */
|
||||
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 */
|
||||
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
|
||||
|
|
|
|||
|
|
@ -639,7 +639,12 @@ static RPCHelpMan getblocktemplate()
|
|||
if (strAlgo == "sha256d") {
|
||||
algo = ALGO_SHA256D;
|
||||
} 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");
|
||||
}
|
||||
algo = ALGO_RANDOMX;
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown proof-of-work algorithm");
|
||||
}
|
||||
|
|
@ -809,7 +814,7 @@ static RPCHelpMan getblocktemplate()
|
|||
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
|
||||
|
||||
// Update nTime
|
||||
UpdateTime(pblock, consensusParams, pindexPrev);
|
||||
UpdateTime(pblock, consensusParams, pindexPrev, algo);
|
||||
pblock->nNonce = 0;
|
||||
|
||||
// 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,
|
||||
BlockValidationState& state,
|
||||
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.
|
||||
* 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 &&
|
||||
block.GetHash() != m_params.GetConsensus().hashGenesisBlock &&
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
@ -3160,7 +3161,8 @@ static bool CheckContextualRandomXProofOfWork(
|
|||
const CBlockHeader& block,
|
||||
BlockValidationState& state,
|
||||
const Consensus::Params& consensusParams,
|
||||
const CBlockIndex* pindexPrev)
|
||||
const CBlockIndex* pindexPrev,
|
||||
bool fCheckPOW)
|
||||
{
|
||||
assert(pindexPrev != nullptr);
|
||||
assert(block.GetAlgo() == ALGO_RANDOMX);
|
||||
|
|
@ -3185,6 +3187,9 @@ static bool CheckContextualRandomXProofOfWork(
|
|||
"incorrect RandomX proof of work difficulty");
|
||||
}
|
||||
|
||||
if (!fCheckPOW)
|
||||
return true;
|
||||
|
||||
return CheckRandomXProofOfWork(
|
||||
block,
|
||||
state,
|
||||
|
|
@ -3197,6 +3202,16 @@ static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& st
|
|||
if (!fCheckPOW)
|
||||
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();
|
||||
|
||||
// SHA256D proof of work is context-free.
|
||||
|
|
@ -3357,7 +3372,7 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
|
|||
* in ConnectBlock().
|
||||
* 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);
|
||||
const int nHeight = pindexPrev->nHeight + 1;
|
||||
|
|
@ -3367,7 +3382,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|||
const int algo = block.GetAlgo();
|
||||
|
||||
if (algo == ALGO_RANDOMX) {
|
||||
if (!CheckContextualRandomXProofOfWork(block, state, consensusParams, pindexPrev))
|
||||
if (!CheckContextualRandomXProofOfWork(block, state, consensusParams, pindexPrev, fCheckPOW))
|
||||
return false;
|
||||
} else if (algo == ALGO_SHA256D) {
|
||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams, ALGO_SHA256D))
|
||||
|
|
@ -3751,7 +3766,7 @@ bool TestBlockValidity(BlockValidationState& state,
|
|||
indexDummy.phashBlock = &block_hash;
|
||||
|
||||
// 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());
|
||||
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
||||
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',
|
||||
# vv Tests less than 5m vv
|
||||
'mining_getblocktemplate_longpoll.py',
|
||||
'mining_randomx_gbt.py',
|
||||
'feature_maxuploadtarget.py',
|
||||
'feature_block.py',
|
||||
'rpc_fundrawtransaction.py --legacy-wallet',
|
||||
|
|
|
|||
Loading…
Reference in a new issue