text: // Copyright (c) 2010 Satoshi Nakamoto
  // Copyright (c) 2009-2015 The Bitcoin developers
  // Copyright (c) 2014-2015 The Dash developers
  // Copyright (c) 2015-2020 The PIVX developers
  // Copyright (c) 2020-2021 The Sapphire Core Developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "chainparams.h"
  
  #include "chainparamsseeds.h"
  #include "consensus/merkle.h"
  #include "util.h"
  #include "utilstrencodings.h"
  
  #include <boost/assign/list_of.hpp>
  
  #include <assert.h>
  
  #define disabled 4294967295
  
  static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
  {
      CMutableTransaction txNew;
      txNew.nVersion = 1;
      txNew.vin.resize(1);
      txNew.vout.resize(1);
      txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
      txNew.vout[0].nValue = genesisReward;
      txNew.vout[0].scriptPubKey = genesisOutputScript;
  
      CBlock genesis;
      // genesis.vtx.push_back(std::make_shared<const CTransaction>(std::move(txNew)));
      genesis.vtx.push_back(txNew);
      genesis.hashPrevBlock.SetNull();
      genesis.nVersion = nVersion;
      genesis.nTime    = nTime;
      genesis.nBits    = nBits;
      genesis.nNonce   = nNonce;
      genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
      return genesis;
  }
  
  /**
   * Build the genesis block. Note that the output of the genesis coinbase cannot
   * be spent as it did not originally exist in the database.
   *
   * CBlock(hash=00000ffd590b14, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=e0028e, nTime=1390095618, nBits=1e0ffff0, nNonce=28917698, vtx=1)
   *   CTransaction(hash=e0028e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
   *     CTxIn(COutPoint(000000, -1), coinbase 04ffff001d01044c5957697265642030392f4a616e2f3230313420546865204772616e64204578706572696d656e7420476f6573204c6976653a204f76657273746f636b2e636f6d204973204e6f7720416363657074696e6720426974636f696e73)
   *     CTxOut(nValue=50.00000000, scriptPubKey=0xA9037BAC7050C479B121CF)
   *   vMerkleTree: e0028e
   */
  static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
  {
      const char* pszTimestamp = "25/Apr/2019 The SapphireCoin Launched!";
      const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
      return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
  }
  
  /**
   * Main network
   */
  /**
   * 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 Checkpoints::MapCheckpoints mapCheckpoints =
      boost::assign::map_list_of
      (0, uint256("0x00000eef0583695d6da23a78bab1c39939bbb54cf9bd5f0d4881c8eef364cd26"))
  	(1, uint256("0x000009fbce49246c2fef09c40ba658ea043cca483696fa2ef486b2411379b61c"))
      (6038, uint256("0xc81108274e6f270e7b0593fb2a6434c7f361c87c273a1989331765f73c4233b7"))
  	(122440, uint256("0xd72ea9245e69fa3d1acd0ba30ba99c31b813ee7f3cb70894b16cb9fb53647daf"))
  	(247852, uint256("0xed081efe1d76620413a4b31f0457d2a7755bf400cb54054ac777ec41216aae26"))
  	(351247, uint256("0x4d08ce2fb5de95d860d39a0ffdf397fc13285b8cf892fcfe27f5fcc3983b1ae0"))
  	(433942 , uint256("0xad866a67d0a5409518f48200ad49b968c8e36f1771ce3e5af546fce0dff4469f"))  //!< PIVX v4.1.1 enforced
  	(806565, uint256("0xe4f16d631bf85aac842d9a3cc2e8e098a537de9f616524ed71111192a07d6aa9"));
  
  static const Checkpoints::CCheckpointData data = {
      &mapCheckpoints,
      1605310362, // * UNIX timestamp of last checkpoint block
      1482659,    // * total number of transactions between genesis and last checkpoint
                  //   (the tx=... number in the UpdateTip debug.log lines)
      2000        // * estimated number of transactions per day after checkpoint
  };
  
  static Checkpoints::MapCheckpoints mapCheckpointsTestnet =
      boost::assign::map_list_of
      (0, uint256S("0x00"));
  static const Checkpoints::CCheckpointData dataTestnet = {
      &mapCheckpointsTestnet,
      1605265870,
      0,
      100};
  
  static Checkpoints::MapCheckpoints mapCheckpointsRegtest =
      boost::assign::map_list_of
  	(0, uint256S("0x00"));
  static const Checkpoints::CCheckpointData dataRegtest = {
      &mapCheckpointsRegtest,
      1605265870,
      0,
      100};
  
  class CMainParams : public CChainParams
  {
  public:
      CMainParams()
      {
          networkID = CBaseChainParams::MAIN;
          strNetworkID = "main";
  
          genesis = CreateGenesisBlock(1556165178, 2894113, 0x1e0ffff0, 1, 250 * COIN);
          consensus.hashGenesisBlock = genesis.GetHash();
          assert(consensus.hashGenesisBlock == uint256S("0x00000eef0583695d6da23a78bab1c39939bbb54cf9bd5f0d4881c8eef364cd26"));
  		//TODO: Set hashMerkleRoot. CLUE: Mining Testnet genesis block will give the hashMerkleRoot which will be the same as Mainnet.
          assert(genesis.hashMerkleRoot == uint256S("0x62179b80cc4da52f1ff4bad5d690836a3a663d14d5581a63822963bf1613041b"));
  
          consensus.fPowAllowMinDifficultyBlocks = false;
          consensus.powLimit   = ~UINT256_ZERO >> 20;   // Sapphire starting difficulty is 1 / 2^12
          consensus.posLimitV1 = ~UINT256_ZERO >> 24;
          consensus.posLimitV2 = ~UINT256_ZERO >> 20;
          consensus.nBudgetCycleBlocks = 43200;       // approx. 1 every 30 days
          consensus.nBudgetFeeConfirmations = 6;      // Number of confirmations for the finalization fee
          consensus.nCoinbaseMaturity = 100;
          consensus.nFutureTimeDriftPoW = 7200;
          consensus.nFutureTimeDriftPoS = 180;
          consensus.nMasternodeCountDrift = 20;       // num of MN we allow the see-saw payments to be off by
          consensus.nMaxMoneyOut = 3000000000 * COIN;
          consensus.nPoolMaxTransactions = 3;
          consensus.nProposalEstablishmentTime = 60 * 60 * 24;    // must be at least a day old to make it into a budget
          consensus.nStakeMinAge = 60 * 60;
          consensus.nStakeMinDepth = 60;
          consensus.nStakeMinDepthV2 = 600;
          consensus.nTargetTimespan = 1 * 60;
          consensus.nTargetTimespanV2 = consensus.nTargetTimespan;
          consensus.nTargetSpacing = 1 * 60;
          consensus.nTimeSlotLength = 15;
  
          // spork keys
  		//TODO: Create a new spork key pair for security.
          consensus.strSporkPubKey = "04d45416e4a64b1b051e2a2ebd80ced5efe148cf5fbcb70e56860957675a2da1a21fd522c42c1ed18a1ec42641589a09cf3f58678d213825dc21798183a005a984";
          consensus.strSporkPubKeyOld = "04d45416e4a64b1b051e2a2ebd80ced5efe148cf5fbcb70e56860957675a2da1a21fd522c42c1ed18a1ec42641589a09cf3f58678d213825dc21798183a005a984";
          consensus.nTime_EnforceNewSporkKey = 1556165178;    //!> August 26, 2019 11:00:00 PM GMT
          consensus.nTime_RejectOldSporkKey = 1556165178;     //!> September 26, 2019 11:00:00 PM GMT
  
          // height-based activations
          consensus.height_last_ZC_AccumCheckpoint    = disabled;
          consensus.height_last_ZC_WrappedSerials     = disabled; 
          consensus.height_start_InvalidUTXOsCheck    = disabled; 
          consensus.height_start_ZC_InvalidSerials    = disabled; 
          consensus.height_start_ZC_SerialRangeCheck  = disabled;
          consensus.height_ZC_RecalcAccumulators      = disabled;
  
          // validation by-pass
          // consensus.nPivxBadBlockTime = disabled; 
          // consensus.nPivxBadBlockBits = 0x00;    
  
          // Zerocoin-related params
          consensus.ZC_Modulus = "25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784"
                  "4069182906412495150821892985591491761845028084891200728449926873928072877767359714183472702618963750149718246911"
                  "6507761337985909570009733045974880842840179742910064245869181719511874612151517265463228221686998754918242243363"
                  "7259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133"
                  "8441436038339044149526344321901146575444541784240209246165157233507787077498171257724679629263863563732899121548"
                  "31438167899885040445364023527381951378636564391212010397122822120720357";
          consensus.ZC_MaxPublicSpendsPerTx = 637;    // Assume about 220 bytes each input
          consensus.ZC_MaxSpendsPerTx = 7;            // Assume about 20kb each input
          consensus.ZC_MinMintConfirmations = 20;
          consensus.ZC_MinMintFee = 1 * CENT;
          consensus.ZC_MinStakeDepth = 200;
          consensus.ZC_TimeStart = 1556165178;        // Thu Apr 25 04:06:18 AM +00 2019
  
          // Network upgrades
          consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight                  = Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
          consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight             = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_POS].nActivationHeight                   = 501;
          consensus.vUpgrades[Consensus::UPGRADE_POS_V2].nActivationHeight                = 905001;
          consensus.vUpgrades[Consensus::UPGRADE_ZC].nActivationHeight                    = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_ZC_V2].nActivationHeight                 = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_BIP65].nActivationHeight                 = 905101;
          consensus.vUpgrades[Consensus::UPGRADE_ZC_PUBLIC].nActivationHeight             = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_V3_4].nActivationHeight                  = 905201;
          consensus.vUpgrades[Consensus::UPGRADE_V4_0].nActivationHeight                  = 905301; 
          consensus.vUpgrades[Consensus::UPGRADE_V5_DUMMY].nActivationHeight              = 905401;
          consensus.vUpgrades[Consensus::UPGRADE_STAKE_MIN_DEPTH_V2].nActivationHeight    = 905501;
  
          consensus.vUpgrades[Consensus::UPGRADE_ZC].hashActivationBlock                  = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_ZC_V2].hashActivationBlock               = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_BIP65].hashActivationBlock               = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_ZC_PUBLIC].hashActivationBlock           = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_V3_4].hashActivationBlock                = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_V4_0].hashActivationBlock                = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_V5_DUMMY].hashActivationBlock            = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_STAKE_MIN_DEPTH_V2].hashActivationBlock  = uint256S("0x0");
  
          /**
           * The message start string is designed to be unlikely to occur in normal data.
           * The characters are rarely used upper ASCII, not valid as UTF-8, and produce
           * a large 4-byte int at any alignment.
           */
          pchMessageStart[0] = 0x13;
          pchMessageStart[1] = 0xb2;
          pchMessageStart[2] = 0x3e;
          pchMessageStart[3] = 0x58;
          nDefaultPort = 45328;
  
          // Note that of those with the service bits flag, most only support a subset of possible options
          vSeeds.emplace_back(CDNSSeedData("seed1.sappcoin.com", "seed1.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed2.sappcoin.com", "seed2.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed3.sappcoin.com", "seed3.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed4.sappcoin.com", "seed4.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed5.sappcoin.com", "seed5.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed6.sappcoin.com", "seed6.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed7.sappcoin.com", "seed7.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed8.sappcoin.com", "seed8.sappcoin.com", true));
  
          base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 63);
          base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 18);
  		//TODO: There is no STAKING_ADDRESS part in the old code. Keeping the SAPP value for now. Find a proper value and set it for SAPP!
          base58Prefixes[STAKING_ADDRESS] = std::vector<unsigned char>(1, 28);     // starting with 'C'
          base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, 25);
          base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x02)(0x2D)(0x25)(0x63).convert_to_container<std::vector<unsigned char> >();
          base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x02)(0x21)(0x31)(0x2B).convert_to_container<std::vector<unsigned char> >();
          // 	BIP44 coin type is from https://github.com/satoshilabs/slips/blob/master/slip-0044.md
          base58Prefixes[EXT_COIN_TYPE] = boost::assign::list_of(0x80)(0x00)(0x08)(0x45).convert_to_container<std::vector<unsigned char> >();
  
  		//TODO: By using fixseed creator python script, create fixed seeds in 'chainparamsseeds.h'
          vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
  
          // Sapling
  		//TODO: Set the parameters below for SAPP
          bech32HRPs[SAPLING_PAYMENT_ADDRESS]      = "ps";
          bech32HRPs[SAPLING_FULL_VIEWING_KEY]     = "pviews";
          bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "pivks";
          bech32HRPs[SAPLING_EXTENDED_SPEND_KEY]   = "p-secret-spending-key-main";
          // bech32HRPs[SAPLING_EXTENDED_FVK]         = "pxviews";
      }
  
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return data;
      }
  
  };
  static CMainParams mainParams;
  
  /**
   * Testnet (v3)
   */
  class CTestNetParams : public CMainParams
  {
  public:
      CTestNetParams()
      {
          networkID = CBaseChainParams::TESTNET;
          strNetworkID = "test";
  
          genesis = CreateGenesisBlock(1556165178, 2894113, 0x1e0ffff0, 1, 250 * COIN);
          consensus.hashGenesisBlock = genesis.GetHash();
          assert(consensus.hashGenesisBlock == uint256S("0x00000eef0583695d6da23a78bab1c39939bbb54cf9bd5f0d4881c8eef364cd26"));
          assert(genesis.hashMerkleRoot == uint256S("0x62179b80cc4da52f1ff4bad5d690836a3a663d14d5581a63822963bf1613041b"));
  
          consensus.fPowAllowMinDifficultyBlocks = false;
          consensus.powLimit   = ~UINT256_ZERO >> 20;   // Sapphire starting difficulty is 1 / 2^12
          consensus.posLimitV1 = ~UINT256_ZERO >> 24;
          consensus.posLimitV2 = ~UINT256_ZERO >> 20;
          consensus.nBudgetCycleBlocks = 43200;       // approx. 1 every 30 days
          consensus.nBudgetFeeConfirmations = 6;      // Number of confirmations for the finalization fee
          consensus.nCoinbaseMaturity = 100;
          consensus.nFutureTimeDriftPoW = 7200;
          consensus.nFutureTimeDriftPoS = 180;
          consensus.nMasternodeCountDrift = 20;       // num of MN we allow the see-saw payments to be off by
          consensus.nMaxMoneyOut = 3000000000 * COIN;
          consensus.nPoolMaxTransactions = 3;
          consensus.nProposalEstablishmentTime = 60 * 60 * 24;    // must be at least a day old to make it into a budget
          consensus.nStakeMinAge = 60 * 60;
          consensus.nStakeMinDepth = 60;
          consensus.nStakeMinDepthV2 = 600;
          consensus.nTargetTimespan = 1 * 60;
          consensus.nTargetTimespanV2 = consensus.nTargetTimespan;
          consensus.nTargetSpacing = 1 * 60;
          consensus.nTimeSlotLength = 15;
  
          // spork keys
  		//TODO: Create a new spork key pair for security.
          consensus.strSporkPubKey = "04d45416e4a64b1b051e2a2ebd80ced5efe148cf5fbcb70e56860957675a2da1a21fd522c42c1ed18a1ec42641589a09cf3f58678d213825dc21798183a005a984";
          consensus.strSporkPubKeyOld = "04d45416e4a64b1b051e2a2ebd80ced5efe148cf5fbcb70e56860957675a2da1a21fd522c42c1ed18a1ec42641589a09cf3f58678d213825dc21798183a005a984";
          consensus.nTime_EnforceNewSporkKey = 1556165178;    //!> August 26, 2019 11:00:00 PM GMT
          consensus.nTime_RejectOldSporkKey = 1556165178;     //!> September 26, 2019 11:00:00 PM GMT
  
          // height based activations
          consensus.height_last_ZC_AccumCheckpoint    = disabled;
          consensus.height_last_ZC_WrappedSerials     = disabled; 
          consensus.height_start_InvalidUTXOsCheck    = disabled; 
          consensus.height_start_ZC_InvalidSerials    = disabled; 
          consensus.height_start_ZC_SerialRangeCheck  = disabled;
          consensus.height_ZC_RecalcAccumulators      = disabled;
  
          // validation by-pass
          // consensus.nPivxBadBlockTime = 999999999; // Never
          // consensus.nPivxBadBlockBits = 0x00; // None
  
          // Zerocoin-related params
          consensus.ZC_Modulus = "25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784"
                  "4069182906412495150821892985591491761845028084891200728449926873928072877767359714183472702618963750149718246911"
                  "6507761337985909570009733045974880842840179742910064245869181719511874612151517265463228221686998754918242243363"
                  "7259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133"
                  "8441436038339044149526344321901146575444541784240209246165157233507787077498171257724679629263863563732899121548"
                  "31438167899885040445364023527381951378636564391212010397122822120720357";
          consensus.ZC_MaxPublicSpendsPerTx = 637;    // Assume about 220 bytes each input
          consensus.ZC_MaxSpendsPerTx = 7;            // Assume about 20kb each input
          consensus.ZC_MinMintConfirmations = 20;
          consensus.ZC_MinMintFee = 1 * CENT;
          consensus.ZC_MinStakeDepth = 200;
          consensus.ZC_TimeStart = 1556165178;        // Thu Apr 25 04:06:18 AM +00 2019
  
          // Network upgrades
          consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight                  = Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
          consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight             = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_POS].nActivationHeight                   = 501;
          consensus.vUpgrades[Consensus::UPGRADE_POS_V2].nActivationHeight                = 878611;
          consensus.vUpgrades[Consensus::UPGRADE_ZC].nActivationHeight                    = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_ZC_V2].nActivationHeight                 = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_BIP65].nActivationHeight                 = 878631;
          consensus.vUpgrades[Consensus::UPGRADE_ZC_PUBLIC].nActivationHeight             = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_V3_4].nActivationHeight                  = 878651;
          consensus.vUpgrades[Consensus::UPGRADE_V4_0].nActivationHeight                  = 878671;
          consensus.vUpgrades[Consensus::UPGRADE_V5_DUMMY].nActivationHeight              = 878691;
          consensus.vUpgrades[Consensus::UPGRADE_STAKE_MIN_DEPTH_V2].nActivationHeight    = 1000001;
  
          consensus.vUpgrades[Consensus::UPGRADE_ZC].hashActivationBlock                  = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_ZC_V2].hashActivationBlock               = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_BIP65].hashActivationBlock               = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_ZC_PUBLIC].hashActivationBlock           = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_V3_4].hashActivationBlock                = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_V4_0].hashActivationBlock                = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_V5_DUMMY].hashActivationBlock            = uint256S("0x0");
          consensus.vUpgrades[Consensus::UPGRADE_STAKE_MIN_DEPTH_V2].hashActivationBlock  = uint256S("0x0");
  
          /**
           * The message start string is designed to be unlikely to occur in normal data.
           * The characters are rarely used upper ASCII, not valid as UTF-8, and produce
           * a large 4-byte int at any alignment.
           */
  
          pchMessageStart[0] = 0x46;
          pchMessageStart[1] = 0x32;
          pchMessageStart[2] = 0x64;
          pchMessageStart[3] = 0x23;
          nDefaultPort = 46328;
  
          vFixedSeeds.clear();
          vSeeds.clear();
          // nodes with support for servicebits filtering should be at the top
          vSeeds.emplace_back(CDNSSeedData("kyan-testnet.572133.club", "kyan-testnet.572133.club", true));
          vSeeds.emplace_back(CDNSSeedData("kyan-testnet2.572133.club", "kyan-testnet2.572133.club", true));
          vSeeds.emplace_back(CDNSSeedData("seed1.sappcoin.com", "seed1.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed2.sappcoin.com", "seed2.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed3.sappcoin.com", "seed3.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed4.sappcoin.com", "seed4.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed5.sappcoin.com", "seed5.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed6.sappcoin.com", "seed6.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed7.sappcoin.com", "seed7.sappcoin.com", true));
          vSeeds.emplace_back(CDNSSeedData("seed8.sappcoin.com", "seed8.sappcoin.com", true));
  
          base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 139); // Testnet sapphire addresses start with 'x' or 'y'
          base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 19);  // Testnet sapphire script addresses start with '8' or '9'
          base58Prefixes[STAKING_ADDRESS] = std::vector<unsigned char>(1, 73);     // starting with 'W'
          base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, 239);     // Testnet private keys start with '9' or 'c' (Bitcoin defaults)
          // Testnet sapphire BIP32 pubkeys start with 'tpub' (Bitcoin defaults)
          base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >(); // starting with 'tpub'
          // Testnet sapphire BIP32 prvkeys start with 'tprv' (Bitcoin defaults)
          base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >(); // starting with 'tprv'
          // Testnet sapphire BIP44 coin type is '1' (All coin's testnet default)
          base58Prefixes[EXT_COIN_TYPE] = boost::assign::list_of(0x80)(0x00)(0x00)(0x01).convert_to_container<std::vector<unsigned char> >();
  
          vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
  
          // Sapling
          bech32HRPs[SAPLING_PAYMENT_ADDRESS]      = "ptestsapling";
          bech32HRPs[SAPLING_FULL_VIEWING_KEY]     = "pviewtestsapling";
          bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "pivktestsapling";
          bech32HRPs[SAPLING_EXTENDED_SPEND_KEY]   = "p-secret-spending-key-test";
          // bech32HRPs[SAPLING_EXTENDED_FVK]         = "pxviewtestsapling";
      }
  
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return dataTestnet;
      }
  };
  static CTestNetParams testNetParams;
  
  /**
   * Regression test
   */
  class CRegTestParams : public CTestNetParams
  {
  public:
      CRegTestParams()
      {
          networkID = CBaseChainParams::REGTEST;
          strNetworkID = "regtest";
  
          genesis = CreateGenesisBlock(1605314006, 18440, 0x1effffff, 1, 250 * COIN);
          consensus.hashGenesisBlock = genesis.GetHash();
          assert(consensus.hashGenesisBlock == uint256S("0x0000529df5fae941569b6466128042f5f036a8d1d380dd484a06e8a12fb275a3"));
          assert(genesis.hashMerkleRoot == uint256S("0x62179b80cc4da52f1ff4bad5d690836a3a663d14d5581a63822963bf1613041b"));
  
          consensus.fPowAllowMinDifficultyBlocks = true;
          consensus.powLimit   = ~UINT256_ZERO >> 20;   // Sapphire starting difficulty is 1 / 2^12
          consensus.posLimitV1 = ~UINT256_ZERO >> 24;
          consensus.posLimitV2 = ~UINT256_ZERO >> 20;
          consensus.nBudgetCycleBlocks = 144;         // approx 10 cycles per day
          consensus.nBudgetFeeConfirmations = 3;      // (only 8-blocks window for finalization on regtest)
          consensus.nCoinbaseMaturity = 100;
          consensus.nFutureTimeDriftPoW = 7200;
          consensus.nFutureTimeDriftPoS = 180;
          consensus.nMasternodeCountDrift = 4;        // num of MN we allow the see-saw payments to be off by
          consensus.nMaxMoneyOut = 43199500 * COIN;
          consensus.nPoolMaxTransactions = 2;
          consensus.nProposalEstablishmentTime = 60 * 5;  // at least 5 min old to make it into a budget
          consensus.nStakeMinAge = 0;
          consensus.nStakeMinDepth = 2;
          consensus.nTargetTimespan = 40 * 60;
          consensus.nTargetTimespanV2 = 30 * 60;
          consensus.nTargetSpacing = 1 * 60;
          consensus.nTimeSlotLength = 15;
  
          /* Spork Key for RegTest:
          WIF private key: 932HEevBSujW2ud7RfB1YF91AFygbBRQj3de3LyaCRqNzKKgWXi
          private key hex: bd4960dcbd9e7f2223f24e7164ecb6f1fe96fc3a416f5d3a830ba5720c84b8ca
          Address: yCvUVd72w7xpimf981m114FSFbmAmne7j9
          */
          consensus.strSporkPubKey = "043969b1b0e6f327de37f297a015d37e2235eaaeeb3933deecd8162c075cee0207b13537618bde640879606001a8136091c62ec272dd0133424a178704e6e75bb7";
          consensus.strSporkPubKeyOld = "";
          consensus.nTime_EnforceNewSporkKey = 0;
          consensus.nTime_RejectOldSporkKey = 0;
  
          // height based activations
          consensus.height_last_ZC_AccumCheckpoint = 310;     // no checkpoints on regtest
          consensus.height_last_ZC_WrappedSerials = -1;
          consensus.height_start_InvalidUTXOsCheck = 999999999;
          consensus.height_start_ZC_InvalidSerials = 999999999;
          consensus.height_start_ZC_SerialRangeCheck = 300;
          consensus.height_ZC_RecalcAccumulators = 999999999;
  
          // Zerocoin-related params
          consensus.ZC_Modulus = "25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784"
                  "4069182906412495150821892985591491761845028084891200728449926873928072877767359714183472702618963750149718246911"
                  "6507761337985909570009733045974880842840179742910064245869181719511874612151517265463228221686998754918242243363"
                  "7259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133"
                  "8441436038339044149526344321901146575444541784240209246165157233507787077498171257724679629263863563732899121548"
                  "31438167899885040445364023527381951378636564391212010397122822120720357";
          consensus.ZC_MaxPublicSpendsPerTx = 637;    // Assume about 220 bytes each input
          consensus.ZC_MaxSpendsPerTx = 7;            // Assume about 20kb each input
          consensus.ZC_MinMintConfirmations = 10;
          consensus.ZC_MinMintFee = 1 * CENT;
          consensus.ZC_MinStakeDepth = 10;
          consensus.ZC_TimeStart = 0;                 // not implemented on regtest
  
          // Network upgrades
          consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight =
                  Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
          consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
                  Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
          consensus.vUpgrades[Consensus::UPGRADE_POS].nActivationHeight           = 251;
          consensus.vUpgrades[Consensus::UPGRADE_POS_V2].nActivationHeight        = 251;
          consensus.vUpgrades[Consensus::UPGRADE_ZC].nActivationHeight            = 300;
          consensus.vUpgrades[Consensus::UPGRADE_ZC_V2].nActivationHeight         = 300;
          consensus.vUpgrades[Consensus::UPGRADE_BIP65].nActivationHeight         =
                  Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
          consensus.vUpgrades[Consensus::UPGRADE_ZC_PUBLIC].nActivationHeight     = 400;
          consensus.vUpgrades[Consensus::UPGRADE_V3_4].nActivationHeight          = 251;
          consensus.vUpgrades[Consensus::UPGRADE_V4_0].nActivationHeight          =
                  Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
          consensus.vUpgrades[Consensus::UPGRADE_V5_DUMMY].nActivationHeight       = 300;
          consensus.vUpgrades[Consensus::UPGRADE_STAKE_MIN_DEPTH_V2].nActivationHeight    = 1001;
  
          /**
           * The message start string is designed to be unlikely to occur in normal data.
           * The characters are rarely used upper ASCII, not valid as UTF-8, and produce
           * a large 4-byte int at any alignment.
           */
  
          pchMessageStart[0] = 0xa7;
          pchMessageStart[1] = 0xc3;
          pchMessageStart[2] = 0x72;
          pchMessageStart[3] = 0xa8;
          nDefaultPort = 47328;
  
          vFixedSeeds.clear(); //! Testnet mode doesn't have any fixed seeds.
          vSeeds.clear();      //! Testnet mode doesn't have any DNS seeds.
      }
  
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return dataRegtest;
      }
  
      void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight)
      {
          assert(idx > Consensus::BASE_NETWORK && idx < Consensus::MAX_NETWORK_UPGRADES);
          consensus.vUpgrades[idx].nActivationHeight = nActivationHeight;
      }
  };
  static CRegTestParams regTestParams;
  
  static CChainParams* pCurrentParams = 0;
  
  const CChainParams& Params()
  {
      assert(pCurrentParams);
      return *pCurrentParams;
  }
  
  CChainParams& Params(CBaseChainParams::Network network)
  {
      switch (network) {
      case CBaseChainParams::MAIN:
          return mainParams;
      case CBaseChainParams::TESTNET:
          return testNetParams;
      case CBaseChainParams::REGTEST:
          return regTestParams;
      default:
          assert(false && "Unimplemented network");
          return mainParams;
      }
  }
  
  void SelectParams(CBaseChainParams::Network network)
  {
      SelectBaseParams(network);
      pCurrentParams = &Params(network);
  }
  
  bool SelectParamsFromCommandLine()
  {
      CBaseChainParams::Network network = NetworkIdFromCommandLine();
      if (network == CBaseChainParams::MAX_NETWORK_TYPES)
          return false;
  
      SelectParams(network);
      return true;
  }
  
  void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight)
  {
      regTestParams.UpdateNetworkUpgradeParameters(idx, nActivationHeight);
  }
  