学习区块链的最好方法是构建一个(上)

前言:深入理解区块链最好的方式莫过于亲手搭建一个,在这个过程中理解它背后的逻辑和原理。本文作者是 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 = []
5.

def new_block(self):
Creates a new Block and adds it to the chain
pass
9.

def new_transaction(self):
Adds a new transaction to the list of transactions
pass
13.

@staticmethod
def hash(block):
Hashes a Block
pass
18.

@property
def last_block(self):
Returns the last Block in the chain
pass
Blueprint of our Blockchain Class

区块链 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):

3.

def new_transaction(self, sender, recipient, amount):
“””
Creates a new transaction to go into the next mined Block
7.

: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
“””
13.

self.current_transactions.append({
‘sender’: sender,
‘recipient’: recipient,
‘amount’: amount,
})
19.

return self.last_block[‘index’] + 1
在 new_transaction() 将交易添加到列表之后,它将返回这个交易会被添加到下一个块的索引。这对稍后提交交易的用户有用。

创建新区块

**
**

当 区块链被实例化时,需要将它与一个没有前辈的创世区块一起连接起来。我们还需要向我们的创世区块添加一个“证明”,这是挖矿的结果。

除了在我们的构造函数中创建创世区块之外,我们还将为 new_block()、new_transaction() 和 hash() 添加方法:

import hashlib
import json
from time import time
4.

5.

class Blockchain(object):
def init(self):
self.current_transactions = []
self.chain = []
10.

Create the genesis block
self.new_block(previous_hash=1, proof=100)
13.

def new_block(self, proof, previous_hash=None):
“””
Create a new Block in the Blockchain
17.

:param proof: The proof given by the Proof of Work algorithm
:param previous_hash: (Optional) Hash of previous Block
:return: New Block
“””
22.

block = {
‘index’: len(self.chain) + 1,
‘timestamp’: time(),
‘transactions’: self.current_transactions,
‘proof’: proof,
‘previous_hash’: previous_hash or self.hash(self.chain[-1]),
}
30.

Reset the current list of transactions
self.current_transactions = []
33.

self.chain.append(block)
return block
36.

def new_transaction(self, sender, recipient, amount):
“””
Creates a new transaction to go into the next mined Block
40.

: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,
})
51.

return self.last_block[‘index’] + 1
53.

@property
def last_block(self):
return self.chain[-1]
57.

@staticmethod
def hash(block):
“””
Creates a SHA-256 hash of a Block
62.

:param block: Block
:return:
“””
66.

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()
70.

至此,我们几乎完成了 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
3.

from time import time
from uuid import uuid4
6.

7.

class Blockchain(object):

10.

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
16.

:param last_proof:
:return:
“””
20.

proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1
24.

return proof
26.

@staticmethod
def valid_proof(last_proof, proof):
“””
Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
31.

:param last_proof: Previous Proof
:param proof: Current Proof
:return: True if correct, False if not.
“””
36.

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
6.

from flask import Flask
8.

9.

class Blockchain(object):

12.

13.

Instantiate our Node
app = Flask(name)
16.

Generate a globally unique address for this node
node_identifier = str(uuid4()).replace(‘-‘, ‘’)
19.

Instantiate the Blockchain
blockchain = Blockchain()
22.

23.

@app.route(‘/mine’, methods=[‘GET’])
def mine():
return “We’ll mine a new Block”
27.

@app.route(‘/transactions/new’, methods=[‘POST’])
def new_transaction():
return “We’ll add a new transaction”
31.

@app.route(‘/chain’, methods=[‘GET’])
def full_chain():
response = {
‘chain’: blockchain.chain,
‘length’: len(blockchain.chain),
}
return jsonify(response), 200
39.

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
6.

from flask import Flask, jsonify, request
8.


10.

@app.route(‘/transactions/new’, methods=[‘POST’])
def new_transaction():
values = request.get_json()
14.

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
19.

Create a new Transaction
index = blockchain.new_transaction(values[‘sender’], values[‘recipient’], values[‘amount’])
22.

response = {‘message’: f’Transaction will be added to Block {index}’}
return jsonify(response), 201
Amethod for creating Transactions

挖矿端点

**
**

挖矿端点必须做三件事:

计算工作量证明。
通过增加一笔交易,奖赏给矿工 (也就是我们自己) 一定量的数字货币。
通过将新区块添加到链中来锻造区块。
import hashlib
import json
3.

from time import time
from uuid import uuid4
6.

from flask import Flask, jsonify, request
8.


10.

@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)
17.

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,
)
25.

Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.new_block(proof, previous_hash)
29.

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 进行交互了。

——未完待续——


欢迎加入蓝狐笔记群微信:donnell008

×

喜欢就点赞,疼爱就打赏