// quantities so as to generate blocks faster, degrading the system back into
// a proof-of-work situation.
//
-bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake)
+bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool& fFatal, bool fPrintProofOfStake)
{
if (nTimeTx < txPrev.nTime) // Transaction timestamp violation
+ {
+ fFatal = true;
return error("CheckStakeKernelHash() : nTime violation");
+ }
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
+ {
+ fFatal = true;
return error("CheckStakeKernelHash() : min age violation");
+ }
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
// Now check if proof-of-stake hash meets target protocol
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
- return false;
+ {
+ fFatal = true;
+ return error("CheckStakeKernelHash() : proof-of-stake for block %s not meeting target", hashBlockFrom.ToString().c_str());
+ }
+
if (fDebug && !fPrintProofOfStake)
{
printf("CheckStakeKernelHash() : using modifier 0x%016"PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
}
// Check kernel hash target and coinstake signature
-bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake)
+bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake, bool& fFatal)
{
if (!tx.IsCoinStake())
+ {
+ fFatal = true;
return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString().c_str());
+ }
// Kernel (input 0) must match the stake hash target per coin age (nBits)
const CTxIn& txin = tx.vin[0];
CCoinsViewCache &view = *pcoinsTip;
if (!view.GetCoins(txin.prevout.hash, coins))
- return tx.DoS(1, error("CheckProofOfStake() : INFO: read coins for txPrev failed")); // previous transaction not in main chain, may occur during initial download
+ return fDebug? error("CheckProofOfStake() : INFO: read coins for txPrev failed") : false; // previous transaction not in main chain, may occur during initial download
CBlockIndex* pindex = FindBlockByHeight(coins.nHeight);
// Verify signature
if (!VerifySignature(coins, tx, 0, true, false, 0))
- return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str()));
+ {
+ fFatal = true;
+ return error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str());
+ }
- if (!CheckStakeKernelHash(nBits, block, nTxPos, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fDebug))
- return tx.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str())); // may occur during initial download or if behind on block chain sync
+ if (!CheckStakeKernelHash(nBits, block, nTxPos, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fFatal, fDebug))
+ {
+ if (fFatal)
+ return false;
+ // may occur during initial download or if behind on block chain sync
+ return fDebug? error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str()) : false;
+ }
return true;
}
bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64& nStakeModifier, bool& fGeneratedStakeModifier);
// Check whether stake kernel meets hash target
-// Sets hashProofOfStake on success return
-bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake=false);
+// Sets hashProofOfStake and targetProofOfStake on success return
+bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool& fFatal, bool fPrintProofOfStake=false);
// Check kernel hash target and coinstake signature
-// Sets hashProofOfStake on success return
-bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake);
+// Sets hashProofOfStake and targetProofOfStake on success return
+bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake, bool& fFatal);
// Check whether the coinstake timestamp meets protocol
bool CheckCoinStakeTimestamp(int64 nTimeBlock, int64 nTimeTx);
// Check coinstake timestamp
if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime))
return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime));
-
- // NovaCoin: check proof-of-stake block signature
- if (fCheckSig && !CheckBlockSignature(true))
- return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
}
else
{
{
bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME);
- // NovaCoin: check proof-of-work block signature
- if (checkEntropySig && !CheckBlockSignature(false))
+ // check legacy proof-of-work block signature
+ if (checkEntropySig && !CheckLegacySignature())
return DoS(100, error("CheckBlock() : bad proof-of-work block signature"));
}
}
// Check proof-of-work or proof-of-stake
if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
- return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake"));
+ return DoS(100, error("AcceptBlock() : incorrect proof-of-%s amount", IsProofOfWork() ? "work" : "stake"));
// Check timestamp against prev
if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
- // ppcoin: check proof-of-stake
- // Limited duplicity on stake: prevents block flood attack
- // Duplicate stake allowed only when there is orphan child block
- if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
- return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
-
// Preliminary checks
if (!pblock->CheckBlock())
return error("ProcessBlock() : CheckBlock FAILED");
- // ppcoin: verify hash target and signature of coinstake tx
if (pblock->IsProofOfStake())
{
- uint256 hashProofOfStake = 0, targetProofOfStake = 0;
- if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake, targetProofOfStake))
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+
+ bool fFatal = false;
+ uint256 hashProofOfStake;
+
+ // Verify proof-of-stake hash target and signatures
+ if (!pblock->CheckSignature(fFatal, hashProofOfStake))
{
- printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str());
- return false; // do not error here as we expect this during initial block download
+ if (fFatal)
+ {
+ // Invalid blockhash/coinstake signature or no generator defined, nothing to do here
+ // This also may occur when supplied proof-of-stake doesn't satisfy required target
+ if (pfrom)
+ pfrom->Misbehaving(100);
+ return error("ProcessBlock() : invalid signature in proof-of-stake block %s", hash.ToString().c_str());
+ }
+ else
+ {
+ // Blockhash and coinstake signatures are OK but target checkings failed
+ // This may occur during initial block download
+
+ if (pfrom)
+ pfrom->Misbehaving(1); // Small DoS penalty
+
+ printf("WARNING: ProcessBlock(): proof-of-stake target checkings failed for block %s, we'll try again later\n", hash.ToString().c_str());
+ return false;
+ }
}
+
if (!mapProofOfStake.count(hash)) // add to mapProofOfStake
mapProofOfStake.insert(make_pair(hash, hashProofOfStake));
}
{
if (pfrom)
pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
+ return error("ProcessBlock() : block with too little proof-of-%s", pblock->IsProofOfStake() ? "stake" : "work");
}
}
- // ppcoin: ask for pending sync-checkpoint if any
+ // Ask for pending sync-checkpoint if any
if (!IsInitialBlockDownload())
Checkpoints::AskForPendingSyncCheckpoint(pfrom);
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
CBlock* pblock2 = new CBlock(*pblock);
- // ppcoin: check proof-of-stake
+
if (pblock2->IsProofOfStake())
{
// Limited duplicity on stake: prevents block flood attack
else
setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
}
+
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
return true;
}
-// novacoin: attempt to generate suitable proof-of-stake
+// attempt to generate suitable proof-of-stake
bool CBlock::SignBlock(CWallet& wallet)
{
// if we are trying to sign
return false;
}
-// ppcoin: check block signature
-bool CBlock::CheckBlockSignature(bool fProofOfStake) const
+// get generation key
+bool CBlock::GetGenerator(CKey& GeneratorKey) const
{
- if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
- return vchBlockSig.empty();
+ if(!IsProofOfStake())
+ return false;
vector<valtype> vSolutions;
txnouttype whichType;
- if(fProofOfStake)
+ const CTxOut& txout = vtx[1].vout[1];
+
+ if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+ return false;
+ if (whichType == TX_PUBKEY)
{
- const CTxOut& txout = vtx[1].vout[1];
+ valtype& vchPubKey = vSolutions[0];
+ CKey key;
+ return GeneratorKey.SetPubKey(vchPubKey);
+ }
+
+ return false;
+}
+
+// verify proof-of-stake signatures
+bool CBlock::CheckSignature(bool& fFatal, uint256& hashProofOfStake) const
+{
+ CKey key;
+
+ // no generator or invalid hash signature means fatal error
+ fFatal = !GetGenerator(key) || !key.Verify(GetHash(), vchBlockSig);
+
+ if (fFatal)
+ return false;
+
+ uint256 hashTarget = 0;
+ if (!CheckProofOfStake(vtx[1], nBits, hashProofOfStake, hashTarget, fFatal))
+ return false; // hash target mismatch or invalid coinstake signature
+
+ return true;
+}
+
+// verify legacy proof-of-work signature
+bool CBlock::CheckLegacySignature() const
+{
+ if (IsProofOfStake())
+ return false;
+
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+
+ for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
+ {
+ const CTxOut& txout = vtx[0].vout[i];
if (!Solver(txout.scriptPubKey, whichType, vSolutions))
return false;
+
if (whichType == TX_PUBKEY)
{
+ // Verify
valtype& vchPubKey = vSolutions[0];
CKey key;
if (!key.SetPubKey(vchPubKey))
- return false;
+ continue;
if (vchBlockSig.empty())
- return false;
- return key.Verify(GetHash(), vchBlockSig);
+ continue;
+ if(!key.Verify(GetHash(), vchBlockSig))
+ continue;
+ return true;
}
}
- else
- {
- for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
- {
- const CTxOut& txout = vtx[0].vout[i];
-
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
-
- if (whichType == TX_PUBKEY)
- {
- // Verify
- valtype& vchPubKey = vSolutions[0];
- CKey key;
- if (!key.SetPubKey(vchPubKey))
- continue;
- if (vchBlockSig.empty())
- continue;
- if(!key.Verify(GetHash(), vchBlockSig))
- continue;
- return true;
- }
- }
- }
return false;
}