randomx validation enforcement with activation height
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
#include "script/standard.h"
|
||||
#include "cc/CCinclude.h"
|
||||
#include "sietch.h"
|
||||
#include "pow.h"
|
||||
|
||||
// This is the address for pubkey = 0x000000000000000000000000000000000 (33 bytes)
|
||||
// Funds sent to a burn address can never be spent because the pubkey is invalid
|
||||
@@ -1693,6 +1694,11 @@ int32_t hush_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||
fprintf(stderr,"hush_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed\n",slowflag,height);
|
||||
return(-1);
|
||||
}
|
||||
if ( !CheckRandomXSolution(pblock, height) )
|
||||
{
|
||||
fprintf(stderr,"hush_checkPOW slowflag.%d ht.%d CheckRandomXSolution failed\n",slowflag,height);
|
||||
return(-1);
|
||||
}
|
||||
hash = pblock->GetHash();
|
||||
bnTarget.SetCompact(pblock->nBits,&fNegative,&fOverflow);
|
||||
bhash = UintToArith256(hash);
|
||||
|
||||
@@ -573,6 +573,7 @@ extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_FOUNDERS_REWARD;
|
||||
extern int32_t ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER,ASSETCHAINS_BLOCKTIME;
|
||||
extern uint64_t ASSETCHAINS_TIMELOCKGTE;
|
||||
extern uint32_t ASSETCHAINS_ALGO,ASSETCHAINS_EQUIHASH,ASSETCHAINS_RANDOMX, HUSH_INITDONE;
|
||||
extern int32_t ASSETCHAINS_RANDOMX_VALIDATION;
|
||||
extern int32_t HUSH_MININGTHREADS,HUSH_LONGESTCHAIN,ASSETCHAINS_SEED,IS_HUSH_NOTARY,USE_EXTERNAL_PUBKEY,HUSH_CHOSEN_ONE,HUSH_ON_DEMAND,HUSH_PASSPORT_INITDONE,ASSETCHAINS_STAKED,HUSH_NSPV;
|
||||
extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA,ASSETCHAINS_CBOPRET;
|
||||
extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[],ASSETCHAINS_NK[2];
|
||||
|
||||
@@ -92,6 +92,7 @@ uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff};
|
||||
uint32_t ASSETCHAINS_NONCESHIFT[] = {32};
|
||||
uint32_t ASSETCHAINS_HASHESPERROUND[] = {1};
|
||||
uint32_t ASSETCHAINS_ALGO = _ASSETCHAINS_EQUIHASH;
|
||||
int32_t ASSETCHAINS_RANDOMX_VALIDATION = -1; // activation height for RandomX validation (-1 = disabled)
|
||||
// min diff returned from GetNextWorkRequired needs to be added here for each algo, so they can work with ac_staked.
|
||||
uint32_t ASSETCHAINS_MINDIFF[] = {537857807};
|
||||
int32_t ASSETCHAINS_LWMAPOS = 0; // percentage of blocks should be PoS
|
||||
|
||||
@@ -1916,6 +1916,18 @@ void hush_args(char *argv0)
|
||||
strncpy(SMART_CHAIN_SYMBOL,name.c_str(),sizeof(SMART_CHAIN_SYMBOL)-1);
|
||||
const bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
|
||||
|
||||
// Set RandomX validation activation height per chain
|
||||
if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX) {
|
||||
if (strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) == 0) {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 2838976; // TBD: set to coordinated upgrade height
|
||||
} else if (strncmp(SMART_CHAIN_SYMBOL, "TUMIN", 5) == 0) {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 100; // TBD: set to coordinated upgrade height
|
||||
} else {
|
||||
ASSETCHAINS_RANDOMX_VALIDATION = 1; // all other RandomX HACs: enforce from height 1
|
||||
}
|
||||
printf("ASSETCHAINS_RANDOMX_VALIDATION set to %d for %s\n", ASSETCHAINS_RANDOMX_VALIDATION, SMART_CHAIN_SYMBOL);
|
||||
}
|
||||
|
||||
ASSETCHAINS_LASTERA = GetArg("-ac_eras", 1);
|
||||
if(ishush3) {
|
||||
ASSETCHAINS_LASTERA = 3;
|
||||
|
||||
@@ -4993,6 +4993,8 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,
|
||||
{
|
||||
if ( !CheckEquihashSolution(&blockhdr, Params()) )
|
||||
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution");
|
||||
if ( !CheckRandomXSolution(&blockhdr, height) )
|
||||
return state.DoS(100, error("CheckBlockHeader(): RandomX solution invalid"),REJECT_INVALID, "invalid-randomx-solution");
|
||||
}
|
||||
// Check proof of work matches claimed amount
|
||||
/*hush_index2pubkey33(pubkey33,pindex,height);
|
||||
|
||||
@@ -1011,8 +1011,8 @@ enum RandomXSolverCancelCheck
|
||||
Reason2
|
||||
};
|
||||
|
||||
int GetRandomXInterval() { return GetArg("-ac_randomx_interval",1024); }
|
||||
int GetRandomXBlockLag() { return GetArg("-ac_randomx_lag", 64); }
|
||||
int GetRandomXInterval();
|
||||
int GetRandomXBlockLag();
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
void static RandomXMiner(CWallet *pwallet)
|
||||
@@ -1268,16 +1268,17 @@ void static RandomXMiner()
|
||||
arith_uint256 hashTarget;
|
||||
hashTarget = HASHTarget;
|
||||
|
||||
CRandomXInput rxInput(pblocktemplate->block);
|
||||
CDataStream randomxInput(SER_NETWORK, PROTOCOL_VERSION);
|
||||
// Use the current block as randomx input
|
||||
randomxInput << pblocktemplate->block;
|
||||
// Serialize block header without nSolution but with nNonce for deterministic RandomX input
|
||||
randomxInput << rxInput;
|
||||
|
||||
// std::cerr << "RandomXMiner: randomxInput=" << HexStr(randomxInput) << "\n";
|
||||
// fprintf(stderr,"RandomXMiner: created randomxKey=%s , randomxInput.size=%lu\n", randomxKey, randomxInput.size() ); //randomxInput);
|
||||
rxdebug("%s: randomxKey=%s randomxInput=%s\n", randomxKey, HexStr(randomxInput).c_str());
|
||||
|
||||
rxdebug("%s: calculating randomx hash\n");
|
||||
randomx_calculate_hash(myVM, &randomxInput, sizeof randomxInput, randomxHash);
|
||||
randomx_calculate_hash(myVM, &randomxInput[0], randomxInput.size(), randomxHash);
|
||||
rxdebug("%s: calculated randomx hash\n");
|
||||
|
||||
rxdebug("%s: randomxHash=");
|
||||
|
||||
102
src/pow.cpp
102
src/pow.cpp
@@ -28,6 +28,8 @@
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
#include "sodium.h"
|
||||
#include "RandomX/src/randomx.h"
|
||||
#include <mutex>
|
||||
|
||||
#ifdef ENABLE_RUST
|
||||
#include "librustzcash.h"
|
||||
@@ -683,6 +685,106 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetRandomXInterval() { return GetArg("-ac_randomx_interval", 1024); }
|
||||
int GetRandomXBlockLag() { return GetArg("-ac_randomx_lag", 64); }
|
||||
|
||||
// Cached RandomX validation state — reused across calls, protected by mutex
|
||||
static std::mutex cs_randomx_validator;
|
||||
static randomx_cache *s_rxCache = nullptr;
|
||||
static randomx_vm *s_rxVM = nullptr;
|
||||
static std::string s_rxCurrentKey; // tracks current key to avoid re-init
|
||||
|
||||
CBlockIndex *hush_chainactive(int32_t height);
|
||||
|
||||
bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height)
|
||||
{
|
||||
// Only applies to RandomX chains
|
||||
if (ASSETCHAINS_ALGO != ASSETCHAINS_RANDOMX)
|
||||
return true;
|
||||
|
||||
// Disabled if activation height is negative
|
||||
if (ASSETCHAINS_RANDOMX_VALIDATION < 0)
|
||||
return true;
|
||||
|
||||
// Not yet at activation height
|
||||
if (height < ASSETCHAINS_RANDOMX_VALIDATION)
|
||||
return true;
|
||||
|
||||
// Do not affect initial block loading
|
||||
extern int32_t HUSH_LOADINGBLOCKS;
|
||||
if (HUSH_LOADINGBLOCKS != 0)
|
||||
return true;
|
||||
|
||||
// nSolution must be exactly RANDOMX_HASH_SIZE (32) bytes
|
||||
if (pblock->nSolution.size() != RANDOMX_HASH_SIZE) {
|
||||
return error("CheckRandomXSolution(): nSolution size %u != expected %d at height %d",
|
||||
pblock->nSolution.size(), RANDOMX_HASH_SIZE, height);
|
||||
}
|
||||
|
||||
static int randomxInterval = GetRandomXInterval();
|
||||
static int randomxBlockLag = GetRandomXBlockLag();
|
||||
|
||||
// Determine the correct RandomX key for this height
|
||||
char initialKey[82];
|
||||
snprintf(initialKey, 81, "%08x%s%08x", ASSETCHAINS_MAGIC, SMART_CHAIN_SYMBOL, ASSETCHAINS_RPCPORT);
|
||||
|
||||
std::string rxKey;
|
||||
if (height < randomxInterval + randomxBlockLag) {
|
||||
// Use initial key derived from chain params
|
||||
rxKey = std::string(initialKey, strlen(initialKey));
|
||||
} else {
|
||||
// Use block hash at the key height
|
||||
int keyHeight = ((height - randomxBlockLag) / randomxInterval) * randomxInterval;
|
||||
CBlockIndex *pKeyIndex = hush_chainactive(keyHeight);
|
||||
if (pKeyIndex == nullptr) {
|
||||
return error("CheckRandomXSolution(): cannot get block index at key height %d for block %d", keyHeight, height);
|
||||
}
|
||||
uint256 blockKey = pKeyIndex->GetBlockHash();
|
||||
rxKey = std::string((const char*)&blockKey, sizeof(blockKey));
|
||||
}
|
||||
|
||||
// Serialize the block header without nSolution (but with nNonce) as RandomX input
|
||||
CRandomXInput rxInput(*pblock);
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << rxInput;
|
||||
|
||||
char computedHash[RANDOMX_HASH_SIZE];
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cs_randomx_validator);
|
||||
|
||||
// Initialize cache + VM if needed, or re-init if key changed
|
||||
if (s_rxCache == nullptr) {
|
||||
randomx_flags flags = randomx_get_flags();
|
||||
s_rxCache = randomx_alloc_cache(flags);
|
||||
if (s_rxCache == nullptr) {
|
||||
return error("CheckRandomXSolution(): failed to allocate RandomX cache");
|
||||
}
|
||||
randomx_init_cache(s_rxCache, rxKey.data(), rxKey.size());
|
||||
s_rxCurrentKey = rxKey;
|
||||
s_rxVM = randomx_create_vm(flags, s_rxCache, nullptr);
|
||||
if (s_rxVM == nullptr) {
|
||||
randomx_release_cache(s_rxCache);
|
||||
s_rxCache = nullptr;
|
||||
return error("CheckRandomXSolution(): failed to create RandomX VM");
|
||||
}
|
||||
} else if (s_rxCurrentKey != rxKey) {
|
||||
randomx_init_cache(s_rxCache, rxKey.data(), rxKey.size());
|
||||
s_rxCurrentKey = rxKey;
|
||||
randomx_vm_set_cache(s_rxVM, s_rxCache);
|
||||
}
|
||||
|
||||
randomx_calculate_hash(s_rxVM, &ss[0], ss.size(), computedHash);
|
||||
}
|
||||
|
||||
// Compare computed hash against nSolution
|
||||
if (memcmp(computedHash, pblock->nSolution.data(), RANDOMX_HASH_SIZE) != 0) {
|
||||
return error("CheckRandomXSolution(): RandomX hash mismatch at height %d", height);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t hush_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp);
|
||||
int32_t hush_currentheight();
|
||||
void hush_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height);
|
||||
|
||||
@@ -38,6 +38,15 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
|
||||
/** Check whether the Equihash solution in a block header is valid */
|
||||
bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&);
|
||||
|
||||
/** Check whether a block header contains a valid RandomX solution */
|
||||
bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height);
|
||||
|
||||
/** Return the RandomX key rotation interval in blocks */
|
||||
int GetRandomXInterval();
|
||||
|
||||
/** Return the RandomX key change lag in blocks */
|
||||
int GetRandomXBlockLag();
|
||||
|
||||
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
|
||||
bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t height, const Consensus::Params& params);
|
||||
CChainPower GetBlockProof(const CBlockIndex& block);
|
||||
|
||||
@@ -237,6 +237,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom serializer for CBlockHeader that includes nNonce but omits nSolution,
|
||||
* for use as deterministic input to RandomX hashing.
|
||||
*/
|
||||
class CRandomXInput : private CBlockHeader
|
||||
{
|
||||
public:
|
||||
CRandomXInput(const CBlockHeader &header)
|
||||
{
|
||||
CBlockHeader::SetNull();
|
||||
*((CBlockHeader*)this) = header;
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Describes a place in the block chain to another node such that if the
|
||||
* other node doesn't have the same branch, it can find a recent common trunk.
|
||||
|
||||
Reference in New Issue
Block a user