你来这里是因为和我一样, 你对加密货币的崛起感到兴奋. 你想了解区块链是如何工作的, 它们背后的基本技术原理是怎样的.
但了解区块链并不容易, 至少对我来说不是很容易的. 我喜欢边干边学. 它迫使我在代码级别上处理问题, 这种方法可以让我坚持学习下去.
记住, 区块链是一个不可变的, 有顺序的链记录, 我们称之为区块. 它们可以包含交易, 文件或任何你想要的数据. 但重要的是它们是用哈希链接在一起.
这个指南最适合的阅读对象的要求是什么? 至少你轻松地阅读和编写一些基本的 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:
<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):
"""
Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
: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"
- </bool>
- </int>
- </int>
- </int>
- </int>
我们的类接近完成, 我们已经准备好使用 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 进行交互了.
-- 未完待续 --
来源: http://www.tuicool.com/articles/qmQRbm6