如果你想最快地了解区块链是怎么工作的,那就自己动手建一个吧。
你来这里是因为,和我一样,你对加密货币的崛起非常激动。你想知道区块链的工作原理——支撑它们的核心技术。
但这并不容易——至少对我来说。我艰难地观看了一堆密集的视频,跟着零散的教程摸索,因为缺少例子而倍感沮丧。
我喜欢通过实践来学习。这迫使我在代码层面处理相关内容,使知识更牢固地扎根。如果你也这么做,在完成本指南时,你将拥有一个功能完整的区块链,并能深刻理解它们的工作原理。
在你开始之前,请先看看下面的提示…记住,区块链是一个不可变且按顺序排列的记录链,这些记录被称为区块。每个区块可以包含交易、文件或任何你想要的数据。但,重要的是,它们是通过哈希值链接在一起的。
如果你还不知道什么是哈希值,你可以在这里找到解释:这里有一个解释。
这份指南面向谁? 你需要能舒服地读写一些基本的Python代码,并且了解一些HTTP请求的基本知识,因为我们将会用HTTP与我们的区块链互动。
我需要什么? 确保安装了 Python 3.6(包括 pip
在内)。你还需要安装 Flask 和强大的 Requests 库:
你可以通过pip来安装指定版本的Flask和requests库,版本号分别是0.12.2和2.18.4:
pip install Flask==0.12.2 requests==2.18.4
哦,你还需要一个HTTP客户端,比如Postman或cURL,任何工具都行。
最终代码在哪? 源代码可以在这里找到:[[available here](https://github.com/dvf/blockchain)]。
第一步:构建区块链打开你最喜欢的文本编辑器或IDE,比如我个人最喜欢 ❤️ PyCharm。创建一个新的文件,命名为 blockchain.py
。我们将只使用一个文件,如果你感到困惑,可以随时查看 源代码。
我们将创建一个 Blockchain
类,其构造函数会初始化一个初始为空的列表(用于存储区块),以及另一个列表用于存储交易。这就是我们类的设计蓝图。
区块链课的蓝图
我们的Blockchain
类(以下简称区块链
类)负责管理区块链。它将用来存储交易,并提供一些辅助方法来添加新的区块到区块链中。让我们开始实现一些方法。
每个区块都有一个 索引 ,一个 时间戳 (以 Unix 时间表示),一个 交易列表 ,一个 证明 (稍后讨论),以及 上一个区块的哈希。
下面是一个单个区块的示例:
这是我们区块链中的一个区块示例。
此时,链的概念应该已经很明显了——每个新块都包含它前一个块的哈希。这一点至关重要,因为它正是区块链不可变性的基础:如果攻击者篡改了链中的某个早期区块,那么所有后续区块的哈希都会受影响。
这说得通吗?如果还摸不着头脑,花点时间琢磨琢磨,这正是区块链的核心理念。
将交易添加到区块我们需要一种方法将交易添加到区块中。我们的 new_transaction()
方法负责这一点,这非常简单。
new_transaction()
在添加交易到列表后,它将返回交易将被添加到的 区块的位置索引 — 即将被挖掘的下一个区块的索引。这对于提交交易的用户来说非常有用。
当我们实例化我们的Blockchain
时,我们需要用一个_创世区块来初始化它,这个区块没有前驱。我们还需要在这个创世区块里加入一个“证明”_,这是通过挖矿(或工作量证明)得到的结果。我们之后会更详细地讨论挖矿。
除了在构造函数中创建_创世_区块外,我们还将实现和定义 new_block()
、new_transaction()
和 hash()
方法。
以上应该非常直观——我添加了一些注释和_docstrings_来帮助保持清晰。我们几乎完成了区块链。这时,你可能在想,新的区块是怎么被创建、生成或挖矿(mining)的。
理解工作证明工作量证明(PoW)算法是区块链上新区块如何被创建或 挖掘 的方式。PoW 的目的是找到一个能解决问题的数字。这个数字必须 很难找到但容易验证 ——从计算的角度来看——网络上的任何人都可以轻松验证。这就是工作量证明的核心思想。
我们来举个特别简单的例子,帮助大家理解得更透彻。
我们假设一些整数 x
乘以另一个 y
的 hash 或 哈希 必须以 0
结尾,也就是说,hash(x * y) = ac23dc...0
。对于这个简化示例,我们将 x
设为 5
。在 Python 中实现这个例子:
from hashlib import sha256
x = 5
y = 0 # 我们还不知道 y 应该是多少,先试试看...
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0" :
y += 1
print(f'最后的答案是 y = {y}')
这里 y = 21
是解决方案,因为生成的哈希值以 0 结尾,所以。
5 乘以 21 的哈希值是 1253e9373e...5e3600155e860
在比特币网络中,工作量证明算法称为Hashcash。它与我们上面提到的基本示例很相似。它是矿工们竞相解决以创建新区块的算法。一般来说,难度由在字符串中找到特定字符序列的难度决定的。矿工会因找到解决方案而获得一枚硬币作为奖励,通过一次交易接收奖励。
网络能够轻松验证他们的答案。
实现基本的工作量证明机制让我们为我们的区块链实现一个类似的算法。为我们的区块链,我们的规则也将类似于上面的例子。
找到一个数 p ,将它与上一个区块的哈希值一起哈希时,产生一个以四个
0
开头的哈希值。
为了调节算法的难度,我们可以修改前导零的个数。但4个前导零已经足够。你会发现,增加一个前导零会让找到解决方案所需的时间显著增加。
我们这个班级快要结束了,我们准备开始用HTTP请求与这个系统进行互动操作了。
第二步:我们的区块链API我们将使用Python Flask微框架。这是一个微框架,这使得将端点映射到Python函数变得十分简单。这使我们能够通过HTTP请求与我们的区块链进行通信。
我们会创建三个方法。
/transactions/new
用于创建一个新的交易并添加到区块中/mine
用于告诉我们的服务器挖出一个新的区块。/chain
用于返回完整的区块链信息。
我们的“节点”将在我们的区块链系统中形成一个节点。我们来创建一些基本代码。
简单说一下我们在上面加了什么
- 第15行: 实例化我们的Node。更多关于Flask的信息可以在这里查看。
- 第18行: 生成我们节点的随机名称。
- 第21行: 实例化我们的
Blockchain
类。 - 第24–26行: 这接收一个GET请求的
/mine
端点。 - 第28–30行: 创建一个接收POST请求的
/transactions/new
端点,因为我们将通过该端点发送数据。 - 第32–38行: 创建一个返回整个区块链数据的
/chain
端点。 - 第40–41行: 启动服务器并在端口5000上运行。
下面是一个交易请求消息的示例。用户会把这样的信息发送给服务器:
{
"sender": "我方地址",
"recipient": "对方地址",
"amount": 5
}
因为我们已经有了向区块添加交易的方法,剩下的就容易多了。我们来写一个添加交易的函数吧。
如何创建交易的步骤
挖矿端口我们的挖矿端点很容易,只需要做三件事。
- 计算工作量证明
- 通过给矿工(也就是我们)添加一个奖励1个币的交易来进行奖励
- 将新区块添加到链上
需要注意的是,挖矿得到的区块的接收者是这个地址,这个地址是我们的节点地址。我们在这里主要做的工作只是调用我们Blockchain类中的方法。到这个时候,我们已经完成了这些步骤,就可以开始和我们的区块链打交道了。
步骤三:与我们的区块链互动你可以通过普通的 cURL 或 Postman 工具与我们的 API 进行网络互动操作。
启动服务器。
$ python blockchain.py*, 启动在 <http://127.0.0.1:5000/> 运行中 (按 CTRL+C 终止)
让我们试试通过向 http://localhost:5000/mine
发送一个 GET
请求来挖掘一个区块
使用 Postman 发送一个 GET 请求
让我们通过向http://localhost:5000/transactions/new
发送一个POST
请求来创建一个新的交易记录,其中请求体包含我们的交易结构信息:
使用 Postman 发起一个 POST 请求
如果没有使用 Postman,你可以用 cURL 发出类似的请求:
$ curl -X POST -H "Content-Type: application/json" -d ' {
"sender": "d4ee26eee15148ee92c6cd394edd974e",
"recipient": "someone-other-address",
"amount": 5
}' http://localhost:5000/transactions/new
我重新启动了我的服务器,并挖到了两个区块,总计三个区块。让我们通过请求 [http://localhost:5000/chain](http://localhost:5000/chain:)
检查整个区块链。
{
"链": [
{
"区块索引": 1,
"前一区块哈希": 1,
"工作量证明": 100,
"时间戳": 1506280650.770839,
"交易": []
},
{
"区块索引": 2,
"前一区块哈希": "c099bc...bfb7",
"工作量证明": 35293,
"时间戳": 1506280664.717925,
"交易": [
{
"金额": 1,
"接收方": "8bbcb347e0634905b0cac7955bae152b",
"发送方": "0"
}
]
},
{
"区块索引": 3,
"前一区块哈希": "eff91a...10f2",
"工作量证明": 35089,
"时间戳": 1506280666.1086972,
"交易": [
{
"金额": 1,
"接收方": "8bbcb347e0634905b0cac7955bae152b",
"发送方": "0"
}
]
}
],
"长度": 3
}
步骤 4:达成共识
这真的很酷!我们有一个基本的区块链,可以接受交易,并让我们能够挖出新的区块。但区块链的关键在于它们应该是去中心化的。但如果它们是去中心化的,我们又怎么保证它们都是一条链呢?这就是所谓的共识难题,如果我们想要网络中有多个节点,我们就得实现一个共识算法。
注册新节点在我们能够实施一个共识算法之前,我们需要一种方式让一个节点能够找到网络上的邻近节点。每个节点都应该记录下网络上的其他节点。因此,所以我们需要一些额外的接口或端点。
/nodes/register
用于接收一系列以 URL 格式表示的新节点。/nodes/resolve
用于实现我们的共识算法(Consensus Algorithm),解决可能出现的任何冲突,以确保节点拥有正确的区块链。
我们需要修改我们区块链的构造函数,并提供一种注册节点的方法。
将邻近节点添加到我们网络中的方法
注意,我们使用了 set()
来存放节点。这是一种低成本的方法,确保每次添加新节点都是幂等的——这意味着无论我们添加同一节点多少次,它只会被添加一次。
如前所述,当两个节点的链不一致时,即发生了冲突。为解决这个问题,我们将规定 最长的有效链具有权威。换句话说,网络中最长的链就是 事实上的 标准链。“主链”可能会引起误解,而“标准链”更符合技术语境。通过这种方法,我们网络中的节点可以达成共识。
第一个方法 valid_chain()
负责检查链是否有效,通过遍历每个区块并验证哈希和工作量证明。
resolve_conflicts()
是一个方法,它会遍历所有邻近的节点,下载 它们的链,并使用上述方法验证。如果找到一个有效链,其长度大于我们当前的链,我们就用它替换我们现有的链。
让我们将这两个端点注册到我们的API,一个用于添加相邻节点,另外一个用于解决冲突问题:
此时,如果你想的话,可以换另一台机器,或者在同一台机器上使用不同的端口启动不同的进程。我在自己的机器上又启动了一个节点,并将其注册到我当前的节点上,使用的是不同的端口。因此,我现在有两个节点:<http://localhost:5000>
和 http://localhost:5001
。
注册新的节点
然后我在节点2挖掘了一些新的区块,以确保链比其他节点更长。之后,我在节点1上调用GET /nodes/resolve
,在那里链由共识算法重新调整。
共识算法的工作原理
就这样,搞定... 找些朋友一起帮忙测试一下你的区块链,试试看。
我希望这能激励你去创作新的东西。我对加密货币感到非常激动,因为我相信区块链将很快改变我们对经济、政府和记录保存的看法和方式。
更新: 我打算写一篇第二部分,让我们的区块链增加一个交易验证机制,并聊聊如何把区块链实际应用起来。
如果你喜欢这篇指南,或者有任何建议或问题,可以在评论中告诉我。如果你发现任何错误,欢迎在这里面的GitHub仓库https://github.com/dvf/blockchain 提交代码更正。
共同学习,写下你的评论
评论加载中...
作者其他优质文章