diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 6a37f83..b79b8c1 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -1,445 +1,471 @@ -// 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")}, - // Recent finality pin (PoS era). Closes the long unchecked span from - // 17650 to the live tip so stale-bootstrap / low-trust forks below - // this height are rejected outright. Hash from the canonical chain. - { 2205000, uint256("0x6bdd3c5e5a32e1dd9a70e705f1a28d1dd84929f89579bd2696d41bc87f39446f")}, - { 2206004, uint256("0xb34e8e6a7bb7f52167d81aaad4d26f87a876898fdd0fce860916fc1aaf9a2a46")}, - // Continuous finality pins: every 1000 blocks from 2206500 onward so the - // gap between the last hardcoded checkpoint and the live tip stays bounded. - // Without these, a fresh node syncing from zero (no snapshot) has 8,400+ - // unverified blocks at tip — a peer feeding fork blocks at those heights - // could trick an IBD node into accepting a divergent chain. With these - // pins, any divergence >1000 blocks is rejected at AcceptBlock time. - // All hashes verified against the canonical chain on 2026-07-01. - { 2206500, uint256("0x707ea288242227e9b36ceeeecd5a16a6c918f8b6f7e6375128cba908ebfcbf27")}, - { 2207000, uint256("0x7af1cc23fdffb3a9ed2eb9aa5a8697e8af2f98c67c4f6baa9f4d7899cbfaf4ca")}, - { 2210000, uint256("0xe2dc2e55c6e1b3d2ea9d8a1f2b274bf64053ddd6a61335dc6896aa9c056956be")}, - { 2211000, uint256("0x61c8a179c928a1f0bbffa029b4f1aea67b04a98227a6d02e6137280404ed29dc")}, - { 2212000, uint256("0xf4df2b5d0d1de326b97ed5a3eeefef307a51791e03af401373e142f00453a9a8")}, - { 2213000, uint256("0x7bc9652d423676c52ba8b0a287e0b46e1eca6e8eecc51d3f30e0d665d3b236f5")}, - { 2214000, uint256("0x17e61ceb45db36358aaabe91b094a77ecba32370a467185fa9af75eef6c8e414")}, - { 2214400, uint256("0x8ebb818f7280850c5a3916b7c8a2bca603f7c4f9926d3cdc2262f726035d96ed")}, - // Post-rebuild finality pin (v6.2.5.0). Closes the gap between - // the last hardcoded checkpoint and the live tip after -rebuildutxo. - // Hash from the canonical chain on DNS2 after fresh UTXO rebuild. - { 2219922, uint256("0x9ed3e1d38317950927f37f2867e3fc29e239fc1f4c57b182f55c6e04b73b52ec")}, - }; - - // Published UTXO snapshot file SHA256, keyed by snapshot height. - // Each entry binds height -> SHA256 of the canonical snapshot file produced by - // UtxoSnapshot::DumpSnapshot at that height. Used by SnapshotNet to verify - // P2P-delivered snapshots without trusting any peer. - // - // Maintainers: after producing a snapshot, sha256 the file and add an entry - // here. The corresponding (height, blockhash) must already exist in - // mapCheckpoints / mapCheckpointsTestnet. - static std::map mapSnapshotHashes = { - { 2206004, uint256("0x1419282dae817315ee1b955543f6248233fe5800f5e8488734a0ece5bd6781ea")}, - { 2219922, uint256("0x6dd8d782a04bb8dc4ccd5e88a4bc7726fe26bdebaed96b79242de1e2949b6ee6")}, - }; - - static std::map mapSnapshotHashesTestnet = { - }; - - 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")}, - }; - - 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; - } - - bool IsKnownCheckpoint(int nHeight, const uint256& hash) - { - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); - MapCheckpoints::const_iterator i = checkpoints.find(nHeight); - if (i == checkpoints.end()) return false; - return hash == i->second; - } - - int GetTotalBlocksEstimate() - { - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); - - return checkpoints.rbegin()->first; - } - - int GetBestSnapshotHeight() - { - std::map& snaps = (fTestNet ? mapSnapshotHashesTestnet : mapSnapshotHashes); - if (snaps.empty()) return 0; - return snaps.rbegin()->first; - } - - bool GetSnapshotHash(int nHeight, uint256& fileHashOut) - { - std::map& snaps = (fTestNet ? mapSnapshotHashesTestnet : mapSnapshotHashes); - auto it = snaps.find(nHeight); - if (it == snaps.end()) return false; - fileHashOut = it->second; - return true; - } - - 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 nullptr; - } - - // Independent of mapBlockIndex: returns the highest compiled checkpoint - // height for the current network. Returns -1 if the compiled map is - // empty (an unusual, but not impossible, configuration). Used as the - // fail-closed reorg floor before pindexLastHardenedCheckpoint has been - // resolved against the local block index (early IBD / reindex / - // bootstrap before the checkpoint block has been downloaded). - int GetLastCheckpointHeight() - { - MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); - if (checkpoints.empty()) return -1; - return checkpoints.rbegin()->first; - } - - // 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 nullptr; - } - - // 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) - { - auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; - 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; - } - - auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; - 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].get())) - 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()); - auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; - 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) - { - (void)strPrivKey; - return error("SetCheckpointPrivKey: synchronized checkpoints are disabled"); - } - - bool SendSyncCheckpoint(uint256 hashCheckpoint) - { - (void)hashCheckpoint; - return error("SendSyncCheckpoint: synchronized checkpoints are disabled"); - } - - // 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 -// The master-key system is disabled. Reject these legacy messages instead of -// treating unsigned data as authenticated if a dispatcher is added later. -bool CSyncCheckpoint::CheckSignature() -{ - return error("CSyncCheckpoint::CheckSignature: synchronized checkpoints are disabled"); -} - -// 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].get()) : hashCheckpoint)); - } - return false; - } - - if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) - return false; - - auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; - 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; -} +// 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")}, + // Recent finality pin (PoS era). Closes the long unchecked span from + // 17650 to the live tip so stale-bootstrap / low-trust forks below + // this height are rejected outright. Hash from the canonical chain. + { 2205000, uint256("0x6bdd3c5e5a32e1dd9a70e705f1a28d1dd84929f89579bd2696d41bc87f39446f")}, + { 2206004, uint256("0xb34e8e6a7bb7f52167d81aaad4d26f87a876898fdd0fce860916fc1aaf9a2a46")}, + // Continuous finality pins: every 1000 blocks from 2206500 onward so the + // gap between the last hardcoded checkpoint and the live tip stays bounded. + // Without these, a fresh node syncing from zero (no snapshot) has 8,400+ + // unverified blocks at tip — a peer feeding fork blocks at those heights + // could trick an IBD node into accepting a divergent chain. With these + // pins, any divergence >1000 blocks is rejected at AcceptBlock time. + // All hashes verified against the canonical chain on 2026-07-01. + { 2206500, uint256("0x707ea288242227e9b36ceeeecd5a16a6c918f8b6f7e6375128cba908ebfcbf27")}, + { 2207000, uint256("0x7af1cc23fdffb3a9ed2eb9aa5a8697e8af2f98c67c4f6baa9f4d7899cbfaf4ca")}, + { 2210000, uint256("0xe2dc2e55c6e1b3d2ea9d8a1f2b274bf64053ddd6a61335dc6896aa9c056956be")}, + { 2211000, uint256("0x61c8a179c928a1f0bbffa029b4f1aea67b04a98227a6d02e6137280404ed29dc")}, + { 2212000, uint256("0xf4df2b5d0d1de326b97ed5a3eeefef307a51791e03af401373e142f00453a9a8")}, + { 2213000, uint256("0x7bc9652d423676c52ba8b0a287e0b46e1eca6e8eecc51d3f30e0d665d3b236f5")}, + { 2214000, uint256("0x17e61ceb45db36358aaabe91b094a77ecba32370a467185fa9af75eef6c8e414")}, + { 2214400, uint256("0x8ebb818f7280850c5a3916b7c8a2bca603f7c4f9926d3cdc2262f726035d96ed")}, + // Post-rebuild finality pin (v6.2.5.0). Closes the gap between + // the last hardcoded checkpoint and the live tip after -rebuildutxo. + // Hash from the canonical chain on DNS2 after fresh UTXO rebuild. + { 2219922, uint256("0x9ed3e1d38317950927f37f2867e3fc29e239fc1f4c57b182f55c6e04b73b52ec")}, + // Live-tip finality pins (v6.2.6.0). Verified against DNS3 chain state + // on 2026-08-04. Closes the 4,841-block unchecked span between the + // last hardcoded pin (2,219,922) and the live tip (2,224,763). + // All hashes verified against the canonical chain on DNS3 (running + // v6.2.3.0-geb02f34) at block 2,224,763. Verification transcript + // (DNS3 getblockhash output) is archived in the v6.2.6.0 release + // notes on bootstrap.cryptographic-triangles.org. +// +// Note: the gap from 2,219,922 to 2,222,900 is 2,978 blocks (larger than the + // 1,000-block standard spacing), because block 2,220,000 etc. were + // not indexed in DNS3's local block index when this release was + // prepared. The 2,222,900+ pins restore the 1,000-block spacing + // guarantee from that point to the live tip. + { 2222900, uint256("0xe104c29d6a6ff983d9a02a9854a86c221a1f400f0116cb255cee2b8d5c7ced9f")}, + { 2223000, uint256("0x41926ba6dc9147e361ffd1ffc1a0357d7d7b66550ed05864d1ae103c6332371a")}, + { 2223500, uint256("0x998e65941f200359ca0c1f53ea128c27f83111e8bbb1db38b7ed2ed7a48b8e32")}, + { 2223700, uint256("0x97d3a70d258c34429c15b430e654fa1270e4de635ecec3c72ace92a0d04679c3")}, + { 2224000, uint256("0x4dddc0b555266a1207fef70af17db9a7b14ab5e1d7cf27882ea35cc77923841f")}, + { 2224500, uint256("0xe0fea543829dd0e8c02b7c657468cff775c7993658c16c1feaf1418b4080ba27")}, + { 2224700, uint256("0x2a8ea5ef954adb707286bc468fdf43d8d99d23a1d15cf4f17a35d58dd51b0944")}, + { 2224750, uint256("0x0f117fe05befb6d8a93c6e45bc3b3d48889208e2785ba6a3d723c8ad7c9d649f")}, + { 2224763, uint256("0x9d3575ac5428e64911e698ba0a8f773954b17b214a044d4b244fa2ec83c06674")}, // live tip + }; + + // Published UTXO snapshot file SHA256, keyed by snapshot height. + // Each entry binds height -> SHA256 of the canonical snapshot file produced by + // UtxoSnapshot::DumpSnapshot at that height. Used by SnapshotNet to verify + // P2P-delivered snapshots without trusting any peer. + // + // Maintainers: after producing a snapshot, sha256 the file and add an entry + // here. The corresponding (height, blockhash) must already exist in + // mapCheckpoints / mapCheckpointsTestnet. + static std::map mapSnapshotHashes = { + { 2206004, uint256("0x1419282dae817315ee1b955543f6248233fe5800f5e8488734a0ece5bd6781ea")}, + { 2219922, uint256("0x6dd8d782a04bb8dc4ccd5e88a4bc7726fe26bdebaed96b79242de1e2949b6ee6")}, + // Live-tip snapshot (v6.2.6.0). Generated from DNS3 (Samihost) at the + // canonical tip 2,224,763, blockhash 9d3575ac...06674. Verified against + // the canonical chain on 2026-08-04. + { 2224763, uint256("0xa7ea62ad4e158faf07973e5cd1539c1895154c4e28685a3eb7af458a001037b7")}, + }; + + static std::map mapSnapshotHashesTestnet = { + }; + + 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")}, + }; + + 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; + } + + bool IsKnownCheckpoint(int nHeight, const uint256& hash) + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + MapCheckpoints::const_iterator i = checkpoints.find(nHeight); + if (i == checkpoints.end()) return false; + return hash == i->second; + } + + int GetTotalBlocksEstimate() + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + + return checkpoints.rbegin()->first; + } + + int GetBestSnapshotHeight() + { + std::map& snaps = (fTestNet ? mapSnapshotHashesTestnet : mapSnapshotHashes); + if (snaps.empty()) return 0; + return snaps.rbegin()->first; + } + + bool GetSnapshotHash(int nHeight, uint256& fileHashOut) + { + std::map& snaps = (fTestNet ? mapSnapshotHashesTestnet : mapSnapshotHashes); + auto it = snaps.find(nHeight); + if (it == snaps.end()) return false; + fileHashOut = it->second; + return true; + } + + 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 nullptr; + } + + // Independent of mapBlockIndex: returns the highest compiled checkpoint + // height for the current network. Returns -1 if the compiled map is + // empty (an unusual, but not impossible, configuration). Used as the + // fail-closed reorg floor before pindexLastHardenedCheckpoint has been + // resolved against the local block index (early IBD / reindex / + // bootstrap before the checkpoint block has been downloaded). + int GetLastCheckpointHeight() + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + if (checkpoints.empty()) return -1; + return checkpoints.rbegin()->first; + } + + // 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 nullptr; + } + + // 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) + { + auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; + 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; + } + + auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; + 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].get())) + 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()); + auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; + 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) + { + (void)strPrivKey; + return error("SetCheckpointPrivKey: synchronized checkpoints are disabled"); + } + + bool SendSyncCheckpoint(uint256 hashCheckpoint) + { + (void)hashCheckpoint; + return error("SendSyncCheckpoint: synchronized checkpoints are disabled"); + } + + // 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 +// The master-key system is disabled. Reject these legacy messages instead of +// treating unsigned data as authenticated if a dispatcher is added later. +bool CSyncCheckpoint::CheckSignature() +{ + return error("CSyncCheckpoint::CheckSignature: synchronized checkpoints are disabled"); +} + +// 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].get()) : hashCheckpoint)); + } + return false; + } + + if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) + return false; + + auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder; + 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; +}