So you’ve read the theory, but want to understand Bitcoin programmatically. In this post we will analyse the
core codebase written in C++ to understand how one can generate and include a block in the chain. Starting with
the atomic unit of construction in this context, a block is a grouping of transactions that alter the state of
the ledger. For example, a
coinbase transaction may be included by a miner to collect a block reward. But in
order for these to be processed by the network, a miner must provide a proof of work for the serialization of
the following fields:
The class definition of a block header is shown in the following snippet, of particular interest is
which tells the compiler how an instance of this class should be serialized and
GetHash which returns a 256-bit
The implementation of
GetHash calls another function
SerializeHash passing a dereferenced pointer to its context.
CHashWriter is established which computes the digest via the
We can see that
Finalize writes and flushes the hash to the input buffer. It is important to note that
this actually computes
SHA-256d (double) in an effort to prevent “length-extension” attacks.
Assuming this process has given us a valid digest of our input, how can we convince other participants to
append our block to their view of the chain? This is where the consensus parameter
bnTarget comes in.
For brevity, we will ignore how this is chosen but I will note that this is dynamic - the network adapts it
based on mining speeds. Honest nodes will thus accept a gossiped block under the condition that the arithmetical
representation of its hash is less that that of the current target.
To recompute a new hash, we need some variability in the form of
nNonce. As there is no way to predict what input will lead to
a desired hash, all we can do is increment it. There’s a useful example of this in the following test helper.
Providing the final hash meets the criteria and enough nodes know about it, subsequent miners will include it as
hashPrevBlock in their constructions.