text: // Copyright (c) 2010 Satoshi Nakamoto
  // Copyright (c) 2009-2014 The Bitcoin developers
  // Copyright (c) 2014-2015 The Dash developers
  // Copyright (c) 2015-2017 The PIVX developers
  // Copyright (c) 2018 The 401K Coin 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 "bignum.h"
  #include "random.h"
  #include "util.h"
  #include "utilstrencodings.h"
  
  #include "crypto/scrypt.h"
  
  #include <assert.h>
  
  #include <boost/assign/list_of.hpp>
  
  using namespace std;
  using namespace boost::assign;
  
  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("0x0000024c7e1eaa022b22764db8a2ebffb3e40177dc4974b0050e67f565a67907"));
  
  static const Checkpoints::CCheckpointData data = {
      &mapCheckpoints,
      1530280390, // * UNIX timestamp of last checkpoint block
      0,          // * 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,
      1530280411,
      0,
      250};
  
  static Checkpoints::MapCheckpoints mapCheckpointsRegtest =
      boost::assign::map_list_of(0, uint256("0x001"));
  static const Checkpoints::CCheckpointData dataRegtest = {
      &mapCheckpointsRegtest,
      1530280411,
      0,
      100};
  
  void CChainParams::MineNewGenesisBlock()
   {
      // genesis.nTime=time(null);
      genesis.nNonce=0;
      uint256 thash;
      while(1)
      {
          thash=genesis.GetHash();
          if (this->CheckProofOfWork(thash, genesis.nBits))
              break;
          if ((genesis.nNonce & 0xFF) == 0)
          {
              printf("nonce %08X: hash = %s\n",genesis.nNonce, thash.ToString().c_str());
          }
          ++genesis.nNonce;
          if (genesis.nNonce == 0)
          {
              printf("NONCE WRAPPED, incrementing time\n");
              ++genesis.nTime;
          }
      }
      printf("genesis.nTime = %u;\n",genesis.nTime);
      printf("genesis.nNonce = %u;\n",genesis.nNonce);
      printf("assert(genesis.hashMerkleRoot == uint256(\"0x%s\"));\n",genesis.hashMerkleRoot.ToString().c_str());
      printf("//genesis hash: 0x%s\n", genesis.GetHash().ToString().c_str());
      exit(1);
  };
  
  //need a different implementation here that doesn't use error() and that doesn't use Params() since it isn't yet usable
  bool CChainParams::CheckProofOfWork(uint256 hash, unsigned int nBits)
  {
      bool fNegative;
      bool fOverflow;
      uint256 bnTarget;
  
      bnTarget.SetCompact(nBits, &fNegative, &fOverflow);
  
      // Check proof of work matches claimed amount
      if (hash > bnTarget)
          return false;
  
      return true;
  };
  
  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] = 0xa1;
          pchMessageStart[1] = 0xe2;
          pchMessageStart[2] = 0x9a;
          pchMessageStart[3] = 0x2b;
          vAlertPubKey = ParseHex("04878a83b51fcf96c2b43690f09c84bbd64df781333ad1dc96d66121e65f7572d3630542a0def8611bb836a009bd42c7aec41d17ded5e0239536791e02ab9272a3");
          nDefaultPort = 6622;
          bnProofOfWorkLimit = ~uint256(0) >> 20;
          nSubsidyHalvingInterval = 1050000;
          nMaxReorganizationDepth = 100;
          nEnforceBlockUpgradeMajority = 750;
          nRejectBlockOutdatedMajority = 950;
          nToCheckBlockUpgradeMajority = 1000;
          nMinerThreads = 0;
          nTargetTimespan = 2 * 60; // 401K coin: 2 minutes
          nTargetSpacing = 2 * 60;  // 401K coin: 2 minutes
          nMaturity = 15;
          nMasternodeCountDrift = 20;
          nMaxMoneyOut = 25000000 * COIN;
  
          /** Height or Time Based Activations **/
          nLastPOWBlock = 100;
          nModifierUpdateBlock = 1; // we use the version 2 for 401K
  
          const char* pszTimestamp = "401KCoin Core Developers Team";
          CMutableTransaction txNew;
          txNew.vin.resize(1);
          txNew.vout.resize(1);
          txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << 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("044821e8e0acf5d9385fe8ec9cb591698b86e24a848738cba71e26be1363bb8bb95dbcaa4f9fc60d142ad70ad89c57b6a43bd2c217e347ab9d4df2dded9200d104") << OP_CHECKSIG;
          genesis.vtx.push_back(txNew);
          genesis.hashPrevBlock = 0;
          genesis.hashMerkleRoot = genesis.BuildMerkleTree();
          genesis.nVersion = 1;
          genesis.nTime = 1530280390;
          genesis.nBits = 0x1e0ffff0;
          genesis.nNonce = 285077;      
  
          // MineNewGenesisBlock();
  	    hashGenesisBlock = genesis.GetHash();
          assert(hashGenesisBlock == uint256("0x0000024c7e1eaa022b22764db8a2ebffb3e40177dc4974b0050e67f565a67907"));
          assert(genesis.hashMerkleRoot == uint256("0x2df8f312c3166f9f80afd3e87acdff75bf413b38c42e5fc2a93fb2d69ddcdeb6"));
  
          // DNS Seeding
          vSeeds.push_back(CDNSSeedData("1", "178.128.145.147"));
          vSeeds.push_back(CDNSSeedData("2", "167.99.45.10"));
          vSeeds.push_back(CDNSSeedData("3", "139.59.74.84"));
          vSeeds.push_back(CDNSSeedData("4", "206.189.58.59"));
          vSeeds.push_back(CDNSSeedData("5", "178.128.194.194"));
          vSeeds.push_back(CDNSSeedData("6", "206.189.126.13"));
  
          // 401K coin addresses start with 'K'
          base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 45);
          // 401K coin script addresses start with '4'
          base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 8);
          // 401K coin private keys start with 'P'
          base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, 46);
          // 401K coin BIP32 pubkeys start with 'xpub' (Bitcoin defaults)
          base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >();
          // 401K coin BIP32 prvkeys start with 'xprv' (Bitcoin defaults)
          base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >();
          // 401K coin BIP44 coin type is '222' (0x800000de)
          // 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 = false;
          fAllowMinDifficultyBlocks = false;
          fDefaultConsistencyChecks = false;
          fRequireStandard = true;
          fMineBlocksOnDemand = false;
          fSkipProofOfWorkCheck = false;
          fTestnetToBeDeprecatedFieldRPC = false;
          fHeadersFirstSyncingActive = false;
  
          nPoolMaxTransactions = 3;
          strSporkKey = "04c627eb27385ec2bd733fde55c1b2f6e48b768bc43c57cd3ac11f1972846b979418425f29c1dfc5ec7776e46ac8b6c9e5fea610dff0804a5b2681b2633f1e3070";
          strMasternodePoolDummyAddress = "KEYiweVK31tG5DyxzaVHRWcr57KQZjN3jt";
          nStartMasternodePayments = genesis.nTime; // 21600; // 24 hours after genesis creation
  
          nBudget_Fee_Confirmations = 6; // Number of confirmations for the finalization fee
      }
  
      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] = 0xa3;
          pchMessageStart[1] = 0x24;
          pchMessageStart[2] = 0x9c;
          pchMessageStart[3] = 0xbd;
          vAlertPubKey = ParseHex("04cb01107a46c5ff9b5b7f639a89ba5c19b7b8aa2c3a6db651c85bb4f042f617c3b05e221c4ebe2038773b9db6ffe481d91bc38d409e314a919a8609e48e5da288");
          nDefaultPort = 6624;
          nEnforceBlockUpgradeMajority = 51;
          nRejectBlockOutdatedMajority = 75;
          nToCheckBlockUpgradeMajority = 100;
          nMinerThreads = 0;
          nTargetTimespan = 2 * 60; // 401K coin: 1 day
          nTargetSpacing = 2 * 60;  // 401K coin: 2 minutes
          nLastPOWBlock = 200;
          nMaturity = 15;
          nMasternodeCountDrift = 4;
          nModifierUpdateBlock = 1;
          nMaxMoneyOut = 25000000 * COIN;
  
          //! Modify the testnet genesis block so the timestamp is valid for a later start.
          genesis.nTime = 1530280411;
          genesis.nNonce = 1075308;
          
          // MineNewGenesisBlock();
          hashGenesisBlock = genesis.GetHash();
          assert(hashGenesisBlock == uint256("0x000003a240e1323a4c7f0490a72e37a56102c9b311d90342a8535d4dd2e8bf61"));
  
          vFixedSeeds.clear();
          vSeeds.clear();
          
          // Testnet 401K coin addresses start with 'k'
          base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 107);
          // Testnet 401K coin script addresses start with '5' or '6'
          base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 12);
          // Testnet private keys start with 'p'
          base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, 117);
          // Testnet 401K coin 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> >();
          // Testnet 401K coin 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> >();
          // Testnet 401K Coin 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 = true;
          fAllowMinDifficultyBlocks = false;
          fDefaultConsistencyChecks = false;
          fRequireStandard = false;
          fMineBlocksOnDemand = false;
          fTestnetToBeDeprecatedFieldRPC = true;
  
          nPoolMaxTransactions = 2;
          strSporkKey = "0452490279ae0c3be6b9523db00b67e604d8489d17f1d52db51994277ecc7021aa75a3da55a7f828035838d8fe5cd1d8efad29df275c62a57ac733cfa9ca81146d";
          strMasternodePoolDummyAddress = "kK2WToV9w7q1q2BJeGwxYqxf1jJsv55Nxv";
          nStartMasternodePayments = genesis.nTime;// + 86400; // 24 hours after genesis
          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
      }
      const Checkpoints::CCheckpointData& Checkpoints() const
      {
          return dataTestnet;
      }
  };
  static CTestNetParams testNetParams;
  
  /**
   * Regression test
   */
  class CRegTestParams : public CTestNetParams
  {
  public:
      CRegTestParams()
      {
          networkID = CBaseChainParams::REGTEST;
          strNetworkID = "regtest";
          strNetworkID = "regtest";
          pchMessageStart[0] = 0xa5;
          pchMessageStart[1] = 0x56;
          pchMessageStart[2] = 0x3e;
          pchMessageStart[3] = 0xbf;
          nSubsidyHalvingInterval = 150;
          nEnforceBlockUpgradeMajority = 750;
          nRejectBlockOutdatedMajority = 950;
          nToCheckBlockUpgradeMajority = 1000;
          nMinerThreads = 1;
          nTargetTimespan = 24 * 60 * 60; // 401K coin: 1 day
          nTargetSpacing = 2 * 60;        // 401K coin: 1 minutes
          bnProofOfWorkLimit = ~uint256(0) >> 1;
          genesis.nTime = 1530280411;
          genesis.nBits = 0x207fffff;
          genesis.nNonce = 1;
  
          // MineNewGenesisBlock();
          hashGenesisBlock = genesis.GetHash();
          nDefaultPort = 6622;
          assert(hashGenesisBlock == uint256("0x569b549c8cd16d7ad786c18789ebc8c5ff7484ee4d2aecf4ac9740d4c1d24cd1"));
  
          vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds.
          vSeeds.clear();      //! Regtest mode doesn't have any DNS seeds.
  
          fMiningRequiresPeers = false;
          fAllowMinDifficultyBlocks = true;
          fDefaultConsistencyChecks = true;
          fRequireStandard = false;
          fMineBlocksOnDemand = true;
          fTestnetToBeDeprecatedFieldRPC = false;
      }
      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 = 6624;
          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;
  }
  