Skip to content
Permalink
Browse files Browse the repository at this point in the history
Block Verification Vulnerability Eliminated
Fixed a critical vulnerability allowing to accept a proof-of-work block
containing one or more coin stake transactions in addition to a valid
coin base. Output values of such coin stakes were out of control.
  • Loading branch information
ghostlander committed Sep 15, 2021
1 parent 37c2015 commit 0675b25
Showing 1 changed file with 27 additions and 19 deletions.
46 changes: 27 additions & 19 deletions src/main.cpp
Expand Up @@ -2099,6 +2099,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u


bool CBlock::CheckBlock() const {
uint i;

// These are checks that are independent of context
// that can be verified before saving an orphan block.
Expand All @@ -2107,44 +2108,35 @@ bool CBlock::CheckBlock() const {
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CheckBlock() : size limits failed"));

if(IsProofOfWork()) {

/* Proof-of-work verification against target */
if(!CheckProofOfWork(GetHashPoW(), nBits))
return(DoS(50, error("CheckBlock() : proof-of-work verification failed")));

/* Proof-of-work block signature verification */
if(!CheckBlockSignature())
return(DoS(100, error("CheckBlock() : bad proof-of-work block signature")));
}

// Check timestamp
if (GetBlockTime() > FutureDrift(GetAdjustedTime()))
return error("CheckBlock() : block timestamp too far in the future");

// First transaction must be coinbase, the rest must not be
if (vtx.empty() || !vtx[0].IsCoinBase())
return DoS(100, error("CheckBlock() : first tx is not coinbase"));
for (unsigned int i = 1; i < vtx.size(); i++)
if (vtx[i].IsCoinBase())
return DoS(100, error("CheckBlock() : more than one coinbase"));
for(i = 1; i < vtx.size(); i++) {
if(vtx[i].IsCoinBase())
return(DoS(100, error("CheckBlock() : more than one coin base")));
}

// Check coinbase timestamp
if (GetBlockTime() > FutureDrift((int64_t)vtx[0].nTime))
return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));

if (IsProofOfStake())
{
if(IsProofOfStake()) {

// Coinbase output should be empty if proof-of-stake block
if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())
return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block"));

// Second transaction must be coinstake, the rest must not be
if (vtx.empty() || !vtx[1].IsCoinStake())
return DoS(100, error("CheckBlock() : second tx is not coinstake"));
for (unsigned int i = 2; i < vtx.size(); i++)
if (vtx[i].IsCoinStake())
return DoS(100, error("CheckBlock() : more than one coinstake"));
for(i = 2; i < vtx.size(); i++) {
if(vtx[i].IsCoinStake())
return(DoS(100, error("CheckBlock() : more than one coin stake")));
}

// Check coinstake timestamp
if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64_t)vtx[1].nTime))
Expand All @@ -2153,6 +2145,22 @@ bool CBlock::CheckBlock() const {
// NovaCoin: check proof-of-stake block signature
if(!CheckBlockSignature())
return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));

} else {

/* No coin stakes in PoW blocks */
for(i = 1; i < vtx.size(); i++) {
if(vtx[i].IsCoinStake() && (vtx[i].nTime > 1626307200))
return(DoS(100, error("CheckBlock() : rogue coin stake")));
}

/* Proof-of-work verification against target */
if(!CheckProofOfWork(GetHashPoW(), nBits))
return(DoS(50, error("CheckBlock() : proof-of-work verification failed")));

/* Proof-of-work block signature verification */
if(!CheckBlockSignature())
return(DoS(100, error("CheckBlock() : bad proof-of-work block signature")));
}

// Check transactions
Expand Down

0 comments on commit 0675b25

Please sign in to comment.