// 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 "checkpoints.h" #include "txdb.h" #include "main.h" #include "uint256.h" 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 = { { 0, hashGenesisBlockOfficial }, { 2000, uint256("0x0000000000b5f20078bf46ebdf1500813bb6b2cb482065aa93b89e073b2c6467")}, { 2101, uint256("0xd4ea1ac45b63c8162a7fc8033cec441db8d532ba988202849d7831e32fe2d059")}, { 2847, uint256("0xb5015e2835f13fd3bb6135cff9b31ac33310c9b77d694bdb592b8680d98d018e")}, { 3589, uint256("0xb12a2ca3db4e288cada98aa2139768532bd4474c49dd7b8158031032dac08d51")}, { 3935, uint256("0xe16290c9757d1368b8d7c35de07d4f8f70c2c9f9c785b667df0c3bff85086ca6")}, { 5703, uint256("0x587db07bb2172ad7db72c5fabc2518262a1b27f503f99417510b2c6fafa6557b")}, { 9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")}, { 9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")}, { 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")}, { 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")}, { 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")}, {2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")}, }; static MapCheckpoints mapCheckpointsTestnet = { { 0, hashGenesisBlockTestNet }, { 2000, uint256("0x0000000000b5f20078bf46ebdf1500813bb6b2cb482065aa93b89e073b2c6467")}, { 2101, uint256("0xd4ea1ac45b63c8162a7fc8033cec441db8d532ba988202849d7831e32fe2d059")}, { 2847, uint256("0xb5015e2835f13fd3bb6135cff9b31ac33310c9b77d694bdb592b8680d98d018e")}, { 3589, uint256("0xb12a2ca3db4e288cada98aa2139768532bd4474c49dd7b8158031032dac08d51")}, { 3935, uint256("0xe16290c9757d1368b8d7c35de07d4f8f70c2c9f9c785b667df0c3bff85086ca6")}, { 5703, uint256("0x587db07bb2172ad7db72c5fabc2518262a1b27f503f99417510b2c6fafa6557b")}, { 9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")}, { 9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")}, { 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")}, { 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")}, { 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")}, {2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")}, }; 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); for (auto it = checkpoints.rbegin(); it != checkpoints.rend(); ++it) { const uint256& hash = it->second; std::map::const_iterator t = mapBlockIndex.find(hash); if (t != mapBlockIndex.end()) return t->second; } return NULL; } // triangles: synchronized checkpoint (centrally broadcasted) uint256 hashSyncCheckpoint = uint256("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021"); uint256 hashPendingCheckpoint = uint256("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021"); CSyncCheckpoint checkpointMessage; CSyncCheckpoint checkpointMessagePending; uint256 hashInvalidCheckpoint = 0; CCriticalSection cs_hashSyncCheckpoint; // triangles: 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; } // triangles: 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()) { for (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() + CHECKPOINT_MAX_SPAN > pindexBest->GetBlockTime() || pindex->nHeight + 8 > pindexBest->nHeight)) pindex = pindex->pprev; return pindex->GetBlockHash(); } // Check against synchronized checkpoint // Disabled: master key removed in V5, no new sync checkpoints possible. // Always returns true to prevent stale DB-persisted checkpoints from blocking IBD. bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev) { 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; } // triangles: 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()); } for (auto it = mapCheckpoints.rbegin(); it != mapCheckpoints.rend(); ++it) { const uint256& hash = it->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) { // Test signing a sync-checkpoint with genesis block CSyncCheckpoint checkpoint; checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlockOfficial : 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) { 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); for (CNode* pnode : vNodes) checkpoint.RelayTo(pnode); } return true; } // Is the sync-checkpoint outside maturity window? bool IsMatureSyncCheckpoint() { LOCK(cs_hashSyncCheckpoint); if (!mapBlockIndex.count(hashSyncCheckpoint)) return true; // no valid sync checkpoint, treat as mature const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity || pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime()); } } // triangles: sync-checkpoint master key (DISABLED for decentralization - v5 hard fork) const std::string CSyncCheckpoint::strMasterPubKey = ""; std::string CSyncCheckpoint::strMasterPrivKey = ""; // triangles: verify signature of sync-checkpoint message // Master key system disabled - checkpoint signatures are no longer required bool CSyncCheckpoint::CheckSignature() { // Deserialize the checkpoint data without signature verification CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); sMsg >> *(CUnsignedSyncCheckpoint*)this; return true; } // triangles: 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; }