Compare commits
5 Commits
d52550a6fc
...
7e99311210
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e99311210 | |||
| 14e3fb6708 | |||
| b9fdc79818 | |||
| 4351d5b733 | |||
| 4a0a334649 |
@@ -131,7 +131,9 @@ public:
|
|||||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
|
||||||
// The best chain should have at least this much work.
|
// The best chain should have at least this much work.
|
||||||
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000281b32ff3198a1");
|
// DRAGONX mainnet chainwork @ height ~3,100,000 (2026-07), safely below the live tip.
|
||||||
|
// (Previous value 0x281b32ff3198a1 was a stale inherited figure ABOVE the live chain.) Bump on release.
|
||||||
|
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000026dfbdb6fa39e0");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The message start string is designed to be unlikely to occur in normal data.
|
* The message start string is designed to be unlikely to occur in normal data.
|
||||||
|
|||||||
@@ -510,7 +510,9 @@ int32_t hush_validate_chain(uint256 srchash,int32_t notarized_height)
|
|||||||
return(0);
|
return(0);
|
||||||
if ( IsInitialBlockDownload() == 0 && ((pindex= hush_getblockindex(srchash)) == 0 || pindex->GetHeight() != notarized_height) )
|
if ( IsInitialBlockDownload() == 0 && ((pindex= hush_getblockindex(srchash)) == 0 || pindex->GetHeight() != notarized_height) )
|
||||||
{
|
{
|
||||||
fprintf(stderr,"%s: Not in IBD, height=%d\n", __func__, pindex->GetHeight() );
|
// SECURITY (null-deref crash DoS): this branch is entered when pindex==0 (srchash, taken
|
||||||
|
// from an attacker-controlled notarization OP_RETURN, is not a known block). Guard the deref.
|
||||||
|
fprintf(stderr,"%s: Not in IBD, height=%d\n", __func__, pindex != 0 ? pindex->GetHeight() : -1 );
|
||||||
if ( sp->NOTARIZED_HEIGHT > 0 && sp->NOTARIZED_HEIGHT < notarized_height )
|
if ( sp->NOTARIZED_HEIGHT > 0 && sp->NOTARIZED_HEIGHT < notarized_height )
|
||||||
rewindtarget = sp->NOTARIZED_HEIGHT - 1;
|
rewindtarget = sp->NOTARIZED_HEIGHT - 1;
|
||||||
else if ( notarized_height > 101 )
|
else if ( notarized_height > 101 )
|
||||||
|
|||||||
@@ -417,7 +417,9 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
|||||||
{
|
{
|
||||||
request.read(json,n);
|
request.read(json,n);
|
||||||
jreq.parse(request);
|
jreq.parse(request);
|
||||||
strcpy(ptr->method,jreq.strMethod.c_str());
|
// SECURITY (stack overflow): strMethod is attacker-controlled; bound the copy to the fixed buffer.
|
||||||
|
strncpy(ptr->method,jreq.strMethod.c_str(),sizeof(ptr->method)-1);
|
||||||
|
ptr->method[sizeof(ptr->method)-1] = '\0';
|
||||||
len+=sizeof(ptr->method);
|
len+=sizeof(ptr->method);
|
||||||
std::map<std::string, bool>::iterator it = nspv_remote_commands.find(jreq.strMethod);
|
std::map<std::string, bool>::iterator it = nspv_remote_commands.find(jreq.strMethod);
|
||||||
if (it==nspv_remote_commands.end())
|
if (it==nspv_remote_commands.end())
|
||||||
@@ -438,8 +440,10 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
|||||||
{
|
{
|
||||||
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
|
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
|
||||||
response=rpc_result.write();
|
response=rpc_result.write();
|
||||||
memcpy(ptr->json,response.c_str(),response.size());
|
// SECURITY (stack overflow): clamp to the fixed json buffer.
|
||||||
len+=response.size();
|
size_t rlen = response.size(); if ( rlen > sizeof(ptr->json) ) rlen = sizeof(ptr->json);
|
||||||
|
memcpy(ptr->json,response.c_str(),rlen);
|
||||||
|
len+=rlen;
|
||||||
return (len);
|
return (len);
|
||||||
}
|
}
|
||||||
else throw JSONRPCError(RPC_MISC_ERROR, "Error in executing RPC on remote node");
|
else throw JSONRPCError(RPC_MISC_ERROR, "Error in executing RPC on remote node");
|
||||||
@@ -459,8 +463,10 @@ int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
|||||||
rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
||||||
response=rpc_result.write();
|
response=rpc_result.write();
|
||||||
}
|
}
|
||||||
memcpy(ptr->json,response.c_str(),response.size());
|
// SECURITY (stack overflow): the error path echoes attacker-controlled jreq.id; clamp to the buffer.
|
||||||
len+=response.size();
|
size_t rlen = response.size(); if ( rlen > sizeof(ptr->json) ) rlen = sizeof(ptr->json);
|
||||||
|
memcpy(ptr->json,response.c_str(),rlen);
|
||||||
|
len+=rlen;
|
||||||
return (len);
|
return (len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,10 +657,10 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
|||||||
if ( timestamp > pfrom->prevtimes[ind] )
|
if ( timestamp > pfrom->prevtimes[ind] )
|
||||||
{
|
{
|
||||||
struct NSPV_utxosresp U;
|
struct NSPV_utxosresp U;
|
||||||
if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
if ( len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||||
{
|
{
|
||||||
int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0;
|
int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0;
|
||||||
memcpy(coinaddr,&request[2],request[1]);
|
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
|
||||||
coinaddr[request[1]] = 0;
|
coinaddr[request[1]] = 0;
|
||||||
if ( request[1] == len-3 )
|
if ( request[1] == len-3 )
|
||||||
isCC = (request[len-1] != 0);
|
isCC = (request[len-1] != 0);
|
||||||
@@ -691,10 +697,10 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
|||||||
if ( timestamp > pfrom->prevtimes[ind] )
|
if ( timestamp > pfrom->prevtimes[ind] )
|
||||||
{
|
{
|
||||||
struct NSPV_txidsresp T;
|
struct NSPV_txidsresp T;
|
||||||
if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
if ( len < 64+5 && request[1] < 64 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
||||||
{
|
{
|
||||||
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0;
|
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0;
|
||||||
memcpy(coinaddr,&request[2],request[1]);
|
memcpy(coinaddr,&request[2],request[1]); // request[1] < 64 bounds the copy + the terminator write
|
||||||
coinaddr[request[1]] = 0;
|
coinaddr[request[1]] = 0;
|
||||||
if ( request[1] == len-3 )
|
if ( request[1] == len-3 )
|
||||||
isCC = (request[len-1] != 0);
|
isCC = (request[len-1] != 0);
|
||||||
@@ -732,7 +738,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
|||||||
if ( timestamp > pfrom->prevtimes[ind] )
|
if ( timestamp > pfrom->prevtimes[ind] )
|
||||||
{
|
{
|
||||||
struct NSPV_mempoolresp M; char coinaddr[64];
|
struct NSPV_mempoolresp M; char coinaddr[64];
|
||||||
if ( len < sizeof(M)+64 )
|
if ( len >= 40 && len < sizeof(M)+64 ) // SECURITY: lower bound guards the fixed-offset reads request[1..39]
|
||||||
{
|
{
|
||||||
int32_t vout; uint256 txid; uint8_t funcid,isCC = 0;
|
int32_t vout; uint256 txid; uint8_t funcid,isCC = 0;
|
||||||
n = 1;
|
n = 1;
|
||||||
@@ -741,7 +747,7 @@ void hush_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a reque
|
|||||||
n += dragon_rwnum(0,&request[n],sizeof(vout),&vout);
|
n += dragon_rwnum(0,&request[n],sizeof(vout),&vout);
|
||||||
n += dragon_rwbignum(0,&request[n],sizeof(txid),(uint8_t *)&txid);
|
n += dragon_rwbignum(0,&request[n],sizeof(txid),(uint8_t *)&txid);
|
||||||
slen = request[n++];
|
slen = request[n++];
|
||||||
if ( slen < 63 )
|
if ( slen < 63 && n + slen <= len ) // SECURITY: bound the memcpy source read within request
|
||||||
{
|
{
|
||||||
memcpy(coinaddr,&request[n],slen), n += slen;
|
memcpy(coinaddr,&request[n],slen), n += slen;
|
||||||
coinaddr[slen] = 0;
|
coinaddr[slen] = 0;
|
||||||
|
|||||||
16
src/main.cpp
16
src/main.cpp
@@ -2456,6 +2456,17 @@ bool IsInitialBlockDownload()
|
|||||||
//fprintf(stderr,"nullptr in IsInitialDownload\n");
|
//fprintf(stderr,"nullptr in IsInitialDownload\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SECURITY: enforce the known-good minimum chain work (defined in chainparams but previously
|
||||||
|
// never checked). Keeps an eclipsed/bootstrapping node from trusting a cheap low-work fake
|
||||||
|
// chain -- a recent tip timestamp alone (below) is not sufficient. Gated to the DRAGONX symbol
|
||||||
|
// so ephemeral assetchains (fresh, low work) run from the same binary are not trapped in IBD.
|
||||||
|
if (strncmp(SMART_CHAIN_SYMBOL, "DRAGONX", 7) == 0 &&
|
||||||
|
ptr->chainPower.chainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
state = ((chainActive.Height() < ptr->GetHeight() - 24*60) ||
|
state = ((chainActive.Height() < ptr->GetHeight() - 24*60) ||
|
||||||
ptr->GetBlockTime() < (GetTime() - nMaxTipAge));
|
ptr->GetBlockTime() < (GetTime() - nMaxTipAge));
|
||||||
if ( HUSH_INSYNC != 0 )
|
if ( HUSH_INSYNC != 0 )
|
||||||
@@ -5443,7 +5454,10 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->GetHeight():0,*ppindex, block, state,0)) {
|
// SECURITY (header-flood DoS): once synced, verify PoW at header-accept time so a peer cannot
|
||||||
|
// flood unbounded PoW-less headers into mapBlockIndex (they now fail RandomX -> DoS-ban). During
|
||||||
|
// IBD keep fCheckPOW=0 for fast header sync; the full RandomX/target check runs at block connect.
|
||||||
|
if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->GetHeight():0,*ppindex, block, state, IsInitialBlockDownload() ? 0 : 1)) {
|
||||||
if ( *futureblockp == 0 ) {
|
if ( *futureblockp == 0 ) {
|
||||||
LogPrintf("%s: CheckBlockHeader futureblock=0\n", __func__);
|
LogPrintf("%s: CheckBlockHeader futureblock=0\n", __func__);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1654,7 +1654,7 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk
|
|||||||
ret.pushKV("deshielding_payments", (int64_t)pindex->nChainDeshieldingPayments);
|
ret.pushKV("deshielding_payments", (int64_t)pindex->nChainDeshieldingPayments);
|
||||||
ret.pushKV("shielding_payments", (int64_t)pindex->nChainShieldingPayments);
|
ret.pushKV("shielding_payments", (int64_t)pindex->nChainShieldingPayments);
|
||||||
|
|
||||||
int64_t nullifierCount = pwalletMain->NullifierCount();
|
int64_t nullifierCount = pwalletMain ? pwalletMain->NullifierCount() : 0; // null under -disablewallet
|
||||||
//TODO: this is unreliable, is only a cache or subset of total nullifiers
|
//TODO: this is unreliable, is only a cache or subset of total nullifiers
|
||||||
ret.pushKV("nullifiers", (int64_t)nullifierCount);
|
ret.pushKV("nullifiers", (int64_t)nullifierCount);
|
||||||
ret.pushKV("shielded_pool_size", (int64_t)(pindex->nChainShieldedOutputs - pindex->nChainShieldedSpends));
|
ret.pushKV("shielded_pool_size", (int64_t)(pindex->nChainShieldedOutputs - pindex->nChainShieldedSpends));
|
||||||
|
|||||||
@@ -5146,7 +5146,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//GOAL: choose one random zaddress with enough funds
|
//GOAL: choose one random zaddress with enough funds
|
||||||
CAmount nFee;
|
CAmount nFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE; // default when params.size()<=3 (was uninitialized)
|
||||||
if (params.size() > 3) {
|
if (params.size() > 3) {
|
||||||
if (params[3].get_real() == 0.0) {
|
if (params[3].get_real() == 0.0) {
|
||||||
nFee = 0;
|
nFee = 0;
|
||||||
@@ -5298,6 +5298,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
|||||||
|
|
||||||
CAmount total_value = 0;
|
CAmount total_value = 0;
|
||||||
|
|
||||||
|
// correctness: reserve the miner fee during note selection so we don't stop at exactly nTotalOut
|
||||||
|
// and then fail later with a spurious "insufficient funds". Mirrors the nFee computed below.
|
||||||
|
CAmount nFeeReserve = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE;
|
||||||
|
if (params.size() > 3)
|
||||||
|
nFeeReserve = (params[3].get_real() == 0.0) ? 0 : AmountFromValue(params[3]);
|
||||||
|
|
||||||
std::vector<SendManyInputSaplingNote> saplingNoteInputs;
|
std::vector<SendManyInputSaplingNote> saplingNoteInputs;
|
||||||
// Decide which sapling notes will be spent
|
// Decide which sapling notes will be spent
|
||||||
for (const SaplingNoteEntry& entry : saplingEntries) {
|
for (const SaplingNoteEntry& entry : saplingEntries) {
|
||||||
@@ -5309,8 +5315,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
|||||||
saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk);
|
saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk);
|
||||||
total_value += nValue;
|
total_value += nValue;
|
||||||
LogPrintf("%s: adding note to spend with value=%s, total_value=%s\n", __func__, FormatMoney(nValue), FormatMoney(total_value) );
|
LogPrintf("%s: adding note to spend with value=%s, total_value=%s\n", __func__, FormatMoney(nValue), FormatMoney(total_value) );
|
||||||
if (total_value >= nTotalOut) {
|
if (total_value >= nTotalOut + nFeeReserve) {
|
||||||
// we have enough note value to make the tx
|
// we have enough note value (incl. miner fee) to make the tx
|
||||||
LogPrintf("%s: found enough notes, nTotalOut=%s total_value=%s\n", __func__, FormatMoney(nTotalOut), FormatMoney(total_value) );
|
LogPrintf("%s: found enough notes, nTotalOut=%s total_value=%s\n", __func__, FormatMoney(nTotalOut), FormatMoney(total_value) );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user