From: Sunny King Date: Thu, 29 Dec 2011 00:12:25 +0000 (+0000) Subject: PPCoin: Automatic checkpoint X-Git-Tag: v0.4.0-unstable~220 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=e0fa1f887a2a94fd8beb4f62fe997dc3532c266c PPCoin: Automatic checkpoint --- diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index d20fe24..8898ca1 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2011 The Bitcoin developers +// Copyright (c) 2011 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. @@ -10,7 +11,7 @@ namespace Checkpoints { - typedef std::map MapCheckpoints; + typedef std::map MapCheckpoints; // hardened checkpoints // // What makes a good checkpoint block? @@ -24,7 +25,10 @@ namespace Checkpoints ( 0, hashGenesisBlock ) ; // ppcoin: no checkpoint yet; to be created in future releases - bool CheckBlock(int nHeight, const uint256& hash) + // ppcoin: automatic checkpoint (represented by height of checkpoint) + int nAutoCheckpoint = 0; + + bool CheckHardened(int nHeight, const uint256& hash) { if (fTestNet) return true; // Testnet has no checkpoints @@ -33,6 +37,84 @@ namespace Checkpoints return hash == i->second; } + // ppcoin: check automatic checkpoint + // To pass the check: + // - All ancestors (including the block itself) have block index already + // - The immediate ancestor in main chain must not have height less than + // checkpoint height + bool CheckAuto(const CBlockIndex *pindex) + { + while (pindex) + { + if (pindex->IsInMainChain()) + { + if (pindex->nHeight >= nAutoCheckpoint) + return true; + else + return error("Checkpoints: new block on alternative branch at height=%d before auto checkpoint at height=%d", pindex->nHeight, nAutoCheckpoint); + } + else + pindex = pindex->pprev; + } + return error("Checkpoints: failed to find any ancestor on main chain for the new block - internal error"); + } + + // ppcoin: get next auto checkpoint in the chain + int GetNextChainCheckpoint(const CBlockIndex *pindexLast) + { + CBigNum bnTarget; + CBigNum bnTargetMax = 0; // max target of all blocks since checkpoint + CBigNum bnTargetMin = 0; // min target of all candidate checkpoints + int nMinTargetHeight = 0; // min target height since checkpoint + int nCheckpointMin = 0; // minimum candidate checkpoint + int nCheckpointMax = 0; // maximum candidate checkpoint + int nDepth = pindexLast->nHeight - pindexLast->nCheckpoint; + const CBlockIndex *pindex = pindexLast; + while (nDepth >= 0 && pindex) + { + bnTarget.SetCompact(pindex->nBits); + if (bnTarget > bnTargetMax) + bnTargetMax = bnTarget; + if (nCheckpointMax > 0 && bnTarget < bnTargetMin) + { + bnTargetMin = bnTarget; + nMinTargetHeight = pindex->nHeight; + } + if (nCheckpointMax == 0 && pindexLast->GetBlockTime() - pindex->GetBlockTime() > AUTO_CHECKPOINT_MIN_SPAN) + { + nCheckpointMax = pindex->nHeight; + bnTargetMin.SetCompact(pindex->nBits); + nMinTargetHeight = pindex->nHeight; + } + if (pindexLast->GetBlockTime() - pindex->GetBlockTime() < AUTO_CHECKPOINT_MAX_SPAN) + nCheckpointMin = pindex->nHeight; + pindex = pindex->pprev; + nDepth--; + } + + assert (nDepth == -1); // arrive at chain checkpoint now + + printf("Checkpoints: min=%d max=%d tminheight=%d tmin=0x%08x tmax=0x%08x\n", + nCheckpointMin, nCheckpointMax, nMinTargetHeight, + bnTargetMin.GetCompact(), bnTargetMax.GetCompact()); + if (nCheckpointMax == 0) // checkpoint stays if max candidate not found + return pindexLast->nCheckpoint; + + if (bnTargetMin * 100 > bnTargetMax * 90) + return nCheckpointMax; + if (bnTarget * 100 > bnTargetMax * 90) + return std::min(nCheckpointMax, nMinTargetHeight); + else + return nCheckpointMin; + } + + // ppcoin: advance to next automatic checkpoint + void AdvanceAutoCheckpoint(int nCheckpoint) + { + nAutoCheckpoint = std::max(nAutoCheckpoint, nCheckpoint); + printf("Checkpoints: auto checkpoint now at height=%d\n", nAutoCheckpoint); + } + int GetTotalBlocksEstimate() { if (fTestNet) return 0; diff --git a/src/checkpoints.h b/src/checkpoints.h index 9d52da4..78ccce9 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -1,4 +1,5 @@ // Copyright (c) 2011 The Bitcoin developers +// Copyright (c) 2011 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_CHECKPOINT_H @@ -7,6 +8,10 @@ #include #include "util.h" +// ppcoin: auto checkpoint min at 1 day; max at 1 week +#define AUTO_CHECKPOINT_MIN_SPAN (60 * 60 * 24) +#define AUTO_CHECKPOINT_MAX_SPAN (60 * 60 * 24 * 7) + class uint256; class CBlockIndex; @@ -16,8 +21,14 @@ class CBlockIndex; // namespace Checkpoints { + extern int nAutoCheckpoint; + // Returns true if block passes checkpoint checks - bool CheckBlock(int nHeight, const uint256& hash); + bool CheckHardened(int nHeight, const uint256& hash); + bool CheckAuto(const CBlockIndex *pindex); + + int GetNextChainCheckpoint(const CBlockIndex *pindex); + void AdvanceAutoCheckpoint(int nCheckpoint); // Return conservative estimate of total number of blocks, 0 if unknown int GetTotalBlocksEstimate(); diff --git a/src/db.cpp b/src/db.cpp index bb3c3b0..40cc35e 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -7,6 +7,7 @@ #include "headers.h" #include "db.h" #include "net.h" +#include "checkpoints.h" #include #include @@ -469,6 +470,16 @@ bool CTxDB::WriteBestInvalidTrust(uint64 nBestInvalidTrust) return Write(string("nBestInvalidTrust"), nBestInvalidTrust); } +bool CTxDB::ReadAutoCheckpoint(int& nAutoCheckpoint) +{ + return Read(string("nAutoCheckpoint"), nAutoCheckpoint); +} + +bool CTxDB::WriteAutoCheckpoint(int nCheckpoint) +{ + return Write(string("nAutoCheckpoint"), max(Checkpoints::nAutoCheckpoint, nCheckpoint)); +} + CBlockIndex static * InsertBlockIndex(uint256 hash) { if (hash == 0) @@ -560,7 +571,12 @@ bool CTxDB::LoadBlockIndex() pindexBest = mapBlockIndex[hashBestChain]; nBestHeight = pindexBest->nHeight; nBestChainTrust = pindexBest->nChainTrust; - printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight); + printf("LoadBlockIndex(): hashBestChain=%s height=%d trust=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, nBestChainTrust); + + // Load nAutoCheckpoint + if (!ReadAutoCheckpoint(Checkpoints::nAutoCheckpoint)) + return error("CTxDB::LoadBlockIndex() : nAutoCheckpoint not loaded"); + printf("LoadBlockIndex(): automatic checkpoint at height=%d\n", Checkpoints::nAutoCheckpoint); // Load nBestInvalidTrust, OK if it doesn't exist ReadBestInvalidTrust(nBestInvalidTrust); diff --git a/src/db.h b/src/db.h index 578a497..06e8dbd 100644 --- a/src/db.h +++ b/src/db.h @@ -291,6 +291,8 @@ public: bool WriteHashBestChain(uint256 hashBestChain); bool ReadBestInvalidTrust(uint64& nBestInvalidTrust); bool WriteBestInvalidTrust(uint64 nBestInvalidTrust); + bool ReadAutoCheckpoint(int& nAutoCheckpoint); + bool WriteAutoCheckpoint(int nCheckpoint); bool LoadBlockIndex(); }; diff --git a/src/main.cpp b/src/main.cpp index 9df7584..71ce131 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1102,6 +1102,8 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) } if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) return error("Reorganize() : WriteHashBestChain failed"); + if (!txdb.WriteAutoCheckpoint(pindexNew->nCheckpoint)) + return error("Reorganize() : WriteAutoCheckpoint failed"); // Make sure it's successfully written to disk before changing memory structure if (!txdb.TxnCommit()) @@ -1137,6 +1139,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) { txdb.WriteHashBestChain(hash); + txdb.WriteAutoCheckpoint(pindexNew->nCheckpoint); if (!txdb.TxnCommit()) return error("SetBestChain() : TxnCommit failed"); pindexGenesisBlock = pindexNew; @@ -1144,7 +1147,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) else if (hashPrevBlock == hashBestChain) { // Adding to current best branch - if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash) || !txdb.WriteAutoCheckpoint(pindexNew->nCheckpoint)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); @@ -1185,6 +1188,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) nBestChainTrust = pindexNew->nChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; + Checkpoints::AdvanceAutoCheckpoint(pindexBest->nCheckpoint); printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str()); return true; @@ -1247,13 +1251,20 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) if (!pindexNew) return error("AddToBlockIndex() : new CBlockIndex failed"); map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + + // ppcoin: compute chain checkpoint + pindexNew->nCheckpoint = Checkpoints::GetNextChainCheckpoint(pindexNew->pprev); + assert (pindexNew->nCheckpoint >= pindexNew->pprev->nCheckpoint); } + + // ppcoin: compute chain trust score uint64 nCoinAge = GetBlockCoinAge(); if (!nCoinAge) return error("AddToBlockIndex() : invalid or orphaned transaction in block"); @@ -1359,9 +1370,13 @@ bool CBlock::AcceptBlock() if (!tx.IsFinal(nHeight, GetBlockTime())) return DoS(10, error("AcceptBlock() : contains a non-final transaction")); - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight)); + // Check that the block chain matches the known block chain up to a hardened checkpoint + if (!Checkpoints::CheckHardened(nHeight, hash)) + return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight)); + + // ppcoin: check that the block satisfies automatic checkpoint + if (!Checkpoints::CheckAuto(pindexPrev)) + return DoS(100, error("AcceptBlock() : rejected by automatic checkpoint at %d", Checkpoints::nAutoCheckpoint)); // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) diff --git a/src/main.h b/src/main.h index 32fbc39..1bcad19 100644 --- a/src/main.h +++ b/src/main.h @@ -1029,6 +1029,7 @@ public: unsigned int nBlockPos; uint64 nChainTrust;// ppcoin: trust score of chain, in the unit of coin-days int nHeight; + int nCheckpoint; // ppcoin: chain auto checkpoint height // block header int nVersion; @@ -1047,6 +1048,7 @@ public: nBlockPos = 0; nHeight = 0; nChainTrust = 0; + nCheckpoint = 0; nVersion = 0; hashMerkleRoot = 0; @@ -1064,6 +1066,7 @@ public: nBlockPos = nBlockPosIn; nHeight = 0; nChainTrust = 0; + nCheckpoint = 0; nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -1157,8 +1160,8 @@ public: std::string ToString() const { - return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nChainTrust=%"PRI64d" nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, pnext, nFile, nBlockPos, nChainTrust, nHeight, + return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nChainTrust=%"PRI64d" nHeight=%d, nCheckpoint=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nFile, nBlockPos, nChainTrust, nHeight, nCheckpoint, hashMerkleRoot.ToString().substr(0,10).c_str(), GetBlockHash().ToString().substr(0,20).c_str()); } @@ -1202,6 +1205,7 @@ public: READWRITE(nBlockPos); READWRITE(nChainTrust); READWRITE(nHeight); + READWRITE(nCheckpoint); // block header READWRITE(this->nVersion);