return nSubsidy;
}
-static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
-static const int64 nTargetSpacing = 10 * 60;
-static const int64 nInterval = nTargetTimespan / nTargetSpacing;
-static const int64 nMaxClockDrift = 2 * 60 * 60; // 2 hours
+static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
+static const int64 nTargetSpacingStake = 10 * 60; // ten minutes
+static const int64 nTargetSpacingWorkMax = 2 * 60 * 60; // two hours
+static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
//
// minimum amount of work that could possibly be required nTime after
// ppcoin: retarget with exponential moving toward target spacing
CBigNum bnNew;
bnNew.SetCompact(pindexPrev->nBits);
+ int64 nTargetSpacing = fProofOfStake? nTargetSpacingStake : min(nTargetSpacingWorkMax, nTargetSpacingStake * (1 + pindexLast->nHeight - pindexPrev->nHeight));
+ int64 nInterval = nTargetTimespan / nTargetSpacing;
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
bnNew /= ((nInterval + 1) * nTargetSpacing);
if (!CheckBlock())
return false;
- // ppcoin: coin stake tx must meet target protocol
- if (IsProofOfStake() && !vtx[1].CheckProofOfStake(txdb, nBits))
- return error("ConnectBlock() : Block %s unable to meet hash target for coinstake", GetHash().ToString().c_str());
-
//// issue here: it doesn't know the version
unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
// quantities so as to generate blocks faster, degrading the system back into
// a proof-of-work situation.
//
-bool CTransaction::CheckProofOfStake(CTxDB& txdb, unsigned int nBits) const
+bool CTransaction::CheckProofOfStake(unsigned int nBits) const
{
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
const CTxIn& txin = vin[0];
// First try finding the previous transaction in database
+ CTxDB txdb("r");
CTransaction txPrev;
CTxIndex txindex;
if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
return false; // previous transaction not in main chain
+ txdb.Close();
if (nTime < txPrev.nTime)
return false; // Transaction timestamp violation
+ // Verify signature
+ if (!VerifySignature(txPrev, *this, 0))
+ return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str()));
+
// Read block header
CBlock block;
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
return true;
else
- return false;
+ return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str()));
}
// ppcoin: total coin age spent in transaction, in the unit of coin-days.
CBlockIndex* pindexPrev = (*mi).second;
int nHeight = pindexPrev->nHeight+1;
- // ppcoin: check for coinstake duplicate
- if (IsProofOfStake())
- { // check if coinstake is already connected; that would imply the owner
- // of the coinstake sent multiple blocks with the same coinstake
- CTxIndex txindex;
- if (CTxDB("r").ReadTxIndex(vtx[1].GetHash(), txindex))
- return error("AcceptBlock() : block %s has duplicate coinstake %s", hash.ToString().c_str(), vtx[1].GetHash().ToString().c_str());
- }
-
// Check proof-of-work or proof-of-stake
if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake"));
+ // ppcoin: coinstake tx must meet target protocol
+ if (IsProofOfStake() && !vtx[1].CheckProofOfStake(nBits))
+ return error("AcceptBlock() : Block %s unable to meet hash target for coinstake", GetHash().ToString().c_str());
+
// Check timestamp against prev
if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime())
return error("AcceptBlock() : block's timestamp is too early");
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
// ppcoin: check proof-of-stake
- if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()))
+ // 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))
return error("ProcessBlock() : duplicate proof-of-stake (%s) for block %s", pblock->GetProofOfStake().ToString().c_str(), hash.ToString().c_str());
// Preliminary checks
// ppcoin: check proof-of-stake
if (pblock2->IsProofOfStake())
{
- if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()))
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash))
return error("ProcessBlock() : duplicate proof-of-stake (%s) for orphan block %s", pblock2->GetProofOfStake().ToString().c_str(), hash.ToString().c_str());
else
setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
+
+ // ppcoin: initialize synchronized checkpoint
+ CTxDB txdbc;
+ if (!txdbc.WriteSyncCheckpoint(hashGenesisBlock))
+ return error("LoadBlockIndex() : failed to init sync checkpoint");
+ txdbc.Close();
+ Checkpoints::hashSyncCheckpoint = hashGenesisBlock;
}
return true;