Learn Blockchains by Building One by Daniel Van Flymen
Slightly cut and changed. Original address: hackernoon.com/learn-block…
You’re as intrigued as I am by the rise of digital currencies and wondering how the technology behind them, blockchain, works.
But understanding blockchain is not easy, at least for me. I was frustrated by hard to understand videos, buggy tutorials, and the lack of examples.
I like to learn by doing, and learning by writing code makes my skills stronger. If you do the same, by the end of this article you will have a working blockchain and a deep understanding of blockchain.
Before we begin…
The first thing you need to know is that a blockchain is an immutable, ordered chain of records called blocks. These records can be transactions, files, or whatever data you want, and most importantly they’re Hash together.
If you don’t know the Hash, here is an example learncryptography.com/hash-functi…
Second, you need to install Python3.6+, Flask, Request
PIP install Flask = = 0.12.2 requests = = 2.18.4Copy the code
You also need an HTTP client, such as Postman, cURL, or any other client.
The final source code is here: github.com/dvf/blockch…
Step 1: Build a Blockchain
Create a new file blockchain.py where all the code for this article is written. First we create a Blockchain class. In the constructor we create two lists, one for storing the Blockchain and one for storing transactions.
1 class Blockchain(object):
2 def __init__(self):
3 self.chain = []
4 self.current_transactions = []
5
6 def new_block(self):
7 # Creates a new Block and adds it to the chain
8 pass
9
10 def new_transaction(self):
11 # Adds a new transaction to the list of transactions
12 pass
13
14 @staticmethod
15 def hash(block):
16 # Hashes a Block
17 pass
18
19 @property
20 def last_block(self):
21 # Returns the last Block in the chain
22 passCopy the code
A block has five basic attributes: index, TIMESTAMP (in Unix time), transaction list, proof of work (explained later), and the Hash value of the previous block.
1 block = {
2 'index': 1.. 3 'timestamp': 1506057125.900785. 4 'transactions': [
5 {
6 'sender': "8527147fe1f5426f9dd545de4b27ee00". 7 'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f". 8 'amount': 5. 9 }
10 ].11 'proof': 324984774000.12 'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
13 }Copy the code
At this point, the concept of blockchain should be clear: each new block contains the Hash value of the previous block. This is crucial because it is the fundamental guarantee of the immutability of blockchain. If an attacker breaks one of the previous blocks, all subsequent blocks will Hash incorrectly. Don’t understand? Digest slowly ~
We need a way to add transactions to the block:
1 class Blockchain(object):
2 . 3
4 def new_transaction(self, sender, recipient, amount):
5 "" "
6 Creates a new transaction to go into the next mined Block
7 :param sender: <str> Address of the Sender
8 :param recipient: <str> Address of the Recipient
9 :param amount: <int> Amount
10 :return: <int> The index of the Block that will hold this transaction
11 "" "
12
13 self.current_transactions.append({
14 'sender': sender,
15 'recipient': recipient,
16 'amount': amount,
17 })
18
19 return self.last_block['index'] + 1Copy the code
The new_TRANSACTION () method adds a transaction record to the list and returns an index of the block to which the record will be added — the next block to be mined — which will be useful later when the user submits the transaction.
After Blockchain is instantiated, we need to create an initial block (genesis Block) and preset it with a proof of work.
In addition to adding the creation block code, we need to add the new_block(), new_transaction(), and hash() methods:
1 import hashlib
2 import json
3 from time import time
4
5 class Blockchain(object):
6 def __init__(self):
7 self.current_transactions = []
8 self.chain = []
9
10 # Create the genesis block
11 self.new_block(previous_hash=1, proof=100)
12
13 def new_block(self, proof, previous_hash=None):
14 block = {
15 'index': len(self.chain) + 1.16 'timestamp': time(),
17 'transactions': self.current_transactions,
18 'proof': proof,
19 'previous_hash': previous_hash or self.hash(self.chain[-1]),
20 }
21
22 # Reset the current list of transactions
23 self.current_transactions = []
24
25 self.chain.append(block)
26 return block
27
28 def new_transaction(self, sender, recipient, amount):
29 self.current_transactions.append({
30 'sender': sender,
31 'recipient': recipient,
32 'amount': amount,
33 })
34
35 return self.last_block['index'] + 1
36
37 @property
38 def last_block(self):
39 return self.chain[-1]
40
41 @staticmethod
42 def hash(block):
43 block_string = json.dumps(block, sort_keys=True).encode()
44 return hashlib.sha256(block_string).hexdigest()Copy the code
The above code should be intuitive, we basically have a prototype blockchain. But at this point you’ll be wondering exactly how a block is created or mined.
The new block comes from the proof-of-work (PoW) algorithm. The goal of PoW is to calculate a number that meets certain conditions, which must be computationally difficult for all but easy to verify. That’s the core idea of proof of work.
Here’s an example:
Suppose the Hash value of the product of an integer x times another integer y must end in 0, i.e. Hash (x * y) = ac23dc… 0. Let’s say x is equal to 5. What’s y?
1 from hashlib import sha256
2 x = 5
3 y = 0 # We don't know what y should be yet...
4 while sha256(f'{x*y}'.encode()).hexdigest()[-1] ! ="0":
5 y += 1
6 print(f'The solution is y = {y}')Copy the code
Y = 21 // hash(5 * 21) = 1253e9373e… 5e3600155e860
In Bitcoin, the proof-of-work algorithm is called Hashcash, and it is similar to the above problem, except it is very difficult to calculate. That’s where the miners scramble to calculate for the right to create blocks. Usually, the difficulty of the calculation is proportional to the number of specific characters that the target string needs to satisfy, and the miner will be rewarded with a certain amount of Bitcoin (through a transaction) after the result is calculated.
It is, of course, very easy for the network to verify the results.
Let’s implement a PoW algorithm, very similar to the example above, that looks for a number p that starts with four zeros as the Hash value of the string concatenated with the previous block’s proof.
1 import hashlib
2 import json
3 from time import time
4 from uuid import uuid4
5
6 class Blockchain(object):
7 . 8
9 def proof_of_work(self, last_proof):
10 proof = 0
11 while self.valid_proof(last_proof, proof) is False:
12 proof += 1
13
14 return proof
15
16 @staticmethod
17 def valid_proof(last_proof, proof):
18 guess = f'{last_proof}{proof}'.encode()
19 guess_hash = hashlib.sha256(guess).hexdigest()
20 return guess_hash[:4] == "0000"Copy the code
One way to measure the complexity of an algorithm is to change the number of zeros. Four zeros is enough for the demonstration, and you’ll see that even one more zero will greatly increase the time it takes to compute the result.
Our Blockchain is almost complete, and we’ll use HTTP Requests to interact with it.
Step 2: Blockchain as API
We’ll use the Flask framework, which is lightweight and makes it easy to map network requests to Python functions.
We will create three interfaces:
/transactions/new Creates a transaction and adds it to the block
/mine tells the server to mine a new block
/chain returns the entire blockchain
Our server will act as a node in the blockchain network. Let’s start by adding some general code:
1 import hashlib
2 import json
3 from textwrap import dedent
4 from time import time
5 from uuid import uuid4
6 from flask import Flask, jsonify, request
7
8 class Blockchain(object):
9 .10
11 # Instantiate our Node
12 app = Flask(__name__)
13
14 # Generate a globally unique address for this node
15 node_identifier = str(uuid4()).replace('-'.' ')
16
17 # Instantiate the Blockchain
18 blockchain = Blockchain()
19
20 @app.route('/mine', methods=['GET'])
21 def mine():
22 return "We'll mine a new Block"
23
24 @app.route('/transactions/new', methods=['POST'])
25 def new_transaction():
26 return "We'll add a new transaction"
27
28 @app.route('/chain', methods=['GET'])
29 def full_chain():
30 response = {
31 'chain': blockchain.chain,
32 'length': len(blockchain.chain),
33 }
34 return jsonify(response), 200
35
36 if __name__= ='__main__':
37 app.run(host='127.0.0.1', port=5000)Copy the code
This is the request sent to the server when the user initiates a transaction:
1 {
2 "sender": "my address".3 "recipient": "someone else's address".4 "amount": 5
5 }Copy the code
We already have the method to add transactions to the block, so the rest is simple:
1 @app.route('/transactions/new', methods=['POST'])
2 def new_transaction():
3 values = request.get_json()
4
5 # Check that the required fields are in the POST'ed data
6 required = ['sender'.'recipient'.'amount']
7 if not all(k in values for k in required):
8 return 'Missing values', 400, 9
10 # Create a new Transaction
11 index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
12
13 response = {'message': f'Transaction will be added to Block {index}'}
14 return jsonify(response), 201Copy the code
Digging straight is where the magic happens, and it does just three things: calculate PoW; A new transaction grants miners a set amount of Bitcoin; Construct a new block and add it to the blockchain.
1 @app.route('/mine', methods=['GET'])
2 def mine():
3 # We run the proof of work algorithm to get the next proof...
4 last_block = blockchain.last_block
5 last_proof = last_block['proof']
6 proof = blockchain.proof_of_work(last_proof)
7
8 # We must receive a reward for finding the proof.
9 # The sender is "0" to signify that this node has mined a new coin.
10 blockchain.new_transaction(
11 sender="0".12 recipient=node_identifier,
13 amount=1.14 )
15
16 # Forge the new Block by adding it to the chain
17 block = blockchain.new_block(proof)
18
19 response = {
20 'message': "New Block Forged".21 'index': block['index'].22 'transactions': block['transactions'].23 'proof': block['proof'].24 'previous_hash': block['previous_hash'].25 }
26 return jsonify(response), 200Copy the code
Note that the recipient of the transaction is our own server node, and most of what we do right now is just interaction around the Blockchain class. At this point, our blockchain is complete.
Step 3: Interactive presentation
Use Postman to demonstrate, skip.
Step 4: Consistency
It’s really great that we already have a basic blockchain to add transactions and mining. However, the entire blockchain system must be distributed. Since it is distributed, how on earth can we guarantee that all nodes run on the same chain? This is the problem of consistency, and if we want to add new nodes to the network, we have to implement algorithms that guarantee consistency.
Before implementing the consistency algorithm, we need to find a way for a node to know its neighbors. Each node needs to keep a record of other nodes in the network. Let’s add some new interfaces:
1. / Nodes /register Receives a list of new nodes in the form of URLS
2. / Nodes /resolve is used to execute the consistency algorithm to resolve any conflicts and ensure that the nodes have the correct chain
1 . 2 from urllib.parse import urlparse
3 . 4
5 class Blockchain(object):
6 def __init__(self):
7 . 8 self.nodes = set()
9 .10
11 def register_node(self, address):
12 parsed_url = urlparse(address)
13 self.nodes.add(parsed_url.netloc)Copy the code
Note that we use set to store nodes, which is an easy way to avoid adding nodes repeatedly.
The conflict mentioned above means that different nodes have different chains. To solve this problem, we stipulate that the longest compliant chain is the most effective chain. In other words, only the longest and compliant chain is the actual chain.
Let’s add two more methods, one for adding adjacent nodes and one for resolving conflicts.
1 . 2 import requests
3
4 class Blockchain(object)
5 . 6
7 def valid_chain(self, chain):
8 last_block = chain[0]
9 current_index = 1
10
11 while current_index < len(chain):
12 block = chain[current_index]
13 print(f'{last_block}')
14 print(f'{block}')
15 print("\n-----------\n")
16 # Check that the hash of the block is correct
17 if block['previous_hash'] != self.hash(last_block):
18 return False
19
20 # Check that the Proof of Work is correct
21 if not self.valid_proof(last_block['proof'], block['proof') :22 return False
23
24 last_block = block
25 current_index += 1
26
27 return True
28
29 def resolve_conflicts(self):
30 neighbours = self.nodes
31 new_chain = None
32
33 # We're only looking for chains longer than ours
34 max_length = len(self.chain)
35
36 # Grab and verify the chains from all the nodes in our network
37 for node in neighbours:
38 response = requests.get(f'http://{node}/chain')
39
40 if response.status_code == 200:
41 length = response.json()['length']
42 chain = response.json()['chain']
43
44 # Check if the length is longer and the chain is valid
45 if length > max_length and self.valid_chain(chain):
46 max_length = length
47 new_chain = chain
48
49 # Replace our chain if we discovered a new, valid chain longer than ours
50 if new_chain:
51 self.chain = new_chain
52 return True
53
54 return FalseCopy the code
Now you can start a new machine, or turn on different network interfaces on your own machine to simulate a multi-node network, or invite some friends to test your blockchain.
I hope this article inspires you to create something new. I’m fascinated by digital currencies because I believe blockchain will soon change the way we look at things, including the economy, government, archives management, and more.
What is bitcoin
Pay attention to my
Collect the paper
Vue. Js computes the secret of attributes