前言:深入理解区块链最好的方式莫过于亲手搭建一个,在这个过程中理解它背后的逻辑和原理。本文作者是Danielvan Flymen ,文章来源于hackernoon.com,由蓝狐笔记社群“iGreenMind”翻译。
你来这里是因为和我一样,你对加密货币的崛起感到兴奋。你想了解区块链是如何工作的,它们背后的基本技术原理是怎样的。
但了解区块链并不容易,至少对我来说不是很容易的。我喜欢边干边学。它迫使我在代码级别上处理问题,这种方法可以让我坚持学习下去。
记住,区块链是一个不可变的、有顺序的链记录,我们称之为区块。它们可以包含交易、文件或任何你想要的数据。但重要的是它们是用哈希链接在一起。
这个指南最适合的阅读对象的要求是什么?至少你轻松地阅读和编写一些基本的Python,并了解HTTP请求是如何工作的,因为我们将通过HTTP协议与我们的 Blockchain 进行交互。
需要什么工具和环境?确保安装了Python 3.6+(以及 pip ),还需要安装Flask和Requests库:
你还需要一个HTTP客户端,比如Postman或cURL。可用的源代码请点击:https://github.com/dvf/blockchain
第一步:构建Blockchain
打开你喜欢的文本编辑器或IDE,我比较喜欢使用 PyCharm。然后创建一个名为blockchain.py的新文件。只使用这一个文件,但是如果搞丢了此文件,你可以一直引用源代码:https://github.com/dvf/blockchain
区块链蓝图
我们将创建一个区块链 类,它的构造函数会创建一个初始空列表用于存储区块链,另一个用于存储交易。这是我们创建的区块链class的源码:
1.classBlockchain(object):
2.def__init__(self):
3.self.chain = []
4.self.current_transactions = []
5.
6.defnew_block(self):
7.# Creates a new Block and adds it to the chain
8.pass
9.
10.defnew_transaction(self):
11.# Adds a new transaction to the list of transactions
12.pass
13.
14.@staticmethod
15.defhash(block):
16.# Hashes a Block
17.pass
18.
19.@property
20.deflast_block(self):
21.# Returns the last Block in the chain
22.pass
Blueprint of our Blockchain Class
区块链 class 负责管理链。它将存储交易,并有一些辅助方法来为链添加新的区块。让我们开始充实一些方法。
一个区块会是什么样子?
每个块都有一个索引、一个时间戳(Unix时间)、一个交易列表、一个证明和前一个块的哈希值。
区块源码例子:
1.block = {
2.'index': 1,
3.'timestamp': 1506057125.900785,
4.'transactions': [
5.{
6.'sender':"8527147fe1f5426f9dd545de4b27ee00",
7.'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",
8.'amount': 5,
9.}
10.],
13.}
链的概念应该很明显:每个新块都包含在其内部的前一个块的哈希。这点是至关重要的,因为它使 Blockchain 不可篡改:如果攻击者破坏了链中较早的区块,那么随后所有的块都将包含不正确的哈希值。
请花一些时间好好去理解它——这是区块链设计的的核心理念。
在区块中添加交易
我们需要一种将交易添加到块中的方法。new_transaction() 方法可以实现这个功能,而且非常简单:
1.classBlockchain(object):
2....
3.
4.defnew_transaction(self, sender, recipient, amount):
5."""
6.Creates a new transaction to go into the next mined Block
7.
8.:param sender: Address of the Sender
9.:param recipient: Address of the Recipient
10.:param amount: Amount
11.:return: The index of the Block that will hold this transaction
12."""
13.
14.self.current_transactions.append({
15.'sender': sender,
16.'recipient': recipient,
17.'amount': amount,
18.})
19.
20.returnself.last_block['index'] + 1
在new_transaction()将交易添加到列表之后,它将返回这个交易会被添加到下一个块的索引。这对稍后提交交易的用户有用。
创建新区块
当 区块链被实例化时,需要将它与一个没有前辈的创世区块一起连接起来。我们还需要向我们的创世区块添加一个“证明”,这是挖矿的结果。
除了在我们的构造函数中创建创世区块之外,我们还将为new_block()、new_transaction()和hash()添加方法:
1.importhashlib
2.importjson
3.fromtimeimporttime
4.
5.
6.classBlockchain(object):
7.def__init__(self):
8.self.current_transactions = []
9.self.chain = []
10.
11.# Create the genesis block
12.self.new_block(previous_hash=1, proof=100)
13.
14.defnew_block(self, proof, previous_hash=None):
15."""
16.Create a new Block in the Blockchain
17.
18.:param proof: The proof given by the Proof of Work algorithm
19.:param previous_hash: (Optional) Hash of previous Block
20.:return: New Block
21."""
22.
23.block = {
24.'index': len(self.chain) + 1,
25.'timestamp': time(),
26.'transactions': self.current_transactions,
27.'proof': proof,
28.'previous_hash': previous_hashorself.hash(self.chain[-1]),
29.}
30.
31.# Reset the current list of transactions
32.self.current_transactions = []
33.
34.self.chain.append(block)
35.returnblock
36.
37.defnew_transaction(self, sender, recipient, amount):
38."""
39.Creates a new transaction to go into the next mined Block
40.
41.:param sender: Address of the Sender
42.:param recipient: Address of the Recipient
43.:param amount: Amount
44.:return: The index of the Block that will hold this transaction
45."""
46.self.current_transactions.append({
47.'sender': sender,
48.'recipient': recipient,
49.'amount': amount,
50.})
51.
52.returnself.last_block['index'] + 1
53.
54.@property
55.deflast_block(self):
56.returnself.chain[-1]
57.
58.@staticmethod
59.defhash(block):
60."""
61.Creates a SHA-256 hash of a Block
62.
63.:param block: Block
64.:return:
65."""
66.
67.# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
68.block_string = json.dumps(block, sort_keys=True).encode()
69.returnhashlib.sha256(block_string).hexdigest()
70.
至此,我们几乎完成了 Blockchain 的代码化表现。但新的区块是如何被创建、挖掘的?
理解PoW工作量证明
工作量证明,也就是新的区块如何在 Blockchain 上被创建或挖掘出来。它的目标是发现一个解决问题的数字,这个数字一定很难找到,但却很容易被验证——在网络上的任何人都可以通过计算来验证,这是工作证明PoW背后的核心思想。
我们来看一个非常简单的例子:我们想找到这样一个数值,将整数x与另一个数值y的乘积进行hash运算,使得运算的结果是一串字符串的结尾必须是数字0 。用数学表达式表示出来就是:
hash(x * y) = ac23dc…0
我们假定x = 5。在Python中实现,代码如下:
1.fromhashlibimportsha256
2.x = 5
3.y = 0# We don't know what y should be yet...
4.whilesha256(f''.encode()).hexdigest()[-1] !="0":
5.y += 1
6.print(f'The solution is y = ')
这里的解是y = 21。因为,生成的hash值是以0结尾的:
1.hash(5 * 21) = 1253e9373e...5e3600155e860
在比特币中,工作量证明被称为Hashcash 。它和上面举出的简单例子基本没有太大区别。这是为了创建一个新的区块,矿工们竞相解决问题的算法。一般来说,难度取决于字符串中搜索的字符数。
矿工会因为在一个交易中找到了那个难题的解,而获得系统给出的激励:该网络的一定量的数字货币。该网络能够很容易地验证他们的解是否正确。
实现基本的工作量证明
为区块链实现一个类似的算法,规则与上面类似:找到一个数字p,当与上一个区块的解进行哈希运算时,产生一个前4位都是0的哈希值。
为了调整算法的难度,我们可以修改前几位零的个数。但4个0就足够了。你将发现,添加一个前导零就会对找到解所需的时间造成很大的不同。
1.importhashlib
2.importjson
3.
4.fromtimeimporttime
5.fromuuidimportuuid4
6.
7.
8.classBlockchain(object):
9....
10.
11.defproof_of_work(self, last_proof):
12."""
13.Simple Proof of Work Algorithm:
14.- Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
15.- p is the previous proof, and p' is the new proof
16.
17.:param last_proof:
18.:return:
19."""
20.
21.proof = 0
22.whileself.valid_proof(last_proof, proof)isFalse:
23.proof += 1
24.
25.returnproof
26.
27.@staticmethod
28.defvalid_proof(last_proof, proof):
29."""
30.Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
31.
32.:param last_proof: Previous Proof
33.:param proof: Current Proof
34.:return: True if correct, False if not.
35."""
36.
37.guess = f''.encode()
38.guess_hash = hashlib.sha256(guess).hexdigest()
39.returnguess_hash[:4] =="0000"
我们的类接近完成,我们已经准备好使用HTTP请求开始与它交互。
第二步:将区块链作为API使用起来
使用Python的Flask框架。它是一个微型框架,它可以很容易地将端点映射到Python函数。这让我们使用HTTP请求在web上与 Blockchain 进行交互。
我们将创建三个方法:
/transactions/new 创建一个新的交易到一个区块。
/mine 告诉我们的服务器去挖掘一个新的区块。
/chain 返回完整的 Blockchain 。
设置Flask
我们的“服务器”将在 Blockchain 网络中形成单独节点,创建一些样板代码如下所示:
1.importhashlib
2.importjson
3.fromtextwrapimportdedent
4.fromtimeimporttime
5.fromuuidimportuuid4
6.
7.fromflaskimportFlask
8.
9.
10.classBlockchain(object):
11....
12.
13.
14.# Instantiate our Node
15.app = Flask(__name__)
16.
17.# Generate a globally unique address for this node
18.node_identifier = str(uuid4()).replace('-','')
19.
20.# Instantiate the Blockchain
21.blockchain = Blockchain()
22.
23.
24.@app.route('/mine', methods=['GET'])
25.defmine():
26.return"We'll mine a new Block"
27.
28.@app.route('/transactions/new', methods=['POST'])
29.defnew_transaction():
30.return"We'll add a new transaction"
31.
32.@app.route('/chain', methods=['GET'])
33.deffull_chain():
34.response = {
35.'chain': blockchain.chain,
36.'length': len(blockchain.chain),
37.}
38.returnjsonify(response), 200
39.
40.if__name__ =='__main__':
41.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上运行服务器。
交易端点
这就是交易请求的样子。这是用户发送给服务器的内容:
1.{
2."sender":"my address",
3."recipient":"someone else's address",
4."amount": 5
5.}
由于已经有了将交易添加到区块的类的方法,其余的都很简单。让我们编写添加交易的函数:
1.importhashlib
2.importjson
3.fromtextwrapimportdedent
4.fromtimeimporttime
5.fromuuidimportuuid4
6.
7.fromflaskimportFlask, jsonify, request
8.
9....
10.
11.@app.route('/transactions/new', methods=['POST'])
12.defnew_transaction():
13.values = request.get_json()
14.
15.# Check that the required fields are in the POST'ed data
16.required = ['sender','recipient','amount']
17.ifnotall(kinvaluesforkinrequired):
18.return'Missing values', 400
19.
20.# Create a new Transaction
21.index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
22.
23.response = {'message': f'Transaction will be added to Block '}
24.returnjsonify(response), 201
Amethod for creating Transactions
挖矿端点
挖矿端点必须做三件事:
1.计算工作量证明。
2.通过增加一笔交易,奖赏给矿工(也就是我们自己)一定量的数字货币。
3.通过将新区块添加到链中来锻造区块。
1.importhashlib
2.importjson
3.
4.fromtimeimporttime
5.fromuuidimportuuid4
6.
7.fromflaskimportFlask, jsonify, request
8.
9....
10.
11.@app.route('/mine', methods=['GET'])
12.defmine():
13.# We run the proof of work algorithm to get the next proof...
14.last_block = blockchain.last_block
15.last_proof = last_block['proof']
16.proof = blockchain.proof_of_work(last_proof)
17.
18.# We must receive a reward for finding the proof.
19.# The sender is "0" to signify that this node has mined a new coin.
20.blockchain.new_transaction(
21.sender="0",
22.recipient=node_identifier,
23.amount=1,
24.)
25.
26.# Forge the new Block by adding it to the chain
27.previous_hash = blockchain.hash(last_block)
28.block = blockchain.new_block(proof, previous_hash)
29.
30.response = {
31.'message':"New Block Forged",
32.'index': block['index'],
33.'transactions': block['transactions'],
34.'proof': block['proof'],
35.'previous_hash': block['previous_hash'],
36.}
37.returnjsonify(response), 200
被挖掘出来的区块的接收者是我们节点的地址。在这里所做的大部分工作只是与Blockchain class中的方法进行交互。在这一点上,我们已经完成了,并且可以开始与我们的 Blockchain 进行交互了。
——未完待续——
风险警示:蓝狐所有文章都不构成投资推荐,投资有风险,建议对项目进行深入考察,慎重做好自己的投资决策。
想要深入了解区块链,长按下面二维码关注“蓝狐笔记”区块链公众号:lanhubiji 或加入知识星球:https://t.zsxq.com/iaQNnIq(6月28日到期,建议6月28号后再加入)
领取专属 10元无门槛券
私享最新 技术干货