Demistifying blockchain - Part 2
In the previous post we explained from a theoretical point of view how a block chain works. In this post we will get down to work and will implement a working blockchain in Go.
If you haven’t read it yet, we recommend you to do it now before continuing. It’ll provide you the basic concepts needed to understand the examples below.
(You can find the complete example in this Github repository: https://github.com/dplabs/demistifying-blockchain)
First of all, let’s define what a block looks like for us:
type Block struct {
Number int
Timestamp time.Time
Data string
Nonce int
Hash string
Previous string
}
Where:
Number
: is used to identify a block in the chain. In our case it will be the position this block has in the chain. Note that the attribute is just used to have a user friendly reference to the block, not to actually link blocks between them.Timestamp
: when this block was created.Data
: the actual information this block contains.Nonce
: field used to for the hash of the block to meet certain rules. More on this later.Hash
: the result of applying a cryptographic hash function to this block. It has to take into account all the fields in the block (butHash
itself) in order to be able to detect tampering attempts to its content.Previous
: hash of the previous block in the chain. This is the actual field linking blocks in the chain (and in our example, also the position in the array of blocks).
Now let’s define an structure that will help us handle the block chain:
type BlockChain struct {
Blocks map[string]*Block
Challenge string
LastBlock *Block
}
Where:
Blocks
: is the actual collection of blocks. Contiguous blocks are stored in contiguous positions in the array, although this is just an implementation detail, because the actual linkage should be done referencing their hashes.Challenge
: represents the level of difficulty the resulting hash has to meet in order to be considered valid. In our case we will say a block has is valid if it starts with theChallenge
substring. The longer this challenge is, the hardest and more time consuming mining a block would be.LastBlock
: is a pointer to the latest block added to the chain, used to make computation easier.
So far we have presented two quite interesting fields: Nonce
for a Block
, and Challenge
for a BlockChain
, and the deserve a more in depth explanation. But first let’s talk about what a valid hash or signature for a block is, and for that we have to talk a little bit about Bitcoin.
When Bitcoin was designed, in order to achieve value as a coin, the concept of scarcity had to be taken into consideration: if a resource is easy to get, probably it won’t be valuable at all. The more difficult or unique the resource is, the more value people will give it. But being a digital currency, this was a not easy to solve challenge.
That’s how the concept of mining a block came into the picture: same way as gold is extracted from the ground (with lots of effort), a block has to require a lot of (artificial) effort to be added to the chain. Whomever manages to first mine a block, gets rewarded with coins in exchange for his effort.
In Blockchain, mining a block consist on calculating its hash, as we have explained. But usually hash functions are quite fast, so in order to make it harder (and thus create scarcity), a valid hash has to comply with a set of rules. In our implementation, the rule we’ve chosen is that the resulting hash has to begin with the Challenge
substring defined while initializing the chain.
So in order to mine a block in our chain, the resulting signature of a block has to start with a certain string (in our example we’ve chosen it to be '0000'
). So the algorithm for mining a block would first calculate current hash of the block and if the results fits the criteria of a valid hash, then we could add a successfully mined block to the chain. Otherwise we would have to change something in the block and repeat the process, and this is important, because if we don’t change anything in the block, the resulting hash will be the same. And thats what Nonce
field is for. We will increment this number on every attempt to get a valid hash, until when eventually, we will get a hash matching the criteria. That’s why the longer the Challenge
string is, the harder to mine a block will be.
func (block *Block) Mine(challenge string) string {
fmt.Printf("Mining block %d... ", block.Number)
tsStart := time.Now()
block.Hash = ""
block.Nonce = -1
for done := false; !done; done = strings.HasPrefix(block.Hash, challenge) {
block.Nonce = block.Nonce + 1
block.Hash = calculateHash(block)
}
tsEnd := time.Now()
elapsedTime := tsEnd.Sub(tsStart)
p := message.NewPrinter(language.English)
p.Printf("Valid hash found in %s! (hash: %s, nonce: %d)\n", elapsedTime.String(), block.Hash, block.Nonce)
return block.Hash
}
As you can see in the following execution of our demo, it takes between 10 and 30 seconds to find a valid hash (starting with ‘0000’) for every block. If you try this yourself, you will get different results as the timestamp of a block is taken into consideration for mining it (https://github.com/dplabs/demistifying-blockchain/blob/master/block.go#L51):
$ go run .
Blockchain initialized with challenge '0000'
Mining block 0... Valid hash found in 10.943824518s! (hash: 0000ociyEZOQk8ekRZ5VXcNJPLY=, nonce: 18,158,423)
Mining block 1... Valid hash found in 11.316462656s! (hash: 0000Dk0duKeCCHvDKuyFepXgUNc=, nonce: 15,208,609)
Mining block 2... Valid hash found in 28.805735271s! (hash: 0000O104QmKwfp2tVJ2JowVd9DI=, nonce: 37,840,116)
Resulting blockchain (in reversed order):
Block 2: {
ts: 2018-11-10 15:17:40.602154 +0100 CET m=+22.260933223
nonce: 37840116
hash: 0000O104QmKwfp2tVJ2JowVd9DI=
previous: 0000Dk0duKeCCHvDKuyFepXgUNc=
}
Block 1: {
ts: 2018-11-10 15:17:29.285761 +0100 CET m=+10.944444407
nonce: 15208609
hash: 0000Dk0duKeCCHvDKuyFepXgUNc=
previous: 0000ociyEZOQk8ekRZ5VXcNJPLY=
}
Block 0: {
ts: 2018-11-10 15:17:18.341964 +0100 CET m=+0.000555870
nonce: 18158423
hash: 0000ociyEZOQk8ekRZ5VXcNJPLY=
previous:
}
We encourage you to play with this code. You can download from the repository and compile and execute it yourself: https://github.com/dplabs/demistifying-blockchain
In the following and last post of this series, we will implement some benchmarks to analyze how much time it requires to mine a block based on the difficult we set.