text: // 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.
  #ifdef _MSC_VER
      #include "msvc_warnings.push.h"
  #endif
  
  #include <boost/assign/list_of.hpp> // for 'map_list_of()'
  #include <boost/foreach.hpp>
  
  #ifndef BITCOIN_CHECKPOINT_H
   #include "checkpoints.h"
  #endif
  
  #ifndef BITCOIN_TXDB_H
   #include "txdb.h"
  #endif
  
  namespace Checkpoints
  {
      typedef std::map<int, std::pair<uint256, unsigned int> > 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, std::make_pair(hashGenesisBlock, 1367991220) )
          ( 15000, std::make_pair(uint256("0x00000082cab82d04354692fac3b83d19cbe3c3ab4b73610d0e73397545eb012e"), 1368024582) )
          ( 30000, std::make_pair(uint256("0x0000000af2f6e71951d6e8befbd43a3dac36681b5095cb822b5c9c8de626e371"), 1368071548) )
          ( 45000, std::make_pair(uint256("0x00000000591110a1411cf37739cde0c558c0c070aa38686d89b2e70fe39b654f"), 1368188743) )
          ( 60000, std::make_pair(uint256("0x000000000c067c5df98a8285ff045c3ffee46eb64b248bc6622f6bdceb8558be"), 1368486465) )
          ( 75000, std::make_pair(uint256("0x000000004ab2d277c8a056f55f32efa515a9931cb0404d60d0efc4f573412e66"), 1369894448) )
          ( 90000, std::make_pair(uint256("0x000000000cfe2ec9d27b784c2627c3864d26e5829cc5b18b4eff37d863ed0675"), 1371070047) )
          ( 105000, std::make_pair(uint256("0x00000000b0480b6a15fee32ee47d4b30dc82dc44ab680f1debb2ce2b13f73aab"), 1371870193) )
          ( 120000, std::make_pair(uint256("0x00000000d843c5c818620d00c9352e0cc3bbf7fdb9d69093795fbfffff13c92a"), 1372878458) )
          ( 135000, std::make_pair(uint256("0x0000000292cb16d5935e015a786d33f3228da23d92dfeb6ddff7249a3227f956"), 1374104602) )
          ( 150000, std::make_pair(uint256("0x000000035d01ee7f75032c0293a7e6b1217d447fe3e000ede7911cb0520c60c7"), 1375119379) )
          ( 165000, std::make_pair(uint256("0x00000001e790d65de9541af419465338220de69e3ffcbda427af2fc94741d321"), 1375961238) )
          ( 180000, std::make_pair(uint256("0x000000054595380eb246887c79ff25fd997eebd3e59385830e4987c11153a31d"), 1377139488) )
          ( 195000, std::make_pair(uint256("0x00000007c12dc0533ab2dbf66c56308ee0b259e1a5f09435381ea6d541b6a2c5"), 1378153929) )
          ( 214998, std::make_pair(uint256("0x000000054e1a96d68bddda9b63276d604f33b6679d7dab079e4d241a1ee31be9"), 1379316207) )
          ( 236895, std::make_pair(uint256("0x00000000a103442adaf96aa2af5cbc083e74cddbea558a7740c7a6781f561c08"), 1380923756) )
          ( 259405, std::make_pair(uint256("0x00000019f67b00bc3482208d5b82393df96c648b510f24ab0d3318294a9bcde5"), 1382451651) )
          ( 281002, std::make_pair(uint256("0x0000003076d627bd2e13e914a3032c2ef8a69de4792452c697bc23a6e158be2b"), 1383844777) )
          ( 303953, std::make_pair(uint256("0x0000007c2fd3ca884a5b77e9b2a508cb617b75f2162beacafffa7193d2db7069"), 1385560454) )
          ( 388314, std::make_pair(uint256("0x0000001d9a76c3a52288638568dad601a8a69ba7dc1038ff4d12b614de73a49a"), 1390167524) )
          ( 420000, std::make_pair(uint256("0x000000368f2f40e3d2d9ed2a2220c8a424e3d80871c0b72c2bdeea35863aa779"), 1392241857) )
          ( 465000, std::make_pair(uint256("0x0000000826f7b8b504fde92ba0388b647b2179d4b2c7cdfd232505cd7d79ac61"), 1394854385) )
          ( 487658, std::make_pair(uint256("0x00000008dee4518a08084c5b65d647a57faf7ff28bc7d8786719ac13d21356d4"), 1396181452) )
          ( 550177, std::make_pair(uint256("0x000000087d507052cb66d5a3770cf62f8ff9196ab861ffec3452d939b818567b"), 1399962064) )
          ( 612177, std::make_pair(uint256("0x0000004bc02ebb045398fd8cdb249b790892a4fa3a8b03dbbbf5743c53f2a508"), 1404141665) )
  		( 712177, std::make_pair(uint256("0x000001881da9ee73de48a54ebae7d0dec3b453d795b6704d62414e4b581e3aea"), 1410900724) )
          ;
  
      // TestNet has no checkpoints
  
  // YACOIN TODO CHANGE
      static MapCheckpoints mapCheckpointsTestnet =
          boost::assign::map_list_of
          ( 0, std::make_pair(hashGenesisBlockTestNet, nChainStartTimeTestNet + 20) )
          ;
  
      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.first);
      }
  
      int GetTotalBlocksEstimate()
      {
          MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  
          return checkpoints.rbegin()->first;
      }
  
      unsigned int GetLastCheckpointTime()
      {
          MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  
          return checkpoints.rbegin()->second.second;
      }
  
      CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
      {
          MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
  
          BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
          {
              const uint256& hash = i.second.first;
              std::map<uint256, CBlockIndex*>::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))
          {
  #ifndef Yac1dot0
              error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
  #endif
          }
          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());
  
  #ifndef USE_LEVELDB
          txdb.Close();
  #endif
  
          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());
                  }
              }
  
  #ifndef USE_LEVELDB
              txdb.Close();
  #endif
              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() + CHECKPOINT_MAX_SPAN > pindexBest->GetBlockTime() || pindex->nHeight + 8 > 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
          Yassert(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.first;
          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());
              }
  
  #ifndef USE_LEVELDB
              txdb.Close();
  #endif
  
          }
          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.first;
              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 ? hashGenesisBlock : hashGenesisBlockTestNet;
          CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
          sMsg << (CUnsignedSyncCheckpoint)checkpoint;
          checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
  
          std::vector<unsigned char> 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<unsigned char>(sMsg.begin(), sMsg.end());
  
          if (CSyncCheckpoint::strMasterPrivKey.empty())
              return error("SendSyncCheckpoint: Checkpoint master key unavailable.");
          std::vector<unsigned char> 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
          Yassert(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 = "04434f8f64499c03a13cdc928882b8b6c03009a7c672fb34de0a394b484b737ef26e0723e62e3787d8e3f2cd2cd44209531b59fcd6c428903ae139460973189cb4";
  
  
  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;
  }
  
  // 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());
          }
      }
  
  #ifndef USE_LEVELDB
      txdb.Close();
  #endif
  
      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;
  }
  #ifdef _MSC_VER
      #include "msvc_warnings.pop.h"
  #endif
  