text: // Copyright (c) 2018, The CitiCash Project
  // Copyright (c) 2017, SUMOKOIN
  // Copyright (c) 2014-2016, The Monero Project
  //
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without modification, are
  // permitted provided that the following conditions are met:
  //
  // 1. Redistributions of source code must retain the above copyright notice, this list of
  //    conditions and the following disclaimer.
  //
  // 2. Redistributions in binary form must reproduce the above copyright notice, this list
  //    of conditions and the following disclaimer in the documentation and/or other
  //    materials provided with the distribution.
  //
  // 3. Neither the name of the copyright holder nor the names of its contributors may be
  //    used to endorse or promote products derived from this software without specific
  //    prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  //
  // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
  
  #include "include_base_utils.h"
  
  using namespace epee;
  
  #include "checkpoints.h"
  
  #include "common/dns_utils.h"
  #include "include_base_utils.h"
  #include "crypto/hash.h"
  #include <sstream>
  #include <random>
  namespace cryptonote
  {
    //---------------------------------------------------------------------------
    checkpoints::checkpoints()
    {
    }
    //---------------------------------------------------------------------------
    bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str)
    {
      crypto::hash h = null_hash;
      bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h);
      CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!");
  
      // return false if adding at a height we already have AND the hash is different
      if (m_points.count(height))
      {
        CHECK_AND_ASSERT_MES(h == m_points[height], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
      }
      m_points[height] = h;
      return true;
    }
    //---------------------------------------------------------------------------
    bool checkpoints::is_in_checkpoint_zone(uint64_t height) const
    {
      return !m_points.empty() && (height <= (--m_points.end())->first);
    }
    //---------------------------------------------------------------------------
    bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const
    {
      auto it = m_points.find(height);
      is_a_checkpoint = it != m_points.end();
      if(!is_a_checkpoint)
        return true;
  
      if(it->second == h)
      {
        LOG_PRINT_GREEN("CHECKPOINT PASSED FOR HEIGHT " << height << " " << h, LOG_LEVEL_1);
        return true;
      }else
      {
        LOG_ERROR("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH: " << it->second << ", FETCHED HASH: " << h);
        return false;
      }
    }
    //---------------------------------------------------------------------------
    bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const
    {
      bool ignored;
      return check_block(height, h, ignored);
    }
    //---------------------------------------------------------------------------
    //FIXME: is this the desired behavior?
    bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const
    {
      if (0 == block_height)
        return false;
  
      auto it = m_points.upper_bound(blockchain_height);
      // Is blockchain_height before the first checkpoint?
      if (it == m_points.begin())
        return true;
  
      --it;
      uint64_t checkpoint_height = it->first;
      return checkpoint_height < block_height;
    }
    //---------------------------------------------------------------------------
    uint64_t checkpoints::get_max_height() const
    {
      std::map< uint64_t, crypto::hash >::const_iterator highest =
          std::max_element( m_points.begin(), m_points.end(),
                           ( boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _1) <
                             boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _2 ) ) );
      return highest->first;
    }
    //---------------------------------------------------------------------------
    const std::map<uint64_t, crypto::hash>& checkpoints::get_points() const
    {
      return m_points;
    }
  
    bool checkpoints::check_for_conflicts(const checkpoints& other) const
    {
      for (auto& pt : other.get_points())
      {
        if (m_points.count(pt.first))
        {
          CHECK_AND_ASSERT_MES(pt.second == m_points.at(pt.first), false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
        }
      }
      return true;
    }
  
    bool checkpoints::init_default_checkpoints()
    {
  #ifdef DEVNET
      DEVNET_CHECKPOINTS
  #else
      ADD_CHECKPOINT(     1,"9f63b6539a01271baf90813fc999a1fd40fcf54d8f30377a2d437cfc98605308");
      ADD_CHECKPOINT(   100,"b917d748282760f0139c9e411a835f2a77db5de739febbb146fcd9a2110c9be3");
      ADD_CHECKPOINT( 10000,"58dbfcc53ecbcbfb7934d2b4fdbcdf0e6a8360887d64ec9e5c3665883886ebdf");
      ADD_CHECKPOINT(100000,"da154a4294099f17b63d314ecfc892853dc29644e0e5ec740f920283854142a2");
      ADD_CHECKPOINT(271441,"f12bda0bf1ac8f666292315c135b7f8908f754d907b95e5dac57464ee2b6ad91");
  #endif
      return true;
    }
  
    bool checkpoints::load_checkpoints_from_json(const std::string json_hashfile_fullpath)
    {
      boost::system::error_code errcode;
      if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode)))
      {
        LOG_PRINT_L1("Blockchain checkpoints file not found");
        return true;
      }
  
      LOG_PRINT_L1("Adding checkpoints from blockchain hashfile");
  
      uint64_t prev_max_height = get_max_height();
      LOG_PRINT_L1("Hard-coded max checkpoint height is " << prev_max_height);
      t_hash_json hashes;
      epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath);
      for (std::vector<t_hashline>::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); )
      {
        uint64_t height;
        height = it->height;
        if (height <= prev_max_height) {
  	LOG_PRINT_L1("ignoring checkpoint height " << height);
        } else {
  	std::string blockhash = it->hash;
  	LOG_PRINT_L1("Adding checkpoint height " << height << ", hash=" << blockhash);
  	ADD_CHECKPOINT(height, blockhash);
        }
        ++it;
      }
  
      return true;
    }
  
    bool checkpoints::load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet) {
      return load_checkpoints_from_json(json_hashfile_fullpath);
    }
  }
  