Hands-On Blockchain for Python Developers
上QQ阅读APP看书,第一时间看更新

Consensus

As we can see, the hashing function makes history tampering hard, but not too hard. Even if we have a blockchain that consists of 1000 blocks, it would be trivial to alter the content of the first block and change the 999 parent hashes on the other blocks with recent computers. So, to ensure that bad people cannot alter the history (or at least make it very hard), we distribute this append-only database to everyone who wants to keep it (let's call them miners). Say there are ten miners. In this case, you cannot just alter the blockchain in your copy because the other nine miners who would scold, saying something like hey, our records say history A but your record says B. In this case, the majority wins.

However, consensus is not just a case of choosing which blockchain has been chosen by the majority. The problem starts when we want to add a new block to the blockchain. Where do we start? How do we do it? The answer is that we broadcast. When we broadcast the candidate block that contains a new transaction, it will not reach every miner at the same time. You may reach the miner that stands beside you, but it will require time for your message to reach the miner that stands far away from you.

Here's where it gets interesting: the miner that stands far away from you may receive another new candidate block first. So, how do we synchronize all these things and make sure that the majority will have the same blockchain? The simple rule is to choose the longest chain. So if you are a miner in the middle, you may receive two different candidate blocks at the same time, as shown in the following figure:

You get this from the West side:

block_E = Block()
block_E.id = 5
block_E.history = 'Sherly likes fish'
block_E.parent_id = block_D.id

And you get this from the East side:

block_E = Block()
block_E.id = 5
block_E.history = 'Johny likes shrimp'
block_E.parent_id = block_D.id

So, we will keep both versions of block_E. Our blockchain now has a branch. However, in a short time, other blocks have arrived from the East side. Here is the situation now:

This is from the West side:

block_E = Block()
block_E.id = 5
block_E.history = 'Sherly likes fish'
block_E.parent_id = block_D.id

This is from the East side:

block_E = Block()
block_E.id = 5
block_E.history = 'Johny likes shrimp'
block_E.parent_id = block_D.id

block_F = Block()
block_F.id = 6
block_F.history = 'Marie hates shark'
block_F.parent_id = block_E.id

block_G = Block()
block_G.id = 7
block_G.history = 'Sarah loves dog'
block_G.parent_id = block_F.id

By this point, we can get rid of the West side version of the blockchain because we chose the longer version.

Here comes the problem. Say Sherly hates sharks but Sherly wants to get votes from a district where most people only vote for a candidate who loves sharks. To get more votes, Sherly broadcasts a block containing the following lie:

block_E = Block()
block_E.id = 5
block_E.history = 'Sherly loves shark'
block_E.parent_id = block_D.id

All is fine and dandy. The voting session takes one day. After one day has passed, the blockchain has gotten another two blocks:

block_E = Block()
block_E.id = 5
block_E.history = 'Sherly loves shark'
block_E.parent_id = block_D.id

block_F = Block()
block_F.id = 6
block_F.history = 'Lin Dan hates crab'
block_F.parent_id = block_E.id

block_G = Block()
block_G.id = 7
block_G.history = 'Bruce Wayne loves bat'
block_G.parent_id = block_F.id

The following figure illustrates the three blocks:

Now, Sherly needs to get votes from another district where most people only vote for candidates who hate sharks. So, how can Sherly tamper with the blockchain to make this work in her favor? Sherly could broadcast four blocks!

block_E = Block()
block_E.id = 5
block_E.history = 'Sherly hates shark'
block_E.parent_id = block_D.id

block_F = Block()
block_F.id = 6
block_F.history = 'Sherly loves dog'
block_F.parent_id = block_E.id

block_G = Block()
block_G.id = 7
block_G.history = 'Sherly loves turtle'
block_G.parent_id = block_F.id

block_H = Block()
block_H.id = 8
block_H.history = 'Sherly loves unicorn'
block_H.parent_id = block_G.id

The following figure illustrates the four blocks:

The miner will choose the blockchain from Sherly instead of the previous blockchain they kept, which contains the history of Sherly loves sharks. So, Sherly has been able to change the history. This is what we call a double-spending attack.

We can prevent this through proof of work (an incentive for adding blocks). We explained proof of work earlier in this chapter, but we haven't explained the incentive system yet. An incentive means that if the miner successfully adds a new block to the blockchain, the system gives them a digital reward. We can integrate it into the code as follows:

import hashlib

payload = b'{"history": "Sky loves turtle", "parent_id": 3, "id": 4}'
for i in range(10000000):
nonce = str(i).encode('utf-8')
result = hashlib.sha256(payload + nonce).hexdigest()
if result[0:5] == '00000':
// We made it, time to claim the prize
reward[miner_id] += 1
print(i)
print(result)
break

If Sherly wants to alter the history (by replacing some blocks), she needs to spend some resources by solving four puzzles in a short time. By the times she finishes doing this, the blockchain kept by the most miners would have likely added more blocks, making it longer than Sherly's blockchain.

This is the case because most miners want to get that reward we spoke of in the most efficient manner possible. To do this, they would get a new candidate block, work hard to find the answer in proof of work, and then add it to the longest chain as quickly as possible. But, why do they want to add it to the longest chain and not another chain? This is because it secures their reward.

Say we have two versions of the blockchain. One has three blocks, while the other has eight blocks. The most sensible way to add a new block is to add it to the blockchain that has eight blocks. If someone adds it to the blockchain that has three blocks, it is more likely to get discarded. Consequently, the reward would be taken away from the miner. The longest chain attracts the most miners anyway, and you want to be in the blockchain version that is kept by more people.

Some miners could persist in adding the block to the blockchain with three blocks, while other miners could also persist in adding the block to the blockchain with eight blocks. We call this a hard fork. Most of the time, miners will stick to the blockchain that has the longest chain.

To change the history, Sherly will need to outgun at least more than 50% of the miners, which is impossible. The older the block, the more secure the history in that block is. Say one person needs 5 minutes to do the puzzle work. In this case, to replace the last five blocks in the blockchain, Sherly needs more than 25 minutes (because Sherly needs at least six blocks to convince miners to replace the last five blocks in their blockchain). But in those 25 minutes, other miners would keep adding new blocks to the most popular blockchain. So when 25 minutes have passed, the most popular blockchain would have gained an additional five blocks! Maybe the miners take a nap for an hour and don't add any more blocks. In this case, Sherly could accumulate six blocks to tamper with the most popular blockchain. However, the incentive embedded in the blockchain keeps the miners awake 24/7 as they want to get the reward as much as possible. Consequently, it's a losing battle for Sherly.