text: // Copyright (c) 2010 Satoshi Nakamoto
  // Copyright (c) 2009-2014 The Bitcoin developers
  // Copyright (c) 2014-2015 The Boa 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 "random.h"
  #include "util.h"
  #include "utilstrencodings.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("0x000004eb83e6baef0c5d4cb3c32c8a8365fd5979bed1ea429b37ec7aff33d71d"))
          ;
  static const Checkpoints::CCheckpointData data = {
          &mapCheckpoints,
          1519459200, // * 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)
          2800        // * estimated number of transactions per day after checkpoint
      };
  
  static Checkpoints::MapCheckpoints mapCheckpointsTestnet =
          boost::assign::map_list_of
          (  0, uint256("0000020821b22889d258d33df0f01c2d0ccb59145402495331587b897a51ce70"))
          ;
  static const Checkpoints::CCheckpointData dataTestnet = {
          &mapCheckpointsTestnet,
          1519459201,
          0,
          500
      };
  
  static Checkpoints::MapCheckpoints mapCheckpointsRegtest =
          boost::assign::map_list_of
          ( 0, uint256("0x342d175804743e5b44171e45420d9846b3dc8784759e45bfa8e852f544bf4356"))
          ;
  static const Checkpoints::CCheckpointData dataRegtest = {
          &mapCheckpointsRegtest,
          0,
          0,
          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] = 0xbf;
          pchMessageStart[1] = 0x6b;
          pchMessageStart[2] = 0x0c;
          pchMessageStart[3] = 0xbd;
          vAlertPubKey = ParseHex("04908cb0b135314cdd978c90d71e92d6f75dbd6b185ff56fa380f2816a83908b38717c6f5dbbd1acc48191b1526fd6da2c8a6c795f975a18498828e834778c1200");
          nDefaultPort = 9843;
          bnProofOfWorkLimit = ~uint256(0) >> 20;  // Boa starting difficulty is 1 / 2^12
          nSubsidyHalvingInterval = 525000;
          nEnforceBlockUpgradeMajority = 750;
          nRejectBlockOutdatedMajority = 950;
          nToCheckBlockUpgradeMajority = 1000;
          nMinerThreads = 0;
          nTargetTimespan = 24 * 60 * 60; // Boa: 1 day
          nTargetSpacing = 60; // Boa: 2.5 minutes
  
          /**
           * 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 = "Vasya's father is strong in mathematics. Dad is studying for Vasya all year round.";
          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 = 50 * COIN;
          txNew.vout[0].scriptPubKey = CScript() << ParseHex("044482f04fb4552d640071e7c37ffa0be87baac44622e56bde833b7816a66fafb8f132284b8865bfe9af318063858ffd4648cb959a356c1ad86243d7b6aa78aeac") << OP_CHECKSIG;
          genesis.vtx.push_back(txNew);
          genesis.hashPrevBlock = 0;
          genesis.hashMerkleRoot = genesis.BuildMerkleTree();
          genesis.nVersion = 1;
          genesis.nTime    = 1519459200;
          genesis.nBits    = 0x1e0ffff0;
          genesis.nNonce   = 1306738;
  
          hashGenesisBlock = genesis.GetHash();
          assert(hashGenesisBlock == uint256("0x000004eb83e6baef0c5d4cb3c32c8a8365fd5979bed1ea429b37ec7aff33d71d"));
          assert(genesis.hashMerkleRoot == uint256("0x83c0e1e3b827b0042a1ed027d20e9cf413dd5cf8dc071258465a1a26625e5e8b"));
  
          vSeeds.push_back(CDNSSeedData("darkcoin.io", "dnsseed.darkcoin.io"));
          vSeeds.push_back(CDNSSeedData("darkcoin.qa", "dnsseed.darkcoin.qa"));
          vSeeds.push_back(CDNSSeedData("masternode.io", "dnsseed.masternode.io"));
          vSeeds.push_back(CDNSSeedData("boapay.io", "dnsseed.boapay.io"));
  
          base58Prefixes[PUBKEY_ADDRESS] = list_of( 76);                    // Boa addresses start with 'X'
          base58Prefixes[SCRIPT_ADDRESS] = list_of( 16);                    // Boa script addresses start with '7'
          base58Prefixes[SECRET_KEY] =     list_of(204);                    // Boa private keys start with '7' or 'X'
          base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x02)(0xFE)(0x52)(0xF8); // Boa BIP32 pubkeys start with 'drkv'
          base58Prefixes[EXT_SECRET_KEY] = list_of(0x02)(0xFE)(0x52)(0xCC); // Boa BIP32 prvkeys start with 'drkp'
          base58Prefixes[EXT_COIN_TYPE]  = list_of(0x80000005);             // Boa BIP44 coin type is '5'
  
          convertSeed6(vFixedSeeds, pnSeed6_main, ARRAYLEN(pnSeed6_main));
  
          fRequireRPCPassword = true;
          fMiningRequiresPeers = true;
          fAllowMinDifficultyBlocks = false;
          fDefaultConsistencyChecks = false;
          fRequireStandard = true;
          fMineBlocksOnDemand = false;
          fSkipProofOfWorkCheck = false;
          fTestnetToBeDeprecatedFieldRPC = false;
  
          nPoolMaxTransactions = 3;
          strSporkKey = "0438810f93286fc2519c404b169f25c6d4d3e6dfa82f8ebc44612f07220dbc3b61daba8c8558683049c83de846e34c5677901ed2ec8b289e0a524be330376cdc3a";
          strMasternodePaymentsPubKey = "0438810f93286fc2519c404b169f25c6d4d3e6dfa82f8ebc44612f07220dbc3b61daba8c8558683049c83de846e34c5677901ed2ec8b289e0a524be330376cdc3a";
          strDarksendPoolDummyAddress = "Xq19GqFvajRrEdDHYRKGYjTsQfpV5jyipF";
          nStartMasternodePayments = 1403728576; //Wed, 25 Jun 2014 20:36:16 GMT
      }
  
      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] = 0xce;
          pchMessageStart[1] = 0xe2;
          pchMessageStart[2] = 0xca;
          pchMessageStart[3] = 0xff;
          vAlertPubKey = ParseHex("04908cb0b135314cdd978c90d71e92d6f75dbd6b185ff56fa380f2816a83908b38717c6f5dbbd1acc48191b1526fd6da2c8a6c795f975a18498828e834778c1200");
          nDefaultPort = 19843;
          nEnforceBlockUpgradeMajority = 51;
          nRejectBlockOutdatedMajority = 75;
          nToCheckBlockUpgradeMajority = 100;
          nMinerThreads = 0;
          nTargetTimespan = 24 * 60 * 60; // Boa: 1 day
          nTargetSpacing = 60; // Boa: 2.5 minutes
  
          //! Modify the testnet genesis block so the timestamp is valid for a later start.
          genesis.nTime = 1519459201;
          genesis.nNonce = 468395;
  
          hashGenesisBlock = genesis.GetHash();
          assert(hashGenesisBlock == uint256("0x0000020821b22889d258d33df0f01c2d0ccb59145402495331587b897a51ce70"));
  
          vFixedSeeds.clear();
          vSeeds.clear();
          /*vSeeds.push_back(CDNSSeedData("boapay.io", "testnet-seed.boapay.io"));
          vSeeds.push_back(CDNSSeedData("boa.qa", "testnet-seed.boa.qa"));
          *///legacy seeders
          vSeeds.push_back(CDNSSeedData("darkcoin.io",  "testnet-seed.darkcoin.io"));
          vSeeds.push_back(CDNSSeedData("darkcoin.qa", "testnet-seed.darkcoin.qa"));
          vSeeds.push_back(CDNSSeedData("masternode.io", "test.dnsseed.masternode.io"));
  
          base58Prefixes[PUBKEY_ADDRESS] = list_of(139);                    // Testnet boa addresses start with 'x' or 'y'
          base58Prefixes[SCRIPT_ADDRESS] = list_of( 19);                    // Testnet boa script addresses start with '8' or '9'
          base58Prefixes[SECRET_KEY]     = list_of(239);                    // Testnet private keys start with '9' or 'c' (Bitcoin defaults)
          base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x3a)(0x80)(0x61)(0xa0); // Testnet boa BIP32 pubkeys start with 'DRKV'
          base58Prefixes[EXT_SECRET_KEY] = list_of(0x3a)(0x80)(0x58)(0x37); // Testnet boa BIP32 prvkeys start with 'DRKP'
          base58Prefixes[EXT_COIN_TYPE]  = list_of(0x80000001);             // Testnet boa BIP44 coin type is '5' (All coin's testnet default)
  
          convertSeed6(vFixedSeeds, pnSeed6_test, ARRAYLEN(pnSeed6_test));
  
          fRequireRPCPassword = true;
          fMiningRequiresPeers = true;
          fAllowMinDifficultyBlocks = true;
          fDefaultConsistencyChecks = false;
          fRequireStandard = false;
          fMineBlocksOnDemand = false;
          fTestnetToBeDeprecatedFieldRPC = true;
  
          nPoolMaxTransactions = 2;
          strSporkKey = "0438810f93286fc2519c404b169f25c6d4d3e6dfa82f8ebc44612f07220dbc3b61daba8c8558683049c83de846e34c5677901ed2ec8b289e0a524be330376cdc3a";
          strMasternodePaymentsPubKey = "0438810f93286fc2519c404b169f25c6d4d3e6dfa82f8ebc44612f07220dbc3b61daba8c8558683049c83de846e34c5677901ed2ec8b289e0a524be330376cdc3a";
          strDarksendPoolDummyAddress = "y1EZuxhhNMAUofTBEeLqGE1bJrpC2TWRNp";
          nStartMasternodePayments = 1420837558; //Fri, 09 Jan 2015 21:05:58 GMT
      }
      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] = 0xfc;
          pchMessageStart[1] = 0xc1;
          pchMessageStart[2] = 0xb7;
          pchMessageStart[3] = 0xdc;
          nSubsidyHalvingInterval = 150;
          nEnforceBlockUpgradeMajority = 750;
          nRejectBlockOutdatedMajority = 950;
          nToCheckBlockUpgradeMajority = 1000;
          nMinerThreads = 1;
          nTargetTimespan = 24 * 60 * 60; // Boa: 1 day
          nTargetSpacing = 2.5 * 60; // Boa: 2.5 minutes
          bnProofOfWorkLimit = ~uint256(0) >> 1;
          genesis.nTime = 1519459202;
          genesis.nBits = 0x207fffff;
          genesis.nNonce = 0;
          hashGenesisBlock = genesis.GetHash();
          nDefaultPort = 19844;
          assert(hashGenesisBlock == uint256("0x342d175804743e5b44171e45420d9846b3dc8784759e45bfa8e852f544bf4356"));
  
          vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds.
          vSeeds.clear();  //! Regtest mode doesn't have any DNS seeds.
  
          fRequireRPCPassword = false;
          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 = 18843;
          vFixedSeeds.clear(); //! Unit test mode doesn't have any fixed seeds.
          vSeeds.clear();  //! Unit test mode doesn't have any DNS seeds.
  
          fRequireRPCPassword = false;
          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;
  }
  