In this article we’ll take a look at one of the better known Bitcoin Core source files.
Why is chainparams.* known to so many crypto developers?
Because since 2011 or so, basically every coin that appeared before Ethereum would edit this source file and its header to modify parameters and create a new cryptocurrency!
After creating a new genesis block, literally substituting all occurrences of the BTC string for LTC (or DOGE or REDD or whatever) and making some adjustments to block time and other parameters, you’d have a new cryptocurrency! Several cryptos forked Litecoin instead, because they wanted to use Scrypt instead of SHA256. Anyway, forking is transitive so a fork of Litecoin which is a fork of Bitcoin is a fork of Bitcoin!
All first generation cryptocurrencies were created by forking Bitcoin somehow (directly or otherwise) and the first step is to edit this section of the code.
So let’s jump right into chainparams.h
!
chainparams.h
struct SeedSpec6 {
uint8_t addr[16];
uint16_t port;
};
typedef std::map<int, uint256> MapCheckpoints;
struct CCheckpointData {
MapCheckpoints mapCheckpoints;
};
SeedSpec6
encapsulates a 128 bit data structure used to store IP addresses both in IPv6 and IPv4, along with a 16 bit port number.
The way IPv4 addresses are embedded in IPv6 is described here by the IETF. Basically it’s a 96 bit prefix followed by the 32 IPv4 bits. In Bitcoin Core the “well known” prefix ends in two 0xFF
bytes.
Here’s an excerpt from chainparamsseeds.h
where SeedSpec6
is used so you can visualize how the addresses and ports are set up:
static SeedSpec6 pnSeed6_main[] = {
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x02,0x84,0x64,0x2f}, 8333},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x01,0x61,0x04}, 8333},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x27,0xae,0x74}, 8333},
Next up we have the declaration of MapCheckpoints.
typedef std::map<int, uint256> MapCheckpoints;
struct CCheckpointData {
MapCheckpoints mapCheckpoints;
};
MapCheckpoints
is simply a std::map
that hardcodes block numbers and their hash together.
This is an old safety feature in Bitcoin Core that stopped being updated on block height 295000 from April, 2014. The idea was that if anyone changed anything in the blockchain which led to a different hash at that checkpoint, then the new blockchain would be refused by the network.
The latest checkpoint would be updated by hand before the next Bitcoin Core release – it’s hardcoded in the Bitcoin Core sources! (In fact this procedure was described in the release instructions.) Currently, this feature remains in the source code but it’s no longer updated.
CCheckpointData
simply encapsulates a MapCheckpoints
data member.
The reason for this apparently redundant abstraction is probably that other data members were planned to go along with a CCheckpointData
but the feature got discontinued before it ever did.
Next section has self-explanatory comments:
/**
* Holds various statistics on transactions within a chain. Used to estimate
* verification progress during chain sync.
*
* See also: CChainParams::TxData, GuessVerificationProgress.
*/
struct ChainTxData {
int64_t nTime; //!< UNIX timestamp of last known number of transactions
int64_t nTxCount; //!< total number of transactions between genesis and that timestamp
double dTxRate; //!< estimated number of transactions per second after that timestamp
};
Now class CChainParams
is declared, along with all its parameters.
The comments make it mostly self-explanatory but I’ll go over some parts after the source:
/**
* CChainParams defines various tweakable parameters of a given instance of the
* Bitcoin system. There are three: the main network on which people trade goods
* and services, the public test network which gets reset from time to time and
* a regression test mode which is intended for private networks only. It has
* minimal difficulty to ensure that blocks can be found instantly.
*/
class CChainParams
{
public:
enum Base58Type {
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
MAX_BASE58_TYPES
};
const Consensus::Params& GetConsensus() const { return consensus; }
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
int GetDefaultPort() const { return nDefaultPort; }
const CBlock& GenesisBlock() const { return genesis; }
/** Default value for -checkmempool and -checkblockindex argument */
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
/** Policy: Filter transactions that do not match well-defined patterns */
bool RequireStandard() const { return fRequireStandard; }
/** If this chain is exclusively used for testing */
bool IsTestChain() const { return m_is_test_chain; }
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
/** Minimum free space (in GB) needed for data directory */
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
/** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/
uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; }
/** Whether it is possible to mine blocks on demand (no retargeting) */
bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::string& Bech32HRP() const { return bech32_hrp; }
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
protected:
CChainParams() {}
Consensus::Params consensus;
CMessageHeader::MessageStartChars pchMessageStart;
int nDefaultPort;
uint64_t nPruneAfterHeight;
uint64_t m_assumed_blockchain_size;
uint64_t m_assumed_chain_state_size;
std::vector<std::string> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
std::string bech32_hrp;
std::string strNetworkID;
CBlock genesis;
std::vector<SeedSpec6> vFixedSeeds;
bool fDefaultConsistencyChecks;
bool fRequireStandard;
bool m_is_test_chain;
CCheckpointData checkpointData;
ChainTxData chainTxData;
};
/**
* Creates and returns a std::unique_ptr of the chosen chain.
* @returns a CChainParams* of the chosen chain.
* @throws a std::runtime_error if the chain is not supported.
*/
std::unique_ptr CreateChainParams(const std::string& chain);
/**
* Return the currently selected parameters. This won't change after app
* startup, except for unit tests.
*/
const CChainParams &Params();
/**
* Sets the params returned by Params() to those for the given BIP70 chain name.
* @throws std::runtime_error when the chain is not supported.
*/
void SelectParams(const std::string& chain);
enum Base58Type
enumerates the types of addresses recognized by Bitcoin Core and which are encoded by Base58. (These are discussed at length on the Bitcoin Wiki.)
const Consensus::Params& GetConsensus()
returns the chain consensus parameters. These include the several Core forks along the way for implementations of BIP’s, among other info. This information is declared in consensus/params.h
const CMessageHeader::MessageStartChars& MessageStart()
returns the network message prefix. Everything that’s sent out the network is a message. In this specific case it’ll return 4 unsigned characters used to identify message headers.
int GetDefaultPort() const { return nDefaultPort; }
is an inline one-liner. Just a getter that returns the default network port (8333 for main net).
const CBlock& GenesisBlock() const { return genesis; }
returns a CBlock
reference to the genesis block. The genesis block is hard coded in Bitcoin Core.
Next methods have self-explanatory comments:
/** Default value for -checkmempool and -checkblockindex argument */
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
/** Policy: Filter transactions that do not match well-defined patterns */
bool RequireStandard() const { return fRequireStandard; }
/** If this chain is exclusively used for testing */
bool IsTestChain() const { return m_is_test_chain; }
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
/** Minimum free space (in GB) needed for data directory */
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
/** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/
uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; }
/** Whether it is possible to mine blocks on demand (no retargeting) */
bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
Lastly, 5 getter methods return private variables:
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::string& Bech32HRP() const { return bech32_hrp; }
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
Some methods worth mentioning:
Base58Prefix
– we’ve discussed these earlier in the header file section.
Bech32HRP
Bech32 prefix will be bc for Bitcoin main net, tb for testnet and bcrt for the regression testnet using bech32 addresses.
FixedSeeds()
returns a std::vector that is pre-filled at chainparamsseeds.h
where it’s copied from static SeedSpec6 pnSeed6_main[]
Others are trivial.
Now for the class members (I’ve made comments starting with > within the code itself) :
> Consensus::Params is defined in consensus/params.h and includes such fields as the halving interval, block height where certain relevant forks happened, several proof of work parameters and so on. Consensus::Params consensus; > 4 bytes at teh beginning of network messages CMessageHeader::MessageStartChars pchMessageStart; > Default port for Bitcoin RPC, normally 8333 for the main net. int nDefaultPort; > Prune the chain at this height uint64_t nPruneAfterHeight; > Assumed blockchain size in gigabytes. uint64_t m_assumed_blockchain_size; > Assumed chain state size in gigabytes. > Is later added to m_assumed_blockchain_size to get requiredSpace > (e.g in qt/intro.cpp line 146) uint64_t m_assumed_chain_state_size; > Seed hostnames std::vector<std::string> vSeeds; > Already discussed in header section. std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; std::string bech32_hrp; > main, text or regtest std::string strNetworkID; > Genesis block CBlock genesis; > Seeds in IP address form. See discussion of SeedSpec6 in header section. std::vector<SeedSpec6> vFixedSeeds; > Default true only for regtest network, false for main and test > Modified by -checkblockindex and -checkmempool command line options bool fDefaultConsistencyChecks; > Default true for main and test, false for regtest > Modified by -acceptnonstdtxn bool fRequireStandard; > False for main, true for test and regtest bool m_is_test_chain; > Contains MapCheckpoints. No longer updated since 2014. CCheckpointData checkpointData; > Commented on after this code section ChainTxData chainTxData;
Last line we have there is ChainTxData. This struct holds some key stats for the state of the blockchain at some point in time. The three networks, test, regtest and main each have their own ChainTxData. Here are the values for the version this text is based on:
chainTxData = ChainTxData{
// Data from rpc: getchaintxstats 4096 0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee
/* nTime */ 1550374134,
/* nTxCount */ 383732546,
/* dTxRate */ 3.685496590998308
};
nTime
is the Unix timestamp value (number of seconds since the epoch), which is 2019-02-17T03:28:54+00:00. nTxCount is the number of transactions since the Bitcoin genesis block. dTxRate
is the average number of transactions per second (TPS) after the block mined at nTime
.
Note that if you divide nTxCount
by nTime
you get a much smaller number than dTxRate
because early on Bitcoin would have very few TPS.
chainparams.cpp
First thing on chainparams.cpp is a subroutine that generates the Bitcoin genesis block:
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.nTime = nTime;
genesis.nBits = nBits;
genesis.nNonce = nNonce;
genesis.nVersion = nVersion;
genesis.vtx.push_back(MakeTransactionRef(std::move(txNew)));
genesis.hashPrevBlock.SetNull();
genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
return genesis;
}
It’s obviously a single reward transaction block, since nobody could’ve used Bitcoin before it. That transaction is instantiated via CMutableTransaction txNew
and then its parameters are set up. Next a block prototype is created via CBlock genesis
and its parameters filled. The single transaction is std::move
‘d to the genesis block’s vtx
field, the previous block hash is set to null
and a Merkle root is computed for the block.
Now it’s time to use CreateGenesisBlock to generate Bitcoin’s famous cornerstone:
/**
* Build the genesis block. Note that the output of its generation
* transaction cannot be spent since it did not originally exist in the
* database.
*
* CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
* CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
* CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
* CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
* vMerkleTree: 4a5e1e
*/
static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}
The output script is hard-coded and injected into CScript()
via the <<
operator. The famous “Chancellor on brink of bailout” timestamp message is passed via the pszTimestamp
and the other parameters are all constants as described in the function comment. Several genesis block generators are available on the WWW including C and python versions that you can use to practice crypto development skills. We now have a genesis block with a null previous block. It’s where the Bitcoin blockchain begins.
We now define CMainParams which is an instance of CChainParams. In chainparams.cpp we have two other instances of CChainParams, one for the test network and another for the regtest network. I’ll leave the latter two as homework! Here we’ll focus only on the main network chain parameters.
My comments are embedded within the code, always starting with ‘<‘.
class CMainParams : public CChainParams { public: CMainParams() { > Bitcoin network id string. Already discussed in header. Valid values: main, test, regtest. strNetworkID = "main"; > 210k blocks between block reward halvings. > ~About 1458.33 days or 3.9954 years (4 years). consensus.nSubsidyHalvingInterval = 210000; > Recognize BIP16 pay to hash only after block
170060 (timestamp 2012-03-07 16:33:03)consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"); > Move to version 2 blocks, do not recognize v1 blocks after this height + hash consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); > Implements OP_CHECKLOCKTIMEVERIFY opcode which locks tx's until future time. consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 > Enforces DER encoding from 363725 block onwards. https://en.wikipedia.org/wiki/X.690#DER_encoding consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 > From the documentation : "Block height at which CSV (BIP68, BIP112 and BIP113) becomes active." > These 3 BIP's collectively knowh as CheckSequenceVerify = https://en.bitcoin.it/wiki/CheckSequenceVerify consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 > The famous Seggregated Witness SegWit fork from August 2017. > Discussions surrounding Segwit led to the Bitcoin Cash fork earlier on block 478559 consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893 > Maximum proof of work value. This is used in difficulty calculation. consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); > Difficulty is adjusted every 14 days. Units: seconds. consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks > One block every 10 minutes. consensus.nPowTargetSpacing = 10 * 60; > Minimum difficulty blocks IF miners fail to find blocks for two 10 minute > intervals in a row. This is only activated for test networks. consensus.fPowAllowMinDifficultyBlocks = false; > Allow difficulty retargeting on main net. consensus.fPowNoRetargeting = false; > 95% consensus is required to accept protocol rule changes consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 > The 95% consensus must be obtained in 2016 blocks time (window = one retargeting period). consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing > Satoshi's playground consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 > Newly mined blocks must have a hash smaller than this // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000051dc8b82f450202ecb3d471"); > Don't trust, verify. But only if block height > 563378 // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee"); //563378 > Network messages begin with these 4 bytes, for the reasons given in the comment: /** * 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 32-bit integer with any alignment. */ pchMessageStart[0] = 0xf9; pchMessageStart[1] = 0xbe; pchMessageStart[2] = 0xb4; pchMessageStart[3] = 0xd9; > Our favorite network port number: nDefaultPort = 8333; > Prune blocks after 100k nPruneAfterHeight = 100000; > Assume blockchain is at least 240 gigabytes m_assumed_blockchain_size = 240; > Assume chain state is at least 3 gigabytes m_assumed_chain_state_size = 3; > Note the parameters used for our genesis block. > Use these to roll out your own in coding exercises genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); > set genesis block hash consensus.hashGenesisBlock = genesis.GetHash(); > Assert that no one has tampered with Satoshi's block assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); > Hard-coded network seeds. // Note that of those which support the service bits prefix, most only support a subset of // possible options. // This is fine at runtime as we'll fall back to using them as a oneshot if they don't support the // service bits we want, but we should get them updated to support all service bits wanted by any // release ASAP to avoid it where possible. vSeeds.emplace_back("seed.bitcoin.sipa.be"); // Pieter Wuille, only supports x1, x5, x9, and xd vSeeds.emplace_back("dnsseed.bluematt.me"); // Matt Corallo, only supports x9 vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org"); // Luke Dashjr vSeeds.emplace_back("seed.bitcoinstats.com"); // Christian Decker, supports x1 - xf vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch"); // Jonas Schnelli, only supports x1, x5, x9, and xd vSeeds.emplace_back("seed.btc.petertodd.org"); // Peter Todd, only supports x1, x5, x9, and xd vSeeds.emplace_back("seed.bitcoin.sprovoost.nl"); // Sjors Provoost vSeeds.emplace_back("dnsseed.emzy.de"); // Stephan Oeste > Set the prefixes for the Bitcoin main net. Note: 0 becomes one when base58 encoded base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; > Hard-coded prefix for bech32 addresses bech32_hrp = "bc"; > Copy pnSeed6_main from chainparamsseeds.h vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); > Whether to check mempool and block index consistency by default fDefaultConsistencyChecks = false; > Filter out transactions that don't comply with the Bitcoin Core standard fRequireStandard = true; > Whether we're in a test chain m_is_test_chain = false; > Hard-coded check-points > Blockchain is assumed valid before each of these. > Until 2014 these were manually updated with Bitcoin Core releases. checkpointData = { { { 11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")}, { 33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")}, { 74000, uint256S("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")}, {105000, uint256S("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")}, {134444, uint256S("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")}, {168000, uint256S("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")}, {193000, uint256S("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")}, {210000, uint256S("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")}, {216116, uint256S("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")}, {225430, uint256S("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")}, {250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")}, {279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")}, {295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")}, } }; > Transaction data from a certain block onwards. Discussed in the header section. chainTxData = ChainTxData{ // Data from rpc: getchaintxstats 4096 0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee /* nTime */ 1550374134, /* nTxCount */ 383732546, /* dTxRate */ 3.685496590998308 }; } };
This completes our tour of the chainparams header and cpp source. By learning all the fields in chainparams.cpp you’ll become familiar with the specifics not only with Bitcoin Core but also with each blockchain derived from the Bitcoin Core system. Other systems will tweak these values and create new coins based on them.
If you’re learning to program Bitcoin-related software, make sure to play with the test and regtest networks as well. We didn’t discuss the latter two networks here because it’d be redundant. The functionality is the same, only the parameter values change.
Return to Commented Bitcoin Source Code introduction.