text: // Copyright (c) 2010 Satoshi Nakamoto
  // Copyright (c) 2009-2014 The Bitcoin developers
  // Copyright (c) 2014-2015 The Dash developers
  // Copyright (c) 2015-2019 The PIVX developers
  // Copyright (c) 2018-2020 The Kabberry developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "libzerocoin/Params.h"
  #include "chainparams.h"
  #include "consensus/merkle.h"
  #include "random.h"
  #include "util.h"
  #include "utilstrencodings.h"
  
  #include <assert.h>
  
  #include <boost/assign/list_of.hpp>
  #include <limits>
  
  
  struct SeedSpec6 {
      uint8_t addr[16];
      uint16_t port;
  };
  
  #include "chainparamsseeds.h"
  
  /**
   * Main network
   */
  
  //! Convert the pnSeeds6 array into usable address objects.
  static void convertSeed6(std::vector<CAddress>& vSeedsOut, const SeedSpec6* data, unsigned int count)
  {
      // It'll only connect to one or two seed nodes because once it connects,
      // it'll get a pile of addresses with newer timestamps.
      // Seed nodes are given a random 'last seen time' of between one and two
      // weeks ago.
      const int64_t nOneWeek = 7 * 24 * 60 * 60;
      for (unsigned int i = 0; i < count; i++) {
          struct in6_addr ip;
          memcpy(&ip, data[i].addr, sizeof(ip));
          CAddress addr(CService(ip, data[i].port));
          addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
          vSeedsOut.push_back(addr);
      }
  }
  
  //   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("00000fe79c10addf7e023042d3ef98ce24d36262ae7c05c46c085c1ee14434d1"))
      (50, uint256("000000588e9fc1ef432d1a656958be1dd44cd2b9e2322a80f2e35817283d575e"))
      (2000, uint256("0000002802c61a1ce8021322e5ee67465ad118c7db76ffb244bb096be720a439")) 
      (5000, uint256("000000313c1d849f6c6ae1cad1b10e5a2e56da6593c1ba658b7152b644b4cf91"))
      (10000, uint256("000000000003ca43cf16ab5d4ebdaccc6f0ee6108bfaa0b149e070208e1263ac"))
      (167000, uint256("cdeba371d09cbad641d135eb180d98de22c2aa5dda25625e791359daa60fc2f6"))
      (560000, uint256("e2ca342dbe73671b62f8c4b48a0bafaebae28c7134c163d956ede7fd5c97be08"))
      // (863795, uint256("2ad866818c4866e0d555181daccc628056216c0db431f88a825e84ed4f469067"))
      // (863805, uint256("a755bd9a22b63c70d3db474f4b2b61a1f86c835b290a081bb3ec1ba2103eb4cb"))
      // (867733, uint256("03b26296bf693de5782c76843d2fb649cb66d4b05550c6a79c047ff7e1c3ae15"))
      // (879650, uint256("227e1d2b738b6cd83c46d1d64617934ec899d77cee34336a56e61b71acd10bb2"))
      // (895400, uint256("7796a0274a608fac12d400198174e50beda992c1d522e52e5b95b884bc1beac6"))//block that serial# range is enforced
      // (895991, uint256("d53013ed7ea5c325b9696c95e07667d6858f8ff7ee13fecfa90827bf3c9ae316"))//network split here
      // (908000, uint256("202708f8c289b676fceb832a079ff6b308a28608339acbf7584de533619d014d"))
      // (1142400, uint256("98aff9d605bf123247f98b1e3a02567eb5799d208d78ec30fb89737b1c1f79c5"))
      ;
  static const Checkpoints::CCheckpointData data = {
      &mapCheckpoints,
      1580092363, // * UNIX timestamp of last checkpoint block
      1200000,    // * total number of transactions between genesis and last checkpoint
                  //   (the tx=... number in the SetBestChain debug.log lines)
      2000        // * estimated number of transactions per day after checkpoint
  };
  
  static Checkpoints::MapCheckpoints mapCheckpointsTestnet =
      boost::assign::map_list_of(0, uint256("0x001"));
  static const Checkpoints::CCheckpointData dataTestnet = {
      &mapCheckpointsTestnet,
      1740710,
      0,
      250};
  
  static Checkpoints::MapCheckpoints mapCheckpointsRegtest =
      boost::assign::map_list_of(0, uint256("0x001"));
  static const Checkpoints::CCheckpointData dataRegtest = {
      &mapCheckpointsRegtest,
      1434124731,
      0,
      100};
  
  libzerocoin::ZerocoinParams* CChainParams::Zerocoin_Params(bool useModulusV1) const
  {
      assert(this);
      static CBigNum bnHexModulus = 0;
      if (!bnHexModulus)
          bnHexModulus.SetHex(zerocoinModulus);
      static libzerocoin::ZerocoinParams ZCParamsHex = libzerocoin::ZerocoinParams(bnHexModulus);
      static CBigNum bnDecModulus = 0;
      if (!bnDecModulus)
          bnDecModulus.SetDec(zerocoinModulus);
      static libzerocoin::ZerocoinParams ZCParamsDec = libzerocoin::ZerocoinParams(bnDecModulus);
  
      if (useModulusV1)
          return &ZCParamsHex;
  
      return &ZCParamsDec;
  }
  
  bool CChainParams::HasStakeMinAgeOrDepth(const int contextHeight, const uint32_t contextTime,
          const int utxoFromBlockHeight, const uint32_t utxoFromBlockTime) const
  {
      // before stake modifier V2, the age required was 60 * 60 (1 hour). Not required for regtest
      if (!IsStakeModifierV2(contextHeight))
          return NetworkID() == CBaseChainParams::REGTEST || (utxoFromBlockTime + nStakeMinAge <= contextTime);
  
      // after stake modifier V2, we require the utxo to be nStakeMinDepth deep in the chain
      return (contextHeight - utxoFromBlockHeight >= nStakeMinDepth);
  }
  
  int CChainParams::FutureBlockTimeDrift(const int nHeight) const
  {
      if (IsTimeProtocolV2(nHeight))
          // PoS (TimeV2): 14 seconds
          return TimeSlotLength() - 1;
  
      // PoS (TimeV1): 3 minutes
      // PoW: 2 hours
      return (nHeight > LAST_POW_BLOCK()) ? nFutureTimeDriftPoS : nFutureTimeDriftPoW;
  }
  
  bool CChainParams::IsValidBlockTimeStamp(const int64_t nTime, const int nHeight) const
  {
      // Before time protocol V2, blocks can have arbitrary timestamps
      if (!IsTimeProtocolV2(nHeight))
          return true;
  
      // Time protocol v2 requires time in slots
      return (nTime % TimeSlotLength()) == 0;
  }
  
  class CMainParams : public CChainParams
  {
  public:
      CMainParams()
      {
          networkID = CBaseChainParams::MAIN;
          strNetworkID = "main";
          /**
           * 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] = 0x90;
          pchMessageStart[1] = 0xc4;
          pchMessageStart[2] = 0xfd;
          pchMessageStart[3] = 0xe9;
          vAlertPubKey = ParseHex("04c2c3cca22211d722d46e91ec78de67e13ea86b03fffab53609b90ed641a0770772d1780e59b65b9f22de7ac704b3dd31f7182c1ddb537e4024af2676155814e1");
          nDefaultPort = 34124;
          bnProofOfWorkLimit = ~uint256(0) >> 20; // Kabberry starting difficulty is 1 / 2^12
          bnProofOfStakeLimit = ~uint256(0) >> 24;
          bnProofOfStakeLimit_V2 = ~uint256(0) >> 20; // 60/4 = 15 ==> use 2**4 higher limit
          nSubsidyHalvingInterval = 259000;
          nMaxReorganizationDepth = 100;
          nEnforceBlockUpgradeMajority = 5040; // 75%
          nRejectBlockOutdatedMajority = 6384; // 95%
          nToCheckBlockUpgradeMajority = 6720; // Approximate expected amount of blocks in 7 days (960*7)
          nMinerThreads = 0;
          nTargetSpacing = 1.5 * 60;  // Kabberry: 1.5 minute
          nTargetTimespan = 40 * 60;                      // 40 minutes
          nTimeSlotLength = 15;                           // 15 seconds
          nTargetTimespan_V2 = 2 * nTimeSlotLength * 60;  // 30 minutes
          nMaturity = 25;
          nStakeMinAge = 60 * 60;                         // 1 hour
          nStakeMinDepth = 600;
          nFutureTimeDriftPoW = 7200;
          nFutureTimeDriftPoS = 180;
          nMasternodeCountDrift = 20;
          nMaxMoneyOut = 60000000 * COIN;
          nMinColdStakingAmount = 1 * COIN;
  
          /** Height or Time Based Activations **/
          nLastPOWBlock = 13440;
          nKabberryBadBlockTime = 1471401614; // Skip nBit validation of Block 259201 per PR #915
          nKabberryBadBlocknBits = 0x1c056dac; // Skip nBit validation of Block 259201 per PR #915
          nModifierUpdateBlock = 570000;
          nZerocoinStartHeight = 260000;    
          nZerocoinStartTime = 1567339200; // July 1, 2019 12:00:00 PM
          nBlockEnforceSerialRange = 1999999999; //Enforce serial range starting this block
          nBlockRecalculateAccumulators = 1999999999; //Trigger a recalculation of accumulators
          nBlockFirstFraudulent = 1999999999; //First block that bad serials emerged
          nBlockLastGoodCheckpoint = 1999999999; //Last valid accumulator checkpoint
          nBlockEnforceInvalidUTXO = 1999999999; //Start enforcing the invalid UTXO's
          nInvalidAmountFiltered = 1*COIN; //Amount of invalid coins filtered through exchanges, that should be considered valid
          nBlockZerocoinV2 = 565000; //!> The block that zerocoin v2 becomes active - roughly Tuesday, May 8, 2018 4:00:00 AM GMT
          nBlockDoubleAccumulated = 1999999999;
          nEnforceNewSporkKey = 1575115200; //!> Sporks signed after (GMT): Tuesday, May 1, 2018 7:00:00 AM GMT must use the new spork key
          nRejectOldSporkKey = 1577707200; //!> Fully reject old spork key after (GMT): Friday, June 1, 2018 12:00:00 AM
          nBlockStakeModifierlV2 = 572000;
          nBIP65ActivationHeight = 573000;
          // Activation height for TimeProtocolV2, Blocks V7 and newMessageSignatures
          nBlockTimeProtocolV2 = 575000;
  
          // Public coin spend enforcement
          nPublicZCSpends = 1880000;
  
          // New P2P messages signatures
          nBlockEnforceNewMessageSignatures = nBlockTimeProtocolV2;
  
          // Blocks v7
          nBlockLastAccumulatorCheckpoint = 1686240;
          nBlockV7StartHeight = nBlockTimeProtocolV2;
  
          // Fake Serial Attack
          nFakeSerialBlockheightEnd = 1999999999;
          nSupplyBeforeFakeSerial = 1999999999 * COIN;   // zerocoin supply at block nFakeSerialBlockheightEnd
  
          /**
           * 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
           */
          const char* pszTimestamp = "PrimeStone Genesis Block: 2018/12/28 12:30:00";
          CMutableTransaction txNew;
          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 = 0 * COIN;
          txNew.vout[0].scriptPubKey = CScript() << ParseHex("041f5ae176fb57fb89375f57e8e3a701a4f516235327580da9a7c0130d2a7b8c5accc644cd2e9bf3104cfcdb861ce2e336ff31b81473620568e886c7c437ddf954") << OP_CHECKSIG;
          genesis.vtx.push_back(txNew);
          genesis.hashPrevBlock = 0;
          genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
          genesis.nVersion = 1;
          genesis.nTime = 1545970000;
          genesis.nBits = 0x1e0ffff0;
          genesis.nNonce = 1160284;
  
          hashGenesisBlock = genesis.GetHash();
          assert(hashGenesisBlock == uint256("0x00000fe79c10addf7e023042d3ef98ce24d36262ae7c05c46c085c1ee14434d1"));
          assert(genesis.hashMerkleRoot == uint256("0x50c1eda15daf3fb9c6f0a08ab64906d302abc01d57300cf8194c4b2a544bec12"));
  
          // vSeeds.push_back(CDNSSeedData("fuzzbawls.pw", "kabberry.seed.fuzzbawls.pw"));     // Primary DNS Seeder from Fuzzbawls
          // vSeeds.push_back(CDNSSeedData("fuzzbawls.pw", "kabberry.seed2.fuzzbawls.pw"));    // Secondary DNS Seeder from Fuzzbawls
          // vSeeds.push_back(CDNSSeedData("coin-server.com", "coin-server.com"));         // Single node address
          // vSeeds.push_back(CDNSSeedData("s3v3nh4cks.ddns.net", "s3v3nh4cks.ddns.net")); // Single node address
          // vSeeds.push_back(CDNSSeedData("178.254.23.111", "178.254.23.111"));           // Single node address
  
          vSeeds.push_back(CDNSSeedData("77.55.216.249", "77.55.216.249"));
          
          base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 56);     //starts with 'P'
          base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 118);    //starts with 'p'
          base58Prefixes[STAKING_ADDRESS] = std::vector<unsigned char>(1, 63);     // starting with 'S'
          base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, 212);
          base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x02)(0x2D)(0x25)(0x33).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)(0x00)(0x77).convert_to_container<std::vector<unsigned char> >();
  
          convertSeed6(vFixedSeeds, pnSeed6_main, ARRAYLEN(pnSeed6_main));
  
          fMiningRequiresPeers = true;
          fAllowMinDifficultyBlocks = false;
          fDefaultConsistencyChecks = false;
          fRequireStandard = true;
          fMineBlocksOnDemand = false;
          fSkipProofOfWorkCheck = false;
          fTestnetToBeDeprecatedFieldRPC = false;
          fHeadersFirstSyncingActive = false;
  
          nPoolMaxTransactions = 3;
          nBudgetCycleBlocks = 28800; //!< Amount of blocks in a months period of time (using 1.5 minutes per) = (40*24*30)
          strSporkPubKey = "041c900e457f6a33af917f5b4d4a810a259439252059d26dca8fac84137841223c78f8fa7e4259ec35ee641f7bfe0a440736f4730d63a4d26077868328de16b63c";
          strSporkPubKeyOld = "04e2be89e67bde8af162843518cecbb6ad9b58ec99021221d61df591f7ea531e2086d5202d39c0b88198366e7cbe80b185b6409c7410aa6827ad4429f5ce1005d0";
          strObfuscationPoolDummyAddress = "D87q2gC9j6nNrnzCsg4aY6bHMLsT9nUhEw";
          nStartMasternodePayments = 1403728576; //Wed, 25 Jun 2014 20:36:16 GMT
  
          /** Zerocoin */
          zerocoinModulus = "25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784"
              "4069182906412495150821892985591491761845028084891200728449926873928072877767359714183472702618963750149718246911"
              "6507761337985909570009733045974880842840179742910064245869181719511874612151517265463228221686998754918242243363"
              "7259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133"
              "8441436038339044149526344321901146575444541784240209246165157233507787077498171257724679629263863563732899121548"
              "31438167899885040445364023527381951378636564391212010397122822120720357";
          nMaxZerocoinSpendsPerTransaction = 7; // Assume about 20kb each
          nMaxZerocoinPublicSpendsPerTransaction = 637; // Assume about 220 bytes each input
          nMinZerocoinMintFee = 1 * CENT; //high fee required for zerocoin mints
          nMintRequiredConfirmations = 20; //the maximum amount of confirmations until accumulated in 19
          nRequiredAccumulation = 1;
          nDefaultSecurityLevel = 100; //full security level for accumulators
          nZerocoinHeaderVersion = 4; //Block headers must be this version once zerocoin is active
          nZerocoinRequiredStakeDepth = 200; //The required confirmations for a zPSC to be stakable
  
          nBudget_Fee_Confirmations = 6; // Number of confirmations for the finalization fee
          nProposalEstablishmentTime = 60 * 60 * 24; // Proposals must be at least a day old to make it into a budget
      }
  
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return data;
      }
  
  };
  static CMainParams mainParams;
  
  /**
   * Testnet (v3)
   */
  class CTestNetParams : public CMainParams
  {
  public:
      CTestNetParams()
      {
          networkID = CBaseChainParams::TESTNET;
          strNetworkID = "test";
          pchMessageStart[0] = 0x45;
          pchMessageStart[1] = 0x76;
          pchMessageStart[2] = 0x65;
          pchMessageStart[3] = 0xba;
          vAlertPubKey = ParseHex("000010e83b2703ccf322f7dbd62dd5855ac7c10bd055814ce121ba32607d573b8810c02c0582aed05b4deb9c4b77b26d92428c61256cd42774babea0a073b2ed0c9");
          nDefaultPort = 41994;
          nEnforceBlockUpgradeMajority = 4320; // 75%
          nRejectBlockOutdatedMajority = 5472; // 95%
          nToCheckBlockUpgradeMajority = 5760; // 4 days
          nMinerThreads = 0;
          nLastPOWBlock = 200;
          nKabberryBadBlockTime = 1489001494; // Skip nBit validation of Block 259201 per PR #915
          nKabberryBadBlocknBits = 0x1e0a20bd; // Skip nBit validation of Block 201 per PR #915
          nMaturity = 15;
          nStakeMinDepth = 100;
          nMasternodeCountDrift = 4;
          nModifierUpdateBlock = 51197; //approx Mon, 17 Apr 2017 04:00:00 GMT
          nMaxMoneyOut = 43199500 * COIN;
          nZerocoinStartHeight = 201576;
          nZerocoinStartTime = 1501776000;
          nBlockEnforceSerialRange = 1; //Enforce serial range starting this block
          nBlockRecalculateAccumulators = 9908000; //Trigger a recalculation of accumulators
          nBlockFirstFraudulent = 9891737; //First block that bad serials emerged
          nBlockLastGoodCheckpoint = 9891730; //Last valid accumulator checkpoint
          nBlockEnforceInvalidUTXO = 9902850; //Start enforcing the invalid UTXO's
          nInvalidAmountFiltered = 0; //Amount of invalid coins filtered through exchanges, that should be considered valid
          nBlockZerocoinV2 = 444020; //!> The block that zerocoin v2 becomes active
          nEnforceNewSporkKey = 1521604800; //!> Sporks signed after Wednesday, March 21, 2018 4:00:00 AM GMT must use the new spork key
          nRejectOldSporkKey = 1522454400; //!> Reject old spork key after Saturday, March 31, 2018 12:00:00 AM GMT
          nBlockStakeModifierlV2 = 1214000;
          nBIP65ActivationHeight = 851019;
          // Activation height for TimeProtocolV2, Blocks V7 and newMessageSignatures
          nBlockTimeProtocolV2 = 1347000;
  
          // Public coin spend enforcement
          nPublicZCSpends = 1106100;
  
          // New P2P messages signatures
          nBlockEnforceNewMessageSignatures = nBlockTimeProtocolV2;
  
          // Blocks v7
          nBlockLastAccumulatorCheckpoint = nPublicZCSpends - 10;
          nBlockV7StartHeight = nBlockTimeProtocolV2;
  
          // Fake Serial Attack
          nFakeSerialBlockheightEnd = -1;
          nSupplyBeforeFakeSerial = 0;
  
          //! Modify the testnet genesis block so the timestamp is valid for a later start.
          genesis.nTime = 1536062401;
          genesis.nNonce = 110098;
  
          hashGenesisBlock = genesis.GetHash();
          //assert(hashGenesisBlock == uint256("0x00000bd1c98634283145a6b0c450a1d0f815534d76eb6b5fb09bf84d44ad347a"));
  
          vFixedSeeds.clear();
          vSeeds.clear();
          vSeeds.push_back(CDNSSeedData("fuzzbawls.pw", "kabberry-testnet.seed.fuzzbawls.pw"));
          vSeeds.push_back(CDNSSeedData("fuzzbawls.pw", "kabberry-testnet.seed2.fuzzbawls.pw"));
          vSeeds.push_back(CDNSSeedData("s3v3nh4cks.ddns.net", "s3v3nh4cks.ddns.net"));
          vSeeds.push_back(CDNSSeedData("88.198.192.110", "88.198.192.110"));
  
          base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 90); // Testnet kabberry addresses start with 'x' or 'y'
          base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 199);  // Testnet kabberry 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, 247);     // Testnet private keys start with '9' or 'c' (Bitcoin defaults)
          // Testnet kabberry BIP32 pubkeys start with 'DRKV'
          base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x3a)(0x80)(0x61)(0xa0).convert_to_container<std::vector<unsigned char> >();
          // Testnet kabberry BIP32 prvkeys start with 'DRKP'
          base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x3a)(0x80)(0x58)(0x37).convert_to_container<std::vector<unsigned char> >();
          // Testnet kabberry 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> >();
  
          convertSeed6(vFixedSeeds, pnSeed6_test, ARRAYLEN(pnSeed6_test));
  
          fMiningRequiresPeers = false;
          fAllowMinDifficultyBlocks = true;
          fDefaultConsistencyChecks = false;
          fRequireStandard = true;
          fMineBlocksOnDemand = false;
          fTestnetToBeDeprecatedFieldRPC = true;
  
          nPoolMaxTransactions = 2;
          nBudgetCycleBlocks = 144; //!< Ten cycles per day on testnet
          strSporkPubKey = "04A8B319388C0F8588D238B9941DC26B26D3F9465266B368A051C5C100F79306A557780101FE2192FE170D7E6DEFDCBEE4C8D533396389C0DAFFDBC842B002243C";
          strSporkPubKeyOld = "04348C2F50F90267E64FACC65BFDC9D0EB147D090872FB97ABAE92E9A36E6CA60983E28E741F8E7277B11A7479B626AC115BA31463AC48178A5075C5A9319D4A38";
          strObfuscationPoolDummyAddress = "y57cqfGRkekRyDRNeJiLtYVEbvhXrNbmox";
          nStartMasternodePayments = 1420837558; //Fri, 09 Jan 2015 21:05:58 GMT
          nBudget_Fee_Confirmations = 3; // Number of confirmations for the finalization fee. We have to make this very short
                                         // here because we only have a 8 block finalization window on testnet
  
          nProposalEstablishmentTime = 60 * 5; // Proposals must be at least 5 mns old to make it into a test budget
      }
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return dataTestnet;
      }
  };
  static CTestNetParams testNetParams;
  
  /**
   * Regression test
   */
  class CRegTestParams : public CTestNetParams
  {
  public:
      CRegTestParams()
      {
          networkID = CBaseChainParams::REGTEST;
          strNetworkID = "regtest";
          pchMessageStart[0] = 0xa1;
          pchMessageStart[1] = 0xcf;
          pchMessageStart[2] = 0x7e;
          pchMessageStart[3] = 0xac;
          nDefaultPort = 51476;
          nSubsidyHalvingInterval = 150;
          nEnforceBlockUpgradeMajority = 750;
          nRejectBlockOutdatedMajority = 950;
          nToCheckBlockUpgradeMajority = 1000;
          nMinerThreads = 1;
          bnProofOfWorkLimit = ~uint256(0) >> 1;
          nLastPOWBlock = 250;
          nMaturity = 100;
          nStakeMinAge = 0;
          nStakeMinDepth = 0;
          nMasternodeCountDrift = 4;
          nModifierUpdateBlock = 0; //approx Mon, 17 Apr 2017 04:00:00 GMT
          nMaxMoneyOut = 43199500 * COIN;
          nZerocoinStartHeight = 300;
          nBlockZerocoinV2 = 300;
          nZerocoinStartTime = 1501776000;
          nBlockEnforceSerialRange = 1; //Enforce serial range starting this block
          nBlockRecalculateAccumulators = 999999999; //Trigger a recalculation of accumulators
          nBlockFirstFraudulent = 999999999; //First block that bad serials emerged
          nBlockLastGoodCheckpoint = 999999999; //Last valid accumulator checkpoint
          nBlockStakeModifierlV2 = nLastPOWBlock + 1; // start with modifier V2 on testnet
          nBlockTimeProtocolV2 = 999999999;
  
          nMintRequiredConfirmations = 10;
          nZerocoinRequiredStakeDepth = nMintRequiredConfirmations;
  
          // Public coin spend enforcement
          nPublicZCSpends = 350;
  
          // Blocks v7
          nBlockV7StartHeight = nBlockZerocoinV2;
          nBlockLastAccumulatorCheckpoint = nBlockZerocoinV2-1; // no accumul. checkpoints check on regtest
  
          // New P2P messages signatures
          nBlockEnforceNewMessageSignatures = 1;
  
          // Fake Serial Attack
          nFakeSerialBlockheightEnd = -1;
  
          //! Modify the regtest genesis block so the timestamp is valid for a later start.
          genesis.nTime = 1454124731;
          genesis.nNonce = 5;
  
          hashGenesisBlock = genesis.GetHash();
  
      //    assert(hashGenesisBlock == uint256("0x15650dd7610bec6a5f354afdd9fa5dd2de0b3c0326912efc079e9752b9321bb7"));
  
          vFixedSeeds.clear(); //! Testnet mode doesn't have any fixed seeds.
          vSeeds.clear();      //! Testnet mode doesn't have any DNS seeds.
  
          fMiningRequiresPeers = false;
          fAllowMinDifficultyBlocks = true;
          fDefaultConsistencyChecks = true;
          fRequireStandard = false;
          fMineBlocksOnDemand = true;
          fSkipProofOfWorkCheck = true;
          fTestnetToBeDeprecatedFieldRPC = false;
  
          /* Spork Key for RegTest:
          WIF private key: 932HEevBSujW2ud7RfB1YF91AFygbBRQj3de3LyaCRqNzKKgWXi
          private key hex: bd4960dcbd9e7f2223f24e7164ecb6f1fe96fc3a416f5d3a830ba5720c84b8ca
          Address: yCvUVd72w7xpimf981m114FSFbmAmne7j9
          */
          strSporkPubKey = "043969b1b0e6f327de37f297a015d37e2235eaaeeb3933deecd8162c075cee0207b13537618bde640879606001a8136091c62ec272dd0133424a178704e6e75bb7";
      }
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return dataRegtest;
      }
  };
  static CRegTestParams regTestParams;
  
  /**
   * Unit test
   */
  class CUnitTestParams : public CMainParams, public CModifiableParams
  {
  public:
      CUnitTestParams()
      {
          networkID = CBaseChainParams::UNITTEST;
          strNetworkID = "unittest";
          nDefaultPort = 51478;
          vFixedSeeds.clear(); //! Unit test mode doesn't have any fixed seeds.
          vSeeds.clear();      //! Unit test mode doesn't have any DNS seeds.
  
          fMiningRequiresPeers = false;
          fDefaultConsistencyChecks = true;
          fAllowMinDifficultyBlocks = false;
          fMineBlocksOnDemand = true;
      }
  
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          // UnitTest share the same checkpoints as MAIN
          return data;
      }
  
      //! Published setters to allow changing values in unit test cases
      virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) { nSubsidyHalvingInterval = anSubsidyHalvingInterval; }
      virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority) { nEnforceBlockUpgradeMajority = anEnforceBlockUpgradeMajority; }
      virtual void setRejectBlockOutdatedMajority(int anRejectBlockOutdatedMajority) { nRejectBlockOutdatedMajority = anRejectBlockOutdatedMajority; }
      virtual void setToCheckBlockUpgradeMajority(int anToCheckBlockUpgradeMajority) { nToCheckBlockUpgradeMajority = anToCheckBlockUpgradeMajority; }
      virtual void setDefaultConsistencyChecks(bool afDefaultConsistencyChecks) { fDefaultConsistencyChecks = afDefaultConsistencyChecks; }
      virtual void setAllowMinDifficultyBlocks(bool afAllowMinDifficultyBlocks) { fAllowMinDifficultyBlocks = afAllowMinDifficultyBlocks; }
      virtual void setSkipProofOfWorkCheck(bool afSkipProofOfWorkCheck) { fSkipProofOfWorkCheck = afSkipProofOfWorkCheck; }
  };
  static CUnitTestParams unitTestParams;
  
  static CChainParams* pCurrentParams = 0;
  
  CModifiableParams* ModifiableParams()
  {
      assert(pCurrentParams);
      assert(pCurrentParams == &unitTestParams);
      return (CModifiableParams*)&unitTestParams;
  }
  
  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;
      case CBaseChainParams::UNITTEST:
          return unitTestParams;
      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;
  }