The main content of this paper is translated from Learn blockchains by Building one
This article original link, reprint please indicate the source.
The author thinks that the quickest way to learn a blockchain is to create one yourself, and this article follows the author using Python to create a blockchain.
We are curious about the rise of digital money and want to know what the technology behind it--how blockchain is implemented.
But it's not easy to get a complete understanding of the blockchain, I like to learn in practice, and to learn technology by writing code is more solid. By building a blockchain, you can deepen your understanding of the block chain.
Preparatory work
This article requires the reader to have a basic understanding of Python, to read and write basic python, and to have a basic understanding of HTTP requests.
We know that blockchain is an immutable, ordered chain structure made up of chunks of records that can be transactions, files, or any data you want, and it is important that they are linked by a hash value (hashes).
If you don't know the hash yet, you can check out this article
Environment preparation
Environment ready, make sure you have installed python3.6+, Pip, Flask, requests
Installation method:
pip install Flask==0.12.2 requests==2.18.4
An HTTP client, such as Postman,curl or other clients, is also required.
Reference source code (original code when I translated, unable to run, I fork a copy, fixed the error, and added a translation, thank star)
Start creating blockchain
Create a new file blockchain.py, all the code in this article is written in this file, you can refer to the source code at any time
Blockchain class
First, create a blockchain class that creates two lists in the constructor, one for storing the blockchain, and one for storing the transaction.
The following is a framework for the Blockchain class:
class Blockchain(object): def __init__(self): self.chain = [] self.current_transactions = [] def new_block(self): # Creates a new Block and adds it to the chain pass def new_transaction(self): # Adds a new transaction to the list of transactions pass @staticmethod def hash(block): # Hashes a Block pass @property def last_block(self): # Returns the last Block in the chain pass
The blockchain class is used to manage the chain, it can store transactions, add new blocks, and so on, we will further refine these methods.
Block structure
Each chunk contains attributes: Index, UNIX timestamp (timestamp), trade list (transactions), proof of work (explained later), and hash value for the previous chunk.
The following is the structure of a block:
block = { ‘index‘: 1, ‘timestamp‘: 1506057125.900785, ‘transactions‘: [ { ‘sender‘: "8527147fe1f5426f9dd545de4b27ee00", ‘recipient‘: "a77f5cdfa2934df3954a5c7c7da5df1f", ‘amount‘: 5, } ], ‘proof‘: 324984774000, ‘previous_hash‘: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}
Here, the concept of the blockchain is clear, each new block contains the hash of the previous block, which is the key point, it guarantees the block chain immutability. If an attacker destroys a previous chunk, the hash of all subsequent chunks will become incorrect. Do not understand the words, slowly digest, can refer to {% post_link WHATBC block chain accounting principle%}
Join the deal
Next we need to add a deal to refine the next New_transaction method
class Blockchain(object): ... def new_transaction(self, sender, recipient, amount): """ 生成新交易信息,信息将加入到下一个待挖的区块中 :param sender: <str> Address of the Sender :param recipient: <str> Address of the Recipient :param amount: <int> Amount :return: <int> The index of the Block that will hold this transaction """ self.current_transactions.append({ ‘sender‘: sender, ‘recipient‘: recipient, ‘amount‘: amount, }) return self.last_block[‘index‘] + 1
The Add method adds a transaction to the list and returns the index of the chunk (the next chunk to be mined) that the record will be added to, and so on, which is useful when the user submits the transaction.
Create new block
When blockchain is instantiated, we need to construct a Genesis block (no first chunk of the previous block) and add a proof of effort to it.
Each block requires a proof of work, commonly known as mining, and will continue to explain later.
In order to construct the Genesis block, we also need to refine the New_block (), new_transaction () and hash () methods:
Import hashlibimport jsonfrom time import Timeclass Blockchain (object): Def __init__ (self): Self.current_transac tions = [] Self.chain = [] # Create the Genesis Block Self.new_block (previous_hash=1, proof=100) d EF New_block (self, Proof, Previous_hash=none): "" "generates a new block:p Aram Proof: <int> the proof given by The Proof of work algorithm:p Aram Previous_hash: (Optional) <str> hash of previous block:return: < ;d ict> New Block "" "block = {' index ': Len (self.chain) + 1, ' timestamp ': Time (), ' Transactions ': self.current_transactions, ' proof ': proof, ' previous_hash ': Previous_hash or Self.hash (Self.chain[-1]),} # Reset The current list of transactions self.current_transactions = [] self.chain.append (Block) return block def new_transaction (self, sender, recipient, amount): "" " Generate new transaction information that will be added to the nextA block to be dug:p Aram Sender: <str> address of the sender:p Aram recipient: <str> address of the Recipi ENT:p Aram Amount: <int> Amount:return: <int> The index of the Block that would hold this Transa Ction "" "Self.current_transactions.append ({' sender ': sender, ' recipient ': Recipient, ' Amount ': Amount,}) return self.last_block[' index ' + 1 @property def last_block (self): Return self.chain[-1] @staticmethod def hash (block): "" "SHA-256 hash value of the generated block:P Aram Block: <dict> Block:return: <str> "" "# We must make sure that the Dictionary is Ordered, or W E ' ll has inconsistent hashes block_string = Json.dumps (block, sort_keys=true). Encode () return hashlib.sha25 6 (block_string). Hexdigest ()
With the code and comments above you can have a visual understanding of the blockchain, and then we'll look at how chunks are dug out.
Understanding the proof of workload
The new block relies on the proof of effort (PoW) algorithm to construct. The goal of the POW is to find a number that meets certain criteria, which is difficult to calculate, but easy to verify . This is the core idea of the proof of workload.
For ease of understanding, for example:
Suppose that the hash value of an integer x multiplied by the product of another integer y must end with 0, i.e. hash (x * y) = AC23DC ... 0. Set the variable x = 5 to find the value of y?
The following are implemented in Python:
from hashlib import sha256x = 5y = 0 # y未知while sha256(f‘{x*y}‘.encode()).hexdigest()[-1] != "0": y += 1print(f‘The solution is y = {y}‘)
The result is y=21. Because:
hash(5 * 21) = 1253e9373e...5e3600155e860
In Bitcoin, a workload proof algorithm called Hashcash is similar to the problem above. Miners scramble to calculate results for the right to create chunks. In general, the computational difficulty is proportional to the number of specific characters that the target string needs to satisfy, and the miner calculates the result and receives a bitcoin reward.
Of course, it's very easy to verify this result on the web.
Achieve proof of workload
Let's implement a similar POW algorithm, the rule is: look for a number p, so that it and the previous chunk of the proof stitching into a string of hash value starting with 4 0.
import hashlibimport jsonfrom time import timefrom uuid import uuid4class Blockchain(object): ... def proof_of_work(self, last_proof): """ 简单的工作量证明: - 查找一个 p‘ 使得 hash(pp‘) 以4个0开头 - p 是上一个块的证明, p‘ 是当前的证明 :param last_proof: <int> :return: <int> """ proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): """ 验证证明: 是否hash(last_proof, proof)以4个0开头? :param last_proof: <int> Previous Proof :param proof: <int> Current Proof :return: <bool> True if correct, False if not. """ guess = f‘{last_proof}{proof}‘.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == "0000"
The method of measuring the complexity of the algorithm is to modify the number of the beginning of 0. Using 4 for the demo, you will find that one more 0 will greatly increase the time required to calculate the result.
Now that the Blockchain class is basically complete, the next step is to use HTTP requests to interact.
Blockchain as API interface
We'll use the Python flask Framework, a lightweight Web application framework that makes it easy to map network requests to Python functions, and now let's run blockchain on the flask web.
We will create three interfaces:
- /transactions/new Create a transaction and add it to the block
- /mine tells the server to dig up new chunks.
- /chain return the entire blockchain
Create a Node
Our "Flask Server" will act as a node in the blockchain network. Let's add some framework code first:
import hashlibimport jsonfrom textwrap import dedentfrom time import Timefrom UUID Import uuid4from flask Import Flaskclass Blockchain (object): ... # instantiate our Nodeapp = Flask (__name__) # Generate A Globally unique address for this nodenode_identifier = str (UUID4 ()). Replace ('-', ') # instantiate the Blockchainblockchai n = Blockchain () @app. Route ('/mine ', methods=[' GET ') def mine (): Return "We'll mine a new Block" @app. Route ('/transaction S/new ', methods=[' POST ']) def new_transaction (): Return "We ll add a new transaction" @app. Route ('/chain ', methods=[' GET ' ]) def full_chain (): Response = {' Chain ': blockchain.chain, ' length ': Len (Blockchain.chain),} Retu RN Jsonify (response), 200if __name__ = = ' __main__ ': App.run (host= ' 0.0.0.0 ', port=5000)
Simply explain the above code:
Line 15th: Create a node.
Line 18th: Create a random name for the node.
Line 21st: Instance Blockchain class.
Line 24–26: Create the/mine get interface.
Line 28–30: Create the/transactions/new post interface, you can send transaction data to the interface.
Line 32–38: Create the/chain interface and return the entire blockchain.
Line 40–41: The service is running on port 5000.
Send a transaction
The transaction data structure sent to the node is as follows:
{ "sender": "my address", "recipient": "someone else‘s address", "amount": 5}
There have been ways to add transactions, and it's easy to add trades based on the interface.
import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flask, jsonify, request...@app.route(‘/transactions/new‘, methods=[‘POST‘])def new_transaction(): values = request.get_json() # Check that the required fields are in the POST‘ed data required = [‘sender‘, ‘recipient‘, ‘amount‘] if not all(k in values for k in required): return ‘Missing values‘, 400 # Create a new Transaction index = blockchain.new_transaction(values[‘sender‘], values[‘recipient‘], values[‘amount‘]) response = {‘message‘: f‘Transaction will be added to Block {index}‘} return jsonify(response), 201
Digging mine
Digging is amazing, it's simple, and it does three things:
- Calculation of workload Proof POW
- Grant a miner (own) a coin by adding a new transaction
- Construct a new chunk and add it to the chain
import hashlibimport jsonfrom time import timefrom uuid import uuid4from flask import Flask, jsonify, request...@app.route(‘/mine‘, methods=[‘GET‘])def mine(): # We run the proof of work algorithm to get the next proof... last_block = blockchain.last_block last_proof = last_block[‘proof‘] proof = blockchain.proof_of_work(last_proof) # 给工作量证明的节点提供奖励. # 发送者为 "0" 表明是新挖出的币 blockchain.new_transaction( sender="0", recipient=node_identifier, amount=1, ) # Forge the new Block by adding it to the chain block = blockchain.new_block(proof) response = { ‘message‘: "New Block Forged", ‘index‘: block[‘index‘], ‘transactions‘: block[‘transactions‘], ‘proof‘: block[‘proof‘], ‘previous_hash‘: block[‘previous_hash‘], } return jsonify(response), 200
Note that the recipient of the transaction is our own server node, and most of the work we do is simply interacting around the blockchain class method. In this case, our blockchain is completed and we are actually running
Running Blockchain
You can use curl or postman to interact with the API
Start server:
$ python blockchain.py* Runing on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Let's dig the mines by requesting http://localhost:5000/mine.
Add a new transaction via POST request
If you are not using postman, the same Curl statement is used:
$ curl -X POST -H "Content-Type: application/json" -d ‘{ "sender": "d4ee26eee15148ee92c6cd394edd974e", "recipient": "someone-other-address", "amount": 5}‘ "http://localhost:5000/transactions/new"
After digging two mines, there are 3 blocks, and all the block information can be obtained by requesting http://localhost:5000/chain.
{ "chain": [ { "index": 1, "previous_hash": 1, "proof": 100, "timestamp": 1506280650.770839, "transactions": [] }, { "index": 2, "previous_hash": "c099bc...bfb7", "proof": 35293, "timestamp": 1506280664.717925, "transactions": [ { "amount": 1, "recipient": "8bbcb347e0634905b0cac7955bae152b", "sender": "0" } ] }, { "index": 3, "previous_hash": "eff91a...10f2", "proof": 35089, "timestamp": 1506280666.1086972, "transactions": [ { "amount": 1, "recipient": "8bbcb347e0634905b0cac7955bae152b", "sender": "0" } ] } ], "length": 3}
Consistency (consensus)
We already have a basic blockchain that can be traded and mined. But the blockchain system should be distributed. Since it is distributed, what exactly do we take to ensure that all nodes have the same chain? This is the consistency problem, if we want to have multiple nodes on the network, we must implement a consistent algorithm.
Registering nodes
Before implementing the conformance algorithm, we need to find a way for a node to know its neighbors. Each node needs to keep a copy of the records that contain the other nodes in the network. So let's add a few more interfaces:
- /nodes/register a list of new nodes in the form of receiving URLs
- /nodes/resolve performs a consistency algorithm to resolve any conflicts and ensure that the node has the correct chain
We modify the Init function under blockchain and provide a method for registering nodes:
...from urllib.parse import urlparse...class Blockchain(object): def __init__(self): ... self.nodes = set() ... def register_node(self, address): """ Add a new node to the list of nodes :param address: <str> Address of node. Eg. ‘http://192.168.0.5:5000‘ :return: None """ parsed_url = urlparse(address) self.nodes.add(parsed_url.netloc)
We use set to store nodes, which is a simple way to avoid adding nodes repeatedly.
Implementation of consensus algorithm
As mentioned earlier, the conflict refers to different nodes have different chains, in order to solve this problem, the provision of the longest, effective chain is the ultimate chain, in other words, the effective longest chain in the network is the actual chain.
We use the algorithm to reach the consensus in the network.
... import requestsclass Blockchain (object) ... def valid_chain (self, chain): "" "determine if a given Blockchain is valid:p Aram chain: <list> A Blockchain:return: <bool> True If valid, False if no T "" "Last_block = chain[0] Current_index = 1 while Current_index < Len (chain): block = Chain[current_index] Print (f ' {last_block} ') print (f ' {block} ') print ("\ n---------- -\n ") # Check the hash of the block is correct if block[' Previous_hash ']! = Self.hash (last_bloc k): Return False # Check that the Proof of work is correct if not self.valid_proof (LA st_block[' proof '], block[' proof '): return False Last_block = block Current_index + = 1 return True def resolve_conflicts (self): "" "consensus algorithm resolves conflicts using the longest chain in the network. : return: <bool> True If the chain is superseded, otherwise FALSe "" "neighbours = self.nodes New_chain = None # We ' re only looking for chains longer than OU rs max_length = Len (self.chain) # Grab and verify the chains from all the nodes in we network for No De in neighbours:response = Requests.get (f ' http://{node}/chain ') if Response.status_code = = 200: Length = Response.json () [' length '] chain = Response.json () [' Chain '] # Check if The length is longer and the chain is valid if length > Max_length and Self.valid_chain (chain): Max_length = Length New_chain = chain # Replace Our chain if we discovered a new, Val ID chain longer than ours if New_chain:self.chain = New_chain return True return Fals E
The first method, Valid_chain (), is used to check if it is a valid chain, traversing each block to verify hash and proof.
The 2nd method, Resolve_conflicts (), is used to resolve the conflict, traverse all the neighbor nodes, and check the validity of the chain using the previous method, and replace the chain if it finds an effective longer chain .
Let's add two routes, one for registering nodes and one for resolving conflicts.
@app. Route ('/nodes/register ', methods=[' POST ') def register_nodes (): Values = Request.get_json () nodes = Values.get (' nodes ') if nodes is None:return "error:please supply a valid list of Nodes ", for node in Nodes:blockchain.register_node (node) response = {' message ': ' New nodes has Been added ', ' total_nodes ': List (blockchain.nodes),} return Jsonify (response), 201@app.route ('/nodes/resolv E ', methods=[' GET ') def consensus (): replaced = Blockchain.resolve_conflicts () if replaced:response = { ' Message ': ' Our chain is replaced ', ' New_chain ': blockchain.chain} else:response = { ' Message ': ' Our chain is authoritative ', ' Chain ': Blockchain.chain} return Jsonify (Respo NSE),
You can run nodes on different machines, or open different network ports on a single machine to simulate a multi-node network, where a different port demo is opened on the same machine, and a command is run at different terminals, and two nodes are launched: http://localhost:5000 and/http localhost:5001
pipenv run python blockchain.pypipenv run python blockchain.py -p 5001
Then dig two blocks on Node 2, make sure it is a longer chain, and then access the interface/nodes/resolve on Node 1, when the chain of Node 1 is replaced by the chain of Node 2 through the consensus algorithm.
Well, you can invite your friends to test your blockchain.
In-depth blockchain-the system learns blockchain to create the best blockchain technology blog.
Create a blockchain from scratch with Python