From b082ca538c0d334dc2680218aa95cb762ddf9c0b Mon Sep 17 00:00:00 2001 From: Bzlcoin <32078270+bzlcoin@users.noreply.github.com> Date: Wed, 1 Nov 2017 09:34:39 -0200 Subject: [PATCH] Add files via upload Fix the Checkpoints --- src/checkpoints.cpp | 429 ++++++++++++++++++++++++++++++++++++++++++++ src/kernel.cpp | 390 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 819 insertions(+) create mode 100644 src/checkpoints.cpp create mode 100644 src/kernel.cpp diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp new file mode 100644 index 0000000..fcb64e5 --- /dev/null +++ b/src/checkpoints.cpp @@ -0,0 +1,429 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include // for 'map_list_of()' +#include + +#include "checkpoints.h" + +#include "txdb.h" +#include "main.h" +#include "uint256.h" + + +static const int nCheckpointSpan = 10; + +namespace Checkpoints +{ + typedef std::map MapCheckpoints; + + // + // What makes a good checkpoint block? + // + Is surrounded by blocks with reasonable timestamps + // (no blocks before with a timestamp after, none after with + // timestamp before) + // + Contains no strange transactions + // + static MapCheckpoints mapCheckpoints = + boost::assign::map_list_of + ( 0, hashGenesisBlock ) + ( 100, uint256("0x000000000002205082838a1bb176b71d21a9d657114271b876ec489a62f89900") ) + ( 180, uint256("0x0000000000576a49b70020e517e835c3a765bbbad51bc87a8ac46d253fb6546e") ) + ; + + // TestNet has no checkpoints + static MapCheckpoints mapCheckpointsTestnet = + boost::assign::map_list_of + ( 0, hashGenesisBlockTestNet ) + ; + + bool CheckHardened(int nHeight, const uint256& hash) + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + MapCheckpoints::const_iterator i = checkpoints.find(nHeight); + if (i == checkpoints.end()) return true; + return hash == i->second; + } + + int GetTotalBlocksEstimate() + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + return checkpoints.rbegin()->first; + } + + CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex) + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) + { + const uint256& hash = i.second; + std::map::const_iterator t = mapBlockIndex.find(hash); + if (t != mapBlockIndex.end()) + return t->second; + } + return NULL; + } + + // ppcoin: synchronized checkpoint (centrally broadcasted) + uint256 hashSyncCheckpoint = 0; + uint256 hashPendingCheckpoint = 0; + CSyncCheckpoint checkpointMessage; + CSyncCheckpoint checkpointMessagePending; + uint256 hashInvalidCheckpoint = 0; + CCriticalSection cs_hashSyncCheckpoint; + + // ppcoin: get last synchronized checkpoint + CBlockIndex* GetLastSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + if (!mapBlockIndex.count(hashSyncCheckpoint)) + error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); + else + return mapBlockIndex[hashSyncCheckpoint]; + return NULL; + } + + // ppcoin: only descendant of current sync-checkpoint is allowed + bool ValidateSyncCheckpoint(uint256 hashCheckpoint) + { + if (!mapBlockIndex.count(hashSyncCheckpoint)) + return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); + if (!mapBlockIndex.count(hashCheckpoint)) + return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str()); + + CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint]; + CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint]; + + if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight) + { + // Received an older checkpoint, trace back from current checkpoint + // to the same height of the received checkpoint to verify + // that current checkpoint should be a descendant block + CBlockIndex* pindex = pindexSyncCheckpoint; + while (pindex->nHeight > pindexCheckpointRecv->nHeight) + if (!(pindex = pindex->pprev)) + return error("ValidateSyncCheckpoint: pprev null - block index structure failure"); + if (pindex->GetBlockHash() != hashCheckpoint) + { + hashInvalidCheckpoint = hashCheckpoint; + return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); + } + return false; // ignore older checkpoint + } + + // Received checkpoint should be a descendant block of the current + // checkpoint. Trace back to the same height of current checkpoint + // to verify. + CBlockIndex* pindex = pindexCheckpointRecv; + while (pindex->nHeight > pindexSyncCheckpoint->nHeight) + if (!(pindex = pindex->pprev)) + return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure"); + if (pindex->GetBlockHash() != hashSyncCheckpoint) + { + hashInvalidCheckpoint = hashCheckpoint; + return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); + } + return true; + } + + bool WriteSyncCheckpoint(const uint256& hashCheckpoint) + { + CTxDB txdb; + txdb.TxnBegin(); + if (!txdb.WriteSyncCheckpoint(hashCheckpoint)) + { + txdb.TxnAbort(); + return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + if (!txdb.TxnCommit()) + return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str()); + + Checkpoints::hashSyncCheckpoint = hashCheckpoint; + return true; + } + + bool AcceptPendingSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint)) + { + if (!ValidateSyncCheckpoint(hashPendingCheckpoint)) + { + hashPendingCheckpoint = 0; + checkpointMessagePending.SetNull(); + return false; + } + + CTxDB txdb; + CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint]; + if (!pindexCheckpoint->IsInMainChain()) + { + CBlock block; + if (!block.ReadFromDisk(pindexCheckpoint)) + return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + if (!block.SetBestChain(txdb, pindexCheckpoint)) + { + hashInvalidCheckpoint = hashPendingCheckpoint; + return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + } + } + + if (!WriteSyncCheckpoint(hashPendingCheckpoint)) + return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + hashPendingCheckpoint = 0; + checkpointMessage = checkpointMessagePending; + checkpointMessagePending.SetNull(); + printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str()); + // relay the checkpoint + if (!checkpointMessage.IsNull()) + { + BOOST_FOREACH(CNode* pnode, vNodes) + checkpointMessage.RelayTo(pnode); + } + return true; + } + return false; + } + + // Automatically select a suitable sync-checkpoint + uint256 AutoSelectSyncCheckpoint() + { + const CBlockIndex *pindex = pindexBest; + // Search backward for a block within max span and maturity window + while (pindex->pprev && (pindex->GetBlockTime() + nCheckpointSpan * nTargetSpacing > pindexBest->GetBlockTime() || pindex->nHeight + nCheckpointSpan > pindexBest->nHeight)) + pindex = pindex->pprev; + return pindex->GetBlockHash(); + } + + // Check against synchronized checkpoint + bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev) + { + if (fTestNet) return true; // Testnet has no checkpoints + int nHeight = pindexPrev->nHeight + 1; + + LOCK(cs_hashSyncCheckpoint); + // sync-checkpoint should always be accepted block + assert(mapBlockIndex.count(hashSyncCheckpoint)); + const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; + + if (nHeight > pindexSync->nHeight) + { + // trace back to same height as sync-checkpoint + const CBlockIndex* pindex = pindexPrev; + while (pindex->nHeight > pindexSync->nHeight) + if (!(pindex = pindex->pprev)) + return error("CheckSync: pprev null - block index structure failure"); + if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint) + return false; // only descendant of sync-checkpoint can pass check + } + if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint) + return false; // same height with sync-checkpoint + if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock)) + return false; // lower height than sync-checkpoint + return true; + } + + bool WantedByPendingSyncCheckpoint(uint256 hashBlock) + { + LOCK(cs_hashSyncCheckpoint); + if (hashPendingCheckpoint == 0) + return false; + if (hashBlock == hashPendingCheckpoint) + return true; + if (mapOrphanBlocks.count(hashPendingCheckpoint) + && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint])) + return true; + return false; + } + + // ppcoin: reset synchronized checkpoint to last hardened checkpoint + bool ResetSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + const uint256& hash = mapCheckpoints.rbegin()->second; + if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain()) + { + // checkpoint block accepted but not yet in main chain + printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str()); + CTxDB txdb; + CBlock block; + if (!block.ReadFromDisk(mapBlockIndex[hash])) + return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str()); + if (!block.SetBestChain(txdb, mapBlockIndex[hash])) + { + return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str()); + } + } + else if(!mapBlockIndex.count(hash)) + { + // checkpoint block not yet accepted + hashPendingCheckpoint = hash; + checkpointMessagePending.SetNull(); + printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str()); + } + + BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints) + { + const uint256& hash = i.second; + if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain()) + { + if (!WriteSyncCheckpoint(hash)) + return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str()); + printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str()); + return true; + } + } + + return false; + } + + void AskForPendingSyncCheckpoint(CNode* pfrom) + { + LOCK(cs_hashSyncCheckpoint); + if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint))) + pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint)); + } + + bool SetCheckpointPrivKey(std::string strPrivKey) + { + if (fDebug) + printf("SetCheckpointPrivKey()\n"); + + // Test signing a sync-checkpoint with genesis block + CSyncCheckpoint checkpoint; + checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet; + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedSyncCheckpoint)checkpoint; + checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); + + std::vector vchPrivKey = ParseHex(strPrivKey); + CKey key; + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) + return false; + + // Test signing successful, proceed + CSyncCheckpoint::strMasterPrivKey = strPrivKey; + return true; + } + + bool SendSyncCheckpoint(uint256 hashCheckpoint) + { + if (fDebug) + printf("SendSyncCheckpoint()\n"); + + CSyncCheckpoint checkpoint; + checkpoint.hashCheckpoint = hashCheckpoint; + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedSyncCheckpoint)checkpoint; + checkpoint.vchMsg = std::vector(sMsg.begin(), sMsg.end()); + + if (CSyncCheckpoint::strMasterPrivKey.empty()) + return error("SendSyncCheckpoint: Checkpoint master key unavailable."); + std::vector vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey); + CKey key; + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) + return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?"); + + if (!checkpoint.ProcessSyncCheckpoint(NULL)) + { + printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n"); + return false; + } + + // Relay checkpoint + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + checkpoint.RelayTo(pnode); + } + return true; + } + + // Is the sync-checkpoint outside maturity window? + bool IsMatureSyncCheckpoint() + { + LOCK(cs_hashSyncCheckpoint); + // sync-checkpoint should always be accepted block + assert(mapBlockIndex.count(hashSyncCheckpoint)); + const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; + return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity || + pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime()); + } +} + +// ppcoin: sync-checkpoint master key +const std::string CSyncCheckpoint::strMasterPubKey = "02334512c0d7a9a69289c4b7ad1c76e5330ef9dacd9da912c91a853986bddf0434"; + +std::string CSyncCheckpoint::strMasterPrivKey = ""; + +// ppcoin: verify signature of sync-checkpoint message +bool CSyncCheckpoint::CheckSignature() +{ + CKey key; + if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey))) + return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed"); + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + return error("CSyncCheckpoint::CheckSignature() : verify signature failed"); + + // Now unserialize the data + CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); + sMsg >> *(CUnsignedSyncCheckpoint*)this; + return true; +} + +// ppcoin: process synchronized checkpoint +bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom) +{ + if (!CheckSignature()) + return false; + + LOCK(Checkpoints::cs_hashSyncCheckpoint); + if (!mapBlockIndex.count(hashCheckpoint)) + { + // We haven't received the checkpoint chain, keep the checkpoint as pending + Checkpoints::hashPendingCheckpoint = hashCheckpoint; + Checkpoints::checkpointMessagePending = *this; + printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str()); + // Ask this guy to fill in what we're missing + if (pfrom) + { + pfrom->PushGetBlocks(pindexBest, hashCheckpoint); + // ask directly as well in case rejected earlier by duplicate + // proof-of-stake because getblocks may not get it this time + pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint)); + } + return false; + } + + if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) + return false; + + CTxDB txdb; + CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint]; + if (!pindexCheckpoint->IsInMainChain()) + { + // checkpoint chain received but not yet main chain + CBlock block; + if (!block.ReadFromDisk(pindexCheckpoint)) + return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); + if (!block.SetBestChain(txdb, pindexCheckpoint)) + { + Checkpoints::hashInvalidCheckpoint = hashCheckpoint; + return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + } + + if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint)) + return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str()); + Checkpoints::checkpointMessage = *this; + Checkpoints::hashPendingCheckpoint = 0; + Checkpoints::checkpointMessagePending.SetNull(); + printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str()); + return true; +} diff --git a/src/kernel.cpp b/src/kernel.cpp new file mode 100644 index 0000000..39762f3 --- /dev/null +++ b/src/kernel.cpp @@ -0,0 +1,390 @@ +// Copyright (c) 2012-2013 The PPCoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "kernel.h" +#include "txdb.h" + +using namespace std; + +typedef std::map MapModifierCheckpoints; + +// Hard checkpoints of stake modifiers to ensure they are deterministic +static std::map mapStakeModifierCheckpoints = + boost::assign::map_list_of + ( 100, 0x09206ffe ) + ( 180, 0xe250492b ) + ; + +// Hard checkpoints of stake modifiers to ensure they are deterministic (testNet) +static std::map mapStakeModifierCheckpointsTestNet = + boost::assign::map_list_of + ( 0, 0xfd11f4e7 ) + ; + +// Get time weight +int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) +{ + // Kernel hash weight starts from 0 at the min age + // this change increases active coins participating the hash and helps + // to secure the network when proof-of-stake difficulty is low + + return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); +} + +// Get the last stake modifier and its generation time from a given block +static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime) +{ + if (!pindex) + return error("GetLastStakeModifier: null pindex"); + while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier()) + pindex = pindex->pprev; + if (!pindex->GeneratedStakeModifier()) + return error("GetLastStakeModifier: no generation at genesis block"); + nStakeModifier = pindex->nStakeModifier; + nModifierTime = pindex->GetBlockTime(); + return true; +} + +// Get selection interval section (in seconds) +static int64_t GetStakeModifierSelectionIntervalSection(int nSection) +{ + assert (nSection >= 0 && nSection < 64); + return (nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1)))); +} + +// Get stake modifier selection interval (in seconds) +static int64_t GetStakeModifierSelectionInterval() +{ + int64_t nSelectionInterval = 0; + for (int nSection=0; nSection<64; nSection++) + nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection); + return nSelectionInterval; +} + +// select a block from the candidate blocks in vSortedByTimestamp, excluding +// already selected blocks in vSelectedBlocks, and with timestamp up to +// nSelectionIntervalStop. +static bool SelectBlockFromCandidates(vector >& vSortedByTimestamp, map& mapSelectedBlocks, + int64_t nSelectionIntervalStop, uint64_t nStakeModifierPrev, const CBlockIndex** pindexSelected) +{ + bool fSelected = false; + uint256 hashBest = 0; + *pindexSelected = (const CBlockIndex*) 0; + BOOST_FOREACH(const PAIRTYPE(int64_t, uint256)& item, vSortedByTimestamp) + { + if (!mapBlockIndex.count(item.second)) + return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str()); + const CBlockIndex* pindex = mapBlockIndex[item.second]; + if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop) + break; + if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0) + continue; + // compute the selection hash by hashing its proof-hash and the + // previous proof-of-stake modifier + CDataStream ss(SER_GETHASH, 0); + ss << pindex->hashProof << nStakeModifierPrev; + uint256 hashSelection = Hash(ss.begin(), ss.end()); + // the selection hash is divided by 2**32 so that proof-of-stake block + // is always favored over proof-of-work block. this is to preserve + // the energy efficiency property + if (pindex->IsProofOfStake()) + hashSelection >>= 32; + if (fSelected && hashSelection < hashBest) + { + hashBest = hashSelection; + *pindexSelected = (const CBlockIndex*) pindex; + } + else if (!fSelected) + { + fSelected = true; + hashBest = hashSelection; + *pindexSelected = (const CBlockIndex*) pindex; + } + } + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString().c_str()); + return fSelected; +} + +// Stake Modifier (hash modifier of proof-of-stake): +// The purpose of stake modifier is to prevent a txout (coin) owner from +// computing future proof-of-stake generated by this txout at the time +// of transaction confirmation. To meet kernel protocol, the txout +// must hash with a future stake modifier to generate the proof. +// Stake modifier consists of bits each of which is contributed from a +// selected block of a given block group in the past. +// The selection of a block is based on a hash of the block's proof-hash and +// the previous stake modifier. +// Stake modifier is recomputed at a fixed time interval instead of every +// block. This is to make it difficult for an attacker to gain control of +// additional bits in the stake modifier, even after generating a chain of +// blocks. +bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier) +{ + nStakeModifier = 0; + fGeneratedStakeModifier = false; + if (!pindexPrev) + { + fGeneratedStakeModifier = true; + return true; // genesis block's modifier is 0 + } + // First find current stake modifier and its generation block time + // if it's not old enough, return the same stake modifier + int64_t nModifierTime = 0; + if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime)) + return error("ComputeNextStakeModifier: unable to get last modifier"); + if (fDebug) + { + printf("ComputeNextStakeModifier: prev modifier=0x%016"PRIx64" time=%s\n", nStakeModifier, DateTimeStrFormat(nModifierTime).c_str()); + } + if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval) + return true; + + // Sort candidate blocks by timestamp + vector > vSortedByTimestamp; + vSortedByTimestamp.reserve(64 * nModifierInterval / nTargetSpacing); + int64_t nSelectionInterval = GetStakeModifierSelectionInterval(); + int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / nModifierInterval) * nModifierInterval - nSelectionInterval; + const CBlockIndex* pindex = pindexPrev; + while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart) + { + vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash())); + pindex = pindex->pprev; + } + int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0; + reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end()); + sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end()); + + // Select 64 blocks from candidate blocks to generate stake modifier + uint64_t nStakeModifierNew = 0; + int64_t nSelectionIntervalStop = nSelectionIntervalStart; + map mapSelectedBlocks; + for (int nRound=0; nRoundGetStakeEntropyBit()) << nRound); + // add the selected block from candidates to selected list + mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex)); + if (fDebug && GetBoolArg("-printstakemodifier")) + printf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n", nRound, DateTimeStrFormat(nSelectionIntervalStop).c_str(), pindex->nHeight, pindex->GetStakeEntropyBit()); + } + + // Print selection map for visualization of the selected blocks + if (fDebug && GetBoolArg("-printstakemodifier")) + { + string strSelectionMap = ""; + // '-' indicates proof-of-work blocks not selected + strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-'); + pindex = pindexPrev; + while (pindex && pindex->nHeight >= nHeightFirstCandidate) + { + // '=' indicates proof-of-stake blocks not selected + if (pindex->IsProofOfStake()) + strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "="); + pindex = pindex->pprev; + } + BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks) + { + // 'S' indicates selected proof-of-stake blocks + // 'W' indicates selected proof-of-work blocks + strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W"); + } + printf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str()); + } + if (fDebug) + { + printf("ComputeNextStakeModifier: new modifier=0x%016"PRIx64" time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()).c_str()); + } + + nStakeModifier = nStakeModifierNew; + fGeneratedStakeModifier = true; + return true; +} + +// The stake modifier used to hash for a stake kernel is chosen as the stake +// modifier about a selection interval later than the coin generating the kernel +static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake) +{ + nStakeModifier = 0; + if (!mapBlockIndex.count(hashBlockFrom)) + return error("GetKernelStakeModifier() : block not indexed"); + const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom]; + nStakeModifierHeight = pindexFrom->nHeight; + nStakeModifierTime = pindexFrom->GetBlockTime(); + int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval(); + const CBlockIndex* pindex = pindexFrom; + + // loop to find the stake modifier later by a selection interval + while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval) + { + if (!pindex->pnext) + { // reached best block; may happen if node is behind on block chain + if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime())) + return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s", + pindex->GetBlockHash().ToString().c_str(), pindex->nHeight, hashBlockFrom.ToString().c_str()); + else + return false; + } + pindex = pindex->pnext; + if (pindex->GeneratedStakeModifier()) + { + nStakeModifierHeight = pindex->nHeight; + nStakeModifierTime = pindex->GetBlockTime(); + } + } + nStakeModifier = pindex->nStakeModifier; + return true; +} + +// ppcoin kernel protocol +// coinstake must meet hash target according to the protocol: +// kernel (input 0) must meet the formula +// hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight +// this ensures that the chance of getting a coinstake is proportional to the +// amount of coin age one owns. +// The reason this hash is chosen is the following: +// nStakeModifier: scrambles computation to make it very difficult to precompute +// future proof-of-stake at the time of the coin's confirmation +// txPrev.block.nTime: prevent nodes from guessing a good timestamp to +// generate transaction for future advantage +// txPrev.offset: offset of txPrev inside block, to reduce the chance of +// nodes generating coinstake at the same time +// txPrev.nTime: reduce the chance of nodes generating coinstake at the same +// time +// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes +// generating coinstake at the same time +// block/tx hash should not be used here as they can be generated in vast +// 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) +{ + if (nTimeTx < txPrev.nTime) // Transaction timestamp violation + return error("CheckStakeKernelHash() : nTime violation"); + + unsigned int nTimeBlockFrom = blockFrom.GetBlockTime(); + if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement + return error("CheckStakeKernelHash() : min age violation"); + + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + int64_t nValueIn = txPrev.vout[prevout.n].nValue; + + uint256 hashBlockFrom = blockFrom.GetHash(); + + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); + targetProofOfStake = (bnCoinDayWeight * bnTargetPerCoinDay).getuint256(); + + // Calculate hash + CDataStream ss(SER_GETHASH, 0); + uint64_t nStakeModifier = 0; + int nStakeModifierHeight = 0; + int64_t nStakeModifierTime = 0; + + if (!GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake)) + return false; + ss << nStakeModifier; + + ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx; + hashProofOfStake = Hash(ss.begin(), ss.end()); + if (fPrintProofOfStake) + { + printf("CheckStakeKernelHash() : using modifier 0x%016"PRIx64" at height=%d timestamp=%s for block from height=%d timestamp=%s\n", + nStakeModifier, nStakeModifierHeight, + DateTimeStrFormat(nStakeModifierTime).c_str(), + mapBlockIndex[hashBlockFrom]->nHeight, + DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); + printf("CheckStakeKernelHash() : check modifier=0x%016"PRIx64" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", + nStakeModifier, + nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, + hashProofOfStake.ToString().c_str()); + } + + // Now check if proof-of-stake hash meets target protocol + if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay) + return false; + if (fDebug && !fPrintProofOfStake) + { + printf("CheckStakeKernelHash() : using modifier 0x%016"PRIx64" at height=%d timestamp=%s for block from height=%d timestamp=%s\n", + nStakeModifier, nStakeModifierHeight, + DateTimeStrFormat(nStakeModifierTime).c_str(), + mapBlockIndex[hashBlockFrom]->nHeight, + DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); + printf("CheckStakeKernelHash() : pass modifier=0x%016"PRIx64" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", + nStakeModifier, + nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, + hashProofOfStake.ToString().c_str()); + } + return true; +} + +// Check kernel hash target and coinstake signature +bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake) +{ + if (!tx.IsCoinStake()) + 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]; + + // First try finding the previous transaction in database + CTxDB txdb("r"); + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + return tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download + + // Verify signature + if (!VerifySignature(txPrev, tx, 0, 0)) + return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str())); + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction + + if (!CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, 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 + + return true; +} + +// Check whether the coinstake timestamp meets protocol +bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx) +{ + // v0.3 protocol + return (nTimeBlock == nTimeTx); +} + +// Get stake modifier checksum +unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex) +{ + assert (pindex->pprev || pindex->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); + + // Hash previous checksum with flags, hashProofOfStake and nStakeModifier + CDataStream ss(SER_GETHASH, 0); + if (pindex->pprev) + ss << pindex->pprev->nStakeModifierChecksum; + ss << pindex->nFlags << (pindex->IsProofOfStake() ? pindex->hashProof : 0) << pindex->nStakeModifier; + uint256 hashChecksum = Hash(ss.begin(), ss.end()); + hashChecksum >>= (256 - 32); + return hashChecksum.Get64(); +} + +// Check stake modifier hard checkpoints +bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum) +{ + MapModifierCheckpoints& checkpoints = (fTestNet ? mapStakeModifierCheckpointsTestNet : mapStakeModifierCheckpoints); + + if (checkpoints.count(nHeight)) + return nStakeModifierChecksum == checkpoints[nHeight]; + return true; +}