学习区块链的最好方法是构建一个(上)
前言:深入理解区块链最好的方式莫过于亲手搭建一个,在这个过程中理解它背后的逻辑和原理。本文作者是Danielvan Flymen ,文章来源于hackernoon.com,由蓝狐笔记社群“iGreenMind”翻译。
你来这里是因为和我一样,你对加密货币的崛起感到兴奋。你想了解区块链是如何工作的,它们背后的基本技术原理是怎样的。
但了解区块链并不容易,至少对我来说不是很容易的。我喜欢边干边学。它迫使我在代码级别上处理问题,这种方法可以让我坚持学习下去。
记住,区块链是一个不可变的、有顺序的链记录,我们称之为区块。它们可以包含交易、文件或任何你想要的数据。但重要的是它们是用哈希链接在一起。
这个指南最适合的阅读对象的要求是什么?至少你轻松地阅读和编写一些基本的Python,并了解HTTP请求是如何工作的,因为我们将通过HTTP协议与我们的 Blockchain 进行交互。
需要什么工具和环境?确保安装了Python 3.6+(以及 pip ),还需要安装Flask和Requests库:
pip install Flask==0.12.2 requests==2.18.4
你还需要一个HTTP客户端,比如Postman或cURL。可用的源代码请点击:https://github.com/dvf/blockchain
第一步:构建Blockchain
打开你喜欢的文本编辑器或IDE,我比较喜欢使用 PyCharm。然后创建一个名为blockchain.py的新文件。只使用这一个文件,但是如果搞丢了此文件,你可以一直引用源代码: https://github.com/dvf/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
区块链 class 负责管理链。它将存储交易,并有一些辅助方法来为链添加新的区块。让我们开始充实一些方法。
一个区块会是什么样子?
每个块都有一个索引、一个时间戳(Unix时间)、一个交易列表、一个证明和前一个块的哈希值。
区块源码例子:
block = { 'index': 1, 'timestamp': 1506057125.900785, 'transactions': [ { 'sender': "8527147fe1f5426f9dd545de4b27ee00", 'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f", 'amount': 5, } ], 'proof': 324984774000, 'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" }
链的概念应该很明显:每个新块都包含在其内部的前一个块的哈希。 这点是至关重要的,因为它使 Blockchain 不可篡改
:如果攻击者破坏了链中较早的区块,那么随后所有的块都将包含不正确的哈希值。
请花一些时间好好去理解它——这是区块链设计的的核心理念。
在区块中添加交易
我们需要一种将交易添加到块中的方法。new_transaction() 方法可以实现这个功能,而且非常简单:
class Blockchain(object): ... def new_transaction(self, sender, recipient, amount): """ Creates a new transaction to go into the next mined Block :param sender: Address of the Sender :param recipient: Address of the Recipient :param amount: Amount :return: 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
在new_transaction()将交易添加到列表之后,它将返回这个交易会被添加到下一个块的索引。这对稍后提交交易的用户有用。
创建新区块
当 区块链被实例化时,需要将它与一个没有前辈的创世区块一起连接起来。我们还需要向我们的创世区块添加一个“证明”,这是挖矿的结果。
除了在我们的构造函数中创建创世区块之外,我们还将为new_block()、new_transaction()和hash()添加方法:
import hashlib import json from time import time class Blockchain(object): def __init__(self): self.current_transactions = [] self.chain = [] # Create the genesis block self.new_block(previous_hash=1, proof=100) def new_block(self, proof, previous_hash=None): """ Create a new Block in the Blockchain :param proof: The proof given by the Proof of Work algorithm :param previous_hash: (Optional) Hash of previous Block :return: 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): """ Creates a new transaction to go into the next mined Block :param sender: Address of the Sender :param recipient: Address of the Recipient :param amount: Amount :return: 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 @property def last_block(self): return self.chain[-1] @staticmethod def hash(block): """ Creates a SHA-256 hash of a Block :param block: Block :return: """ # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest()
至此,我们几乎完成了 Blockchain 的代码化表现。但新的区块是如何被创建、挖掘的?
理解PoW工作量证明
工作量证明,也就是新的区块如何在 Blockchain 上被创建或挖掘出来。它的目标是发现一个解决问题的数字,这个数字一定 很难找到,但却很容易被验证
——在网络上的任何人都可以通过计算来验证,这是工作证明PoW背后的核心思想。
我们来看一个非常简单的例子:我们想找到这样一个数值,将整数x与另一个数值y的乘积进行hash运算,使得运算的结果是一串字符串的结尾必须是数字0 。用数学表达式表示出来就是:
hash(x * y) = ac23dc…0
我们假定x = 5。在Python中实现,代码如下:
from hashlib import sha256 x = 5 y = 0 # We don't know what y should be yet... while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0": y += 1 print(f'The solution is y = {y}')
这里的解是y = 21。因为,生成的hash值是以0结尾的:
hash(5 * 21) = 1253e9373e...5e3600155e860
在比特币中,工作量证明被称为Hashcash 。它和上面举出的简单例子基本没有太大区别。这是为了创建一个新的区块,矿工们竞相解决问题的算法。一般来说,难度取决于字符串中搜索的字符数。
矿工会因为在一个交易中找到了那个难题的解,而获得系统给出的激励:该网络的一定量的数字货币。该网络能够很容易地验证他们的解是否正确。
实现基本的工作量证明
为区块链实现一个类似的算法,规则与上面类似:找到一个数字p,当与上一个区块的解进行哈希运算时,产生一个前4位都是0的哈希值。
为了调整算法的难度,我们可以修改前几位零的个数。但4个0就足够了。你将发现,添加一个前导零就会对找到解所需的时间造成很大的不同。
import hashlib import json from time import time from uuid import uuid4 class Blockchain(object): ... def proof_of_work(self, last_proof): """ Simple Proof of Work Algorithm: - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p' - p is the previous proof, and p' is the new proof :param last_proof: :return: """ proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): """ Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes? :param last_proof: Previous Proof :param proof: Current Proof :return: True if correct, False if not. """ guess = f'{last_proof}{proof}'.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == "0000"
我们的类接近完成,我们已经准备好使用HTTP请求开始与它交互。
第二步:将区块链作为API使用起来
使用Python的Flask框架。它是一个微型框架,它可以很容易地将端点映射到Python函数。这让我们使用HTTP请求在web上与 Blockchain 进行交互。
我们将创建三个方法:
-
/transactions/new 创建一个新的交易到一个区块。
-
/mine 告诉我们的服务器去挖掘一个新的区块。
-
/chain 返回完整的 Blockchain 。
设置Flask
我们的“服务器”将在 Blockchain 网络中形成单独节点,创建一些样板代码如下所示:
import hashlib import json from textwrap import dedent from time import time from uuid import uuid4 from flask import Flask class Blockchain(object): ... # Instantiate our Node app = Flask(__name__) # Generate a globally unique address for this node node_identifier = str(uuid4()).replace('-', '') # Instantiate the Blockchain blockchain = Blockchain() @app.route('/mine', methods=['GET']) def mine(): return "We'll mine a new Block" @app.route('/transactions/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), } return jsonify(response), 200 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
关于在上面代码中添加的内容的简要说明如下:
-
Line 15: 实例化节点。
-
Line 18: 为我们的节点创建一个随机名称。
-
Line 21: 实例化我们的Blockchain类。
-
Line 24–26: 创建/mine 端点,这是一个GET请求。
-
Line 28–30: 创建 /transactions/new 端点,这是一个POST 请求,因为我们将向它发送数据。
-
Line 32–38: 创建/chain端点,它返回完整的 Blockchain 。
-
Line 40–41: 在端口5000上运行服务器。
交易端点
这就是交易请求的样子。这是用户发送给服务器的内容:
{ "sender": "my address", "recipient": "someone else's address", "amount": 5 }
由于已经有了将交易添加到区块的类的方法,其余的都很简单。让我们编写添加交易的函数:
import hashlib import json from textwrap import dedent from time import time from uuid import uuid4 from 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
挖矿端点
挖矿端点必须做三件事:
-
计算工作量证明。
-
通过增加一笔交易,奖赏给矿工(也就是我们自己)一定量的数字货币。
-
通过将新区块添加到链中来锻造区块。
import hashlib import json from time import time from uuid import uuid4 from 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) # We must receive a reward for finding the proof. # The sender is "0" to signify that this node has mined a new coin. blockchain.new_transaction( sender="0", recipient=node_identifier, amount=1, ) # Forge the new Block by adding it to the chain previous_hash = blockchain.hash(last_block) block = blockchain.new_block(proof, previous_hash) response = { 'message': "New Block Forged", 'index': block['index'], 'transactions': block['transactions'], 'proof': block['proof'], 'previous_hash': block['previous_hash'], } return jsonify(response), 200
被挖掘出来的区块的接收者是我们节点的地址。在这里所做的大部分工作只是与Blockchain class中的方法进行交互。在这一点上,我们已经完成了,并且可以开始与我们的 Blockchain 进行交互了。
——未完待续——
新闻来源
CryptoCurrency | CNY | Change 1h | Change 24h | Change 7d |
---|---|---|---|---|
Bitcoin | 0.58 % | 1.10 % | 12.10 % | |
Ethereum | 0.74 % | 4.71 % | 7.76 % | |
Tether | --- | 0.03 % | 0.26 % | |
USD Coin | 0.19 % | 0.45 % | 0.44 % | |
BNB | 2.20 % | 3.04 % | 26.90 % | |
Binance USD | 0.38 % | 0.60 % | 0.69 % | |
Cardano | 0.42 % | 4.87 % | 0.22 % | |
XRP | 0.74 % | 0.44 % | 13.05 % | |
Solana | 1.27 % | 1.81 % | 28.37 % | |
Polkadot | 0.08 % | 15.21 % | 37.87 % | |
Dogecoin | 0.07 % | 5.76 % | 12.58 % | |
Dai | 0.16 % | 0.37 % | 0.52 % | |
Shiba Inu | 2.36 % | 8.57 % | 35.74 % | |
TRON | 0.04 % | 0.43 % | 3.05 % | |
Wrapped Bitcoin | 0.89 % | 1.38 % | 3.42 % | |
Avalanche | 1.24 % | 9.58 % | 126.95 % | |
LEO Token | 0.33 % | 1.29 % | 7.54 % | |
Lido Staked Ether | 3.41 % | 12.96 % | 26.05 % | |
Litecoin | 1.07 % | 7.23 % | 35.68 % | |
FTX Token | 3.93 % | 19.20 % | 58.37 % | |
Chainlink | 0.87 % | 1.57 % | 4.71 % | |
Polygon | 0.79 % | 0.08 % | 13.45 % | |
Crypto.com Coin | 0.20 % | 4.39 % | 9.05 % | |
Stellar | 0.41 % | 6.68 % | 0.28 % | |
OKB | 1.75 % | 9.87 % | 78.32 % | |
Near | 0.11 % | 6.96 % | 68.08 % | |
Algorand | 1.63 % | 5.73 % | 6.03 % | |
Bitcoin Cash | 0.17 % | 13.74 % | 49.71 % | |
Ethereum Classic | 0.65 % | 2.71 % | 3.48 % | |
Monero | 0.15 % | 7.91 % | 5.83 % |