学习区块链的最好方法是构建一个(上)
深入理解区块链最好的方式莫过于亲手搭建一个,在这个过程中理解它背后的逻辑和原理。
你来这里是因为和我一样,你对加密货币的崛起感到兴奋。你想了解区块链是如何工作的,它们背后的基本技术原理是怎样的。
但了解区块链并不容易,至少对我来说不是很容易的。我喜欢边干边学。它迫使我在代码级别上处理问题,这种方法可以让我坚持学习下去。
记住,区块链是一个不可变的、有顺序的链记录,我们称之为区块。它们可以包含交易、文件或任何你想要的数据。但重要的是它们是用哈希链接在一起。
这个指南最适合的阅读对象的要求是什么?至少你轻松地阅读和编写一些基本的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
(1)区块链蓝图
我们将创建一个区块链 类,它的构造函数会创建一个初始空列表用于存储区块链,另一个用于存储交易。这是我们创建的区块链class 的源码:
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. 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. ],
11. ‘proof’: 324984774000,
12. ‘previous_hash’: “2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”
13. }
链的概念应该很明显:每个新块都包含在其内部的前一个块的哈希。这点是至关重要的,因为它使 Blockchain 不可篡改:如果攻击者破坏了链中较早的区块,那么随后所有的块都将包含不正确的哈希值。
请花一些时间好好去理解它——这是区块链设计的的核心理念。
(2)在区块中添加交易
我们需要一种将交易添加到块中的方法。new_transaction() 方法可以实现这个功能,而且非常简单:
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.
8. :param sender: <str> Address of the Sender
9. :param recipient: <str> Address of the Recipient
10. :param amount: <int> Amount
11. :return: <int> 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. return self.last_block[‘index’] + 1
在new_transaction()将交易添加到列表之后,它将返回这个交易会被添加到下一个块的索引。这对稍后提交交易的用户有用。
(3)创建新区块
当 区块链被实例化时,需要将它与一个没有前辈的创世区块一起连接起来。我们还需要向我们的创世区块添加一个“证明”,这是挖矿的结果。
除了在我们的构造函数中创建创世区块之外,我们还将为new_block()、new_transaction()和hash()添加方法:
1. import hashlib
2. import json
3. from time import time
4.
5.
6. class Blockchain(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. def new_block(self, proof, previous_hash=None):
15. “””
16. Create a new Block in the Blockchain
17.
18. :param proof: <int> The proof given by the Proof of Work algorithm
19. :param previous_hash: (Optional) <str> Hash of previous Block
20. :return: <dict> 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_hash or self.hash(self.chain[-1]),
29. }
30.
31. # Reset the current list of transactions
32. self.current_transactions = []
33.
34. self.chain.append(block)
35. return block
36.
37. def new_transaction(self, sender, recipient, amount):
38. “””
39. Creates a new transaction to go into the next mined Block
40.
41. :param sender: <str> Address of the Sender
42. :param recipient: <str> Address of the Recipient
43. :param amount: <int> Amount
44. :return: <int> 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. return self.last_block[‘index’] + 1
53.
54. @property
55. def last_block(self):
56. return self.chain[-1]
57.
58. @staticmethod
59. def hash(block):
60. “””
61. Creates a SHA-256 hash of a Block
62.
63. :param block: <dict> Block
64. :return: <str>
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. return hashlib.sha256(block_string).hexdigest()
70.
至此,我们几乎完成了 Blockchain 的代码化表现。但新的区块是如何被创建、挖掘的?
(4)理解PoW工作量证明
工作量证明,也就是新的区块如何在 Blockchain 上被创建或挖掘出来。它的目标是发现一个解决问题的数字,这个数字一定很难找到,但却很容易被验证——在网络上的任何人都可以通过计算来验证,这是工作证明PoW背后的核心思想。
我们来看一个非常简单的例子:我们想找到这样一个数值,将整数x与另一个数值y的乘积进行hash运算,使得运算的结果是一串字符串的结尾必须是数字0 。用数学表达式表示出来就是:
hash(x * y) = ac23dc…0
我们假定x = 5。在Python中实现,代码如下:
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}’)
这里的解是y = 21。因为,生成的hash值是以0结尾的:
1. hash(5 * 21) = 1253e9373e…5e3600155e860
在比特币中,工作量证明被称为Hashcash 。它和上面举出的简单例子基本没有太大区别。这是为了创建一个新的区块,矿工们竞相解决问题的算法。一般来说,难度取决于字符串中搜索的字符数。
矿工会因为在一个交易中找到了那个难题的解,而获得系统给出的激励:该网络的一定量的数字货币。该网络能够很容易地验证他们的解是否正确。
(5)实现基本的工作量证明
为区块链实现一个类似的算法,规则与上面类似:找到一个数字p,当与上一个区块的解进行哈希运算时,产生一个前4位都是0的哈希值。
为了调整算法的难度,我们可以修改前几位零的个数。但4个0就足够了。你将发现,添加一个前导零就会对找到解所需的时间造成很大的不同。
1. import hashlib
2. import json
3.
4. from time import time
5. from uuid import uuid4
6.
7.
8. class Blockchain(object):
9. …
10.
11. def proof_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: <int>
18. :return: <int>
19. “””
20.
21. proof = 0
22. while self.valid_proof(last_proof, proof) is False:
23. proof += 1
24.
25. return proof
26.
27. @staticmethod
28. def valid_proof(last_proof, proof):
29. “””
30. Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
31.
32. :param last_proof: <int> Previous Proof
33. :param proof: <int> Current Proof
34. :return: <bool> True if correct, False if not.
35. “””
36.
37. guess = f'{last_proof}{proof}’.encode()
38. guess_hash = hashlib.sha256(guess).hexdigest()
39. return guess_hash[:4] == “0000”
我们的类接近完成,我们已经准备好使用HTTP请求开始与它交互。
第二步:将区块链作为API使用起来
使用Python的Flask框架。它是一个微型框架,它可以很容易地将端点映射到Python函数。这让我们使用HTTP请求在web上与 Blockchain 进行交互。
我们将创建三个方法:
- /transactions/new 创建一个新的交易到一个区块。
- /mine 告诉我们的服务器去挖掘一个新的区块。
- /chain 返回完整的 Blockchain 。
设置Flask
我们的“服务器”将在 Blockchain 网络中形成单独节点,创建一些样板代码如下所示:
1. import hashlib
2. import json
3. from textwrap import dedent
4. from time import time
5. from uuid import uuid4
6.
7. from flask import Flask
8.
9.
10. class Blockchain(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. def mine():
26. return “We’ll mine a new Block”
27.
28. @app.route(‘/transactions/new’, methods=[‘POST’])
29. def new_transaction():
30. return “We’ll add a new transaction”
31.
32. @app.route(‘/chain’, methods=[‘GET’])
33. def full_chain():
34. response = {
35. ‘chain’: blockchain.chain,
36. ‘length’: len(blockchain.chain),
37. }
38. return jsonify(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. import hashlib
2. import json
3. from textwrap import dedent
4. from time import time
5. from uuid import uuid4
6.
7. from flask import Flask, jsonify, request
8.
9. …
10.
11. @app.route(‘/transactions/new’, methods=[‘POST’])
12. def new_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. if not all(k in values for k in required):
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 {index}’}
24. return jsonify(response), 201
Amethod for creating Transactions
挖矿端点
挖矿端点必须做三件事:
(1)计算工作量证明。
(2)通过增加一笔交易,奖赏给矿工(也就是我们自己)一定量的数字货币。
(3)通过将新区块添加到链中来锻造区块。
1. import hashlib
2. import json
3.
4. from time import time
5. from uuid import uuid4
6.
7. from flask import Flask, jsonify, request
8.
9. …
10.
11. @app.route(‘/mine’, methods=[‘GET’])
12. def mine():
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. return jsonify(response), 200
被挖掘出来的区块的接收者是我们节点的地址。在这里所做的大部分工作只是与Blockchain class中的方法进行交互。在这一点上,我们已经完成了,并且可以开始与我们的 Blockchain 进行交互了。
——未完待续——
风险警示:蓝狐所有文章都不构成投资推荐,投资有风险,建议对项目进行深入考察,慎重做好自己的投资决策。
原文作者:Danielvan Flymen
原文地址:hackernoon.com
译者:由蓝狐笔记社群“iGreenMind”翻译
本文由 @蓝狐笔记社群“iGreenMind” 翻译发布于人人都是产品经理。未经许可,禁止转载。
题图来自 Pexels,基于 CC0 协议
- 目前还没评论,等你发挥!