2 Commits

Author SHA1 Message Date
752590348b Fix sapling pool persistence and add subsidy/fees to getblock RPC
Lower SPROUT_VALUE_VERSION and SAPLING_VALUE_VERSION constants in
chain.h from upstream Zcash values (1001400/1010100) to 1000000.
When DragonX was rebranded from HUSH3, CLIENT_VERSION was reset from
3.10.5 to 1.0.0, falling below these thresholds. This caused
nSaplingValue to silently skip serialization, so the sapling pool
total reset to 0 on every node restart. Explorer nodes should reindex
once after upgrading.

Add subsidy and fees fields to the getblock RPC response so explorers
can display the correct 3 DRGX block reward separately from fees,
instead of showing the combined coinbase output as the reward.
2026-03-15 16:07:16 -05:00
f0cb958cac Fix fresh sync failure at diff reset height 2838976
Fresh-syncing nodes rejected the on-chain min-diff block at the
RANDOMX_VALIDATION activation height (2838976) because GetNextWorkRequired
computed the expected nBits from the preceding normal-difficulty blocks,
producing 469847994 instead of the on-chain 0x200f0f0f (HUSH_MINDIFF_NBITS).
This caused all seed nodes to be banned with "Incorrect diffbits" and the
node could never sync past that height.

Two changes:

1. GetNextWorkRequired (pow.cpp): Return nProofOfWorkLimit at the exact
   RANDOMX_VALIDATION activation height, matching the on-chain diff reset.

2. ContextualCheckBlockHeader (main.cpp): Raise DragonX daaForkHeight to
   RANDOMX_VALIDATION + 62000, covering the window where nBits was never
   validated (diff reset at 2838976 through the attack at ~2879907).

Tested by invalidating block 2838975 and reconsidering — node re-validated
through the diff reset and attack window, syncing back to tip with zero
bad-diffbits rejections.

Bump version to 1.0.1.
2026-03-12 01:25:21 -05:00
7 changed files with 36 additions and 6 deletions

View File

@@ -6,7 +6,7 @@
set -eu -o pipefail
VERSION="1.0.0"
VERSION="1.0.1"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RELEASE_DIR="$SCRIPT_DIR/release"

View File

@@ -3,7 +3,7 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 1)
dnl Must be kept in sync with src/clientversion.h , ugh!
define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_REVISION, 1)
define(_CLIENT_VERSION_BUILD, 50)
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1)))

View File

@@ -32,8 +32,12 @@ class CChainPower;
#include <boost/foreach.hpp>
extern bool fZindex;
static const int SPROUT_VALUE_VERSION = 1001400;
static const int SAPLING_VALUE_VERSION = 1010100;
// These version thresholds control whether nSproutValue/nSaplingValue are
// serialized in the block index. They must be <= CLIENT_VERSION or the
// values will never be persisted, causing nChainSaplingValue to reset
// to 0 after node restart. DragonX CLIENT_VERSION is 1000150 (v1.0.1.50).
static const int SPROUT_VALUE_VERSION = 1000000;
static const int SAPLING_VALUE_VERSION = 1000000;
extern int32_t ASSETCHAINS_LWMAPOS;
extern char SMART_CHAIN_SYMBOL[65];
extern uint64_t ASSETCHAINS_NOTARY_PAY[];

View File

@@ -30,7 +30,7 @@
// Must be kept in sync with configure.ac , ugh!
#define CLIENT_VERSION_MAJOR 1
#define CLIENT_VERSION_MINOR 0
#define CLIENT_VERSION_REVISION 0
#define CLIENT_VERSION_REVISION 1
#define CLIENT_VERSION_BUILD 50
//! Set to true for release, false for prerelease or test build

View File

@@ -5107,7 +5107,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
assert(pindexPrev);
int daaForkHeight = GetArg("-daaforkheight", 450000);
// For HUSH3, nBits validation starts above the original DAA fork height (450000).
// For DragonX, nBits was never validated before the standalone binary, so the
// chain contains blocks with incorrect nBits during the vulnerable window
// (diff reset at RANDOMX_VALIDATION height through the attack at ~2879907).
// Set daaForkHeight past that window so fresh sync accepts historical blocks.
bool isdragonx = strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) == 0;
int defaultDaaForkHeight = isdragonx ? ASSETCHAINS_RANDOMX_VALIDATION + 62000 : 450000;
int daaForkHeight = GetArg("-daaforkheight", defaultDaaForkHeight);
int nHeight = pindexPrev->GetHeight()+1;
bool ishush3 = strncmp(SMART_CHAIN_SYMBOL, "HUSH3",5) == 0 ? true : false;
// Check Proof-of-Work difficulty

View File

@@ -315,6 +315,16 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
if (pindexLast == NULL )
return nProofOfWorkLimit;
// DragonX difficulty reset at the RANDOMX_VALIDATION activation height.
// The chain transitioned to a new binary at this height and difficulty was
// reset to minimum (powLimit). Without this, fresh-syncing nodes compute
// a different nBits from GetNextWorkRequired (based on pre-reset blocks)
// and reject the on-chain min-diff block, banning all seed nodes.
if (ASSETCHAINS_RANDOMX_VALIDATION > 0 && pindexLast->GetHeight() + 1 == ASSETCHAINS_RANDOMX_VALIDATION) {
LogPrintf("%s: difficulty reset to powLimit at height %d\n", __func__, ASSETCHAINS_RANDOMX_VALIDATION);
return nProofOfWorkLimit;
}
//{
// Comparing to pindexLast->nHeight with >= because this function
// returns the work required for the block after pindexLast.

View File

@@ -322,6 +322,15 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("anchor", blockindex->hashFinalSproutRoot.GetHex()));
result.push_back(Pair("blocktype", "mined"));
// Report block subsidy and fees separately so explorers don't have to
// reimplement the reward schedule to display them.
CAmount nSubsidy = GetBlockSubsidy(blockindex->GetHeight(), Params().GetConsensus());
CAmount nCoinbase = block.vtx[0].GetValueOut();
CAmount nFees = nCoinbase - nSubsidy;
if (nFees < 0) nFees = 0; // block 1 has premine, avoid negative
result.push_back(Pair("subsidy", ValueFromAmount(nSubsidy)));
result.push_back(Pair("fees", ValueFromAmount(nFees)));
UniValue valuePools(UniValue::VARR);
valuePools.push_back(ValuePoolDesc("sapling", blockindex->nChainSaplingValue, blockindex->nSaplingValue));
result.push_back(Pair("valuePools", valuePools));