作者:Keyones/全栈PM
微信公众号:lovek_cn
个人主页:lovek.cn
前言
1.这两年来比特币、以太币等虚拟货币交易火热,区块链技术成为大家乐于讨论的问题,面对市场和社会的关注,大家一定对区块链技术和原理非常的好奇。
2.但是要理解区块链技术似乎没那么简单,我想你也一样再看了各种动画视频后也没办法完全理解区块链的技术。
如果作者Keyones能在2011年看到区块链的发展,兴许就不会在2011年底彻底格式化那个存着200多个比特币的硬盘...显然这是个忧伤的故事,我们回到今天的主题。)
3.实践是检验真理的唯一标准,上周决定从代码层面来解决我一直以来的困惑,并且将该过程与大家分享。如果你有一定python coding 的基础以及理解Http的请求原理,跟着我的分享,最终你将获得一份可以正常运行的区块链系统,并且深入的理解区块链的工作原理。
点击原文下载Keyones-Blockchain的中代码以及注释
感谢 Daniel van Flymen提供的代码带来的启发,我将在DVF代码的基础上做适当调整,并且编写中文注释。
先行工作
在正式开始我们编码工作之前,确保你已经了解并执行了以下几个要点:
你需要知道区块链是一个不可更改的、有序的记录(被称为区块)的数据链。其中可以包含很多的数据,并且这些有序的记录通过哈希算法来进行链接,从而形成区块链。
你的系统上安装了python3.6+(包含pip),同时安装Flask框架以及为python安装requests库,最后你需要有一个好用的编译器,作者使用pycharm(专业版自带Flask),当然你也可以通过终端pip进行安装:
安装一个HTTP客户端,这里我使用Postman,当然你可以使用你熟悉的工具。
我的编程环境为Mac Os 10.1.36 / Python 3.6 / Flask 1.0.2 / Request 2.19.1
你需要了解什么是哈希值(--->>>点击这里快速了解哈希值)
STEP 1: 创建一个区块链
我在这次演示中,将多有代码都写到一个文件blockchain.py中,方便阅读。现在,就开始我们的奇幻旅程,用不到300行的Python代码,写一个可以运行的区块链系统。
1.1 描述一个区块链
我们将创建一个 Blockchain类,它的构造函数将去初始化一个空列表(去存储我们的区块链),以及另一个列表去保存交易。下面是我们的类规划:
首先,我们创建一个Blockchain的类,在初始化init构造函数中预设一个Chain列表用于保存区块链,另一个transactions列表用于保存本区块的交易记录以及信息。
如果代码遮挡,可以左右滑动代码区域查看!
以上就是Blockchain类的基本原型规划,接着我们要在这个类里面增加一些方法。
我先说下区块中的区块是什么样子,抽象要素之后每个区块包含这样几个内容:
'index': 区块的索引,创世区块的索引为1,以后每次加1
'timestamp': 区块创建的时间戳Unix Time
'transactions': 这个区块的交易列表
'proof': 工足量证明,后面会详细介绍这部分
'previous_hash':前一个区块的哈希值
我们可以看到每一个区块中都有前一个区块的哈希值,这是保证区块链安全不被篡改的核心要点,当任意一个区块发生任何数据的改变,都会导致哈希值的改变,导致无法通过验证。
攻击者如果攻击前面的区块,就要重写后面的所有区块,这样做法的工作量是巨大的。
1.2 完善交易函数
我们现在来完善Blockchain类中的new_transcations方法,来实现向区块中添加交易记录:
1.3 完善区块创建函数
我们现在来完善Blockchain类中的new_block方法,来实现新增区块的功能:在这个示例中,我们使用(工作量证明 PoW),proof就是工作量证明Pow的结果。
到这里,我们已经做了这样几件事情
创建了一个区块链容器、交易容器
完善了生成区块链和增加交易的函数
在区块链类被实例化的时候,我们需要创建一个区块,这个区块我们称之为创世块。所以我们在Blockchain类中初始化函数中增加:
完善之后的Blockchain类初始化函数是这样:
除此之外,我们还需要在Blockchain类中写一个hash函数以及一个last_proof函数,用来哈希处理区块以及返回最后一个区块内容。现在加上这两个函数,blockchain.py的所有内容是这样的:
1.4 Pow工作量证明介绍与演示代码
工作量证明 Proof of Work(PoW)算法是在区块链上创建或者挖出新区块的方法。PoW 的目标是去计算出一个符合条件的数字。这个数字必须满足“找到它很困难但是验证它很容易”的条件 —— 网络上的任何人都可以计算它,这就是 工作量证明 背后的核心思想。
这里我来写一个简单的示例,来阐述工作证明的原理:
注意:请勿将1.4部分的代码加到Blockchain.py中,下面的代码解决以下问题:
m与n乘积的哈希值的前两位必须为00,已知n = 9,求m,则求出的m即为工作量证明。
执行这段代码得到:
根据计算机的计算结果:
Hash(287*9)= 00328ce57bbc14..5d39df60符合前哈希值前两位为0,获得m的解为287。
其实在比特币的Pow算法中,以上述几乎一样,需要提高计算难度只需要把0的位数进行调整,例如哈希值前n位为0则求出解的概率为:
1.5 为我们的区块链实现POW
现在,我们为我们的区块链设计一个较为简单的算法来实现工作量证明。
该算法要求寻找一个数F,与上一个区块的工作量证明(答案)f乘积的哈希值的前四位为0,则F为本区块的工作量证明(Proof of work)。
我们在Blockchain类中在加入两个函数,用来完成工作量证明的验证:
由上述POW的介绍中我们可知,只要调整:
就可以调整哈希解(矿工挖矿)的难度,目前为止,我们已经完成了基本类的设计,接下来我们将用HTTP来和我们创造的区块链进行交互。
STEP 2: 通过接口来访问我们的区块链
2.1 Flask架构
在这里,我们使用Python 的Flask框架来实现,Flask将简单快速的完成端点到python function的映射工作。
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
首先,我们创建一个Flask实例。
接着我们的节点生成一个随机的节点标识符,这里采用uuid4来生成随机数node_identifier,以模拟真实的节点地址。接着,我们实例化我们的区块链节点。(-->关于Flask的中文教程点击这里)
接着,我们通过flask_app.route装饰器来创造下列三个路由函数:
/mine:通过访问mine来完成工作量证明(挖矿)
/transactionsnew
: 通过transactionsnew来完成新交易记录的增加
/chain:通过chain返回区块链全链条信息
结构如下
接着我们设置节点IP以及节点端口为8000
接下来我们来完善这几个路由函数
2.2 挖矿端点(/mine)
现在我们来写一个挖矿的端点,这里其实就是节点(矿机)在进行的工作。挖矿端点主要完成下面几个工作
计算POW(工作量的证明)
如果节点进行了记账,我们将给予节点奖励,每次记账我们奖励6个币,我们去个名字叫K币吧
将记录添加到区块链上,打造一个新的区块
下面是完整的mine函数
2.3 交易端点(/new_transactions)
我们先来看节点提交一笔交易的格式是:
在Blockchain类中我们已经写好了添加交易的方法,现在我们写个路由函数来添加交易数据。
下面是完整的new_transactions函数
STEP 3: 使用POSTMAM来和区块链进行交互
3.1 通过8000端口进行交互
现在,我们运行我们的节点,使用8000端口进行交互
出现以下提示则表示运行成功
现在我们使用POSTMAN生成一个GET请求到http://127.0.0.1:8000/transactions/new 来产生记录一笔新的的交易。POST内容如下:
上述的POST为地址 "66f...bb1"向地址"66f...aa3"转账6个币。通过返回信息我们看到交易信息现在等待确认,当区块4产生后,将记录在区块4上面。
现在我们使用POSTMAN生成一个GET请求到http://127.0.0.1:8000/mine来开采区块
通过返回的信息我们可以知道新的区块已经成功挖到一个新区快,并且上面的交易记录已经添加到新的区块中。
STEP 4: 解决节点共识的问题
如果在前三部完全按照我的教程坐下来,恭喜你!我们已经完成一个初步的区块链系统,节点通过解决我们设计的哈希问题来获得奖励,同时我们允许节点间进行交易,并且把交易记录在下一个区块上。
接下来,我们来解决很酷的一个问题,就是解决区块链的核心思想:去中心化的问题。
在之前我们的区块链网络只有一个节点,只有一个交易记录(账本),不存在交易记录不统一的问题。接下来,我们要解决多个节点区块链网络的共识问题(就是每一个节点要同步账本信息)
4.1 节点注册
在我们开始实现公式算法之前,我们要先让所有节点知道其它节点的地址信息,这样每个节点才能遍历附近的节点,从而实现共识。
我们在Blockchain类中的初始化函数中新增一个属性,用于节点的注册,set()集合将自动去除重复住的的节点,确保节点只被注册一次。
增加完之后,Blockchain类的初始化函数为:
接着我们需要在Blockchain类中增加一个方法register_node用于节点注册:
4.2 创建共识算法
我们知道每一个节点上的区块链不一定一致,这时候我们需要创建一个共识算法来帮助各个节点统一账本。
我们设计一个规则来解决这个问题:我们规定节点中的最长区块链是真实可靠的链条,节点遍历其他节点,如果发现其它节点的区块链长度比自己的区块链长度长,则做以下两件事情:
1.判断对方的区块链是否完成无误,如果对方为无效链则保持自己的链条数据
2.如果对方的链条合法无误,则用对方的区块链数据代替自己的
回到我们的编码部分,首先我们在Blockchain类中创建函数valid_chain来判断区块链是否完整有效:
完成区块链有效性检测函数,接下来我们在Blockchain类中创建函数resolve_conflicts来实现节点共识的问题:
4.3 在Flask中增加端点路由
注意:这一部分不是加在Blockchain类中,是放在Flask路由区域
我们已经创建节点注册、区块链验证以及共识三个函数,现在我们在Flask框架中注册两个端点:
如果你跟着我做到这里,那么恭喜你,你已经完成了第一阶段(一个完整的区块链系统)的全部代码。你可以将代码放到两台设备上,启动两个节点,也可以在同一台机器上使用不同端口启动两个线程:
STEP 5 运行我们的杰作
接下来我们来见证激动人心的时刻,完成运行我们自己创建的区块链!
这里我使用同一台机器的8000和8001端口来模拟两个节点的区块联网络。
我们重启两个节点,先使用http://localhost:8000/chain来查询一下现在的链条,现在只有一个区块
接着我们通过http://localhost:8000/nodes/register 来注册新的节点:
返回下面信息,证明节点注册成功
下一步,我们通过发送GET请求到http://localhost:8001/mine来在8001节点进行采矿4次,使得8001节点上的链条拥有5个区块:
通过GET请求到http://localhost:8001/chain 查询当前8001节点的区块链如下:
现在我们知道8001节点有5个节点,8000节点有一个节点接着我们在8001节点通过GET请求http://localhost:8001/nodes/resolve,因为该链条在注册节点里面最长,得到返回“我们的链条为权威链条”
最后我们在8000节点通过GET请求http://localhost:8000/nodes/resolve,因为该链条在注册节点里面不是最长,得到返回“本地链条已经更新”
通过GET请求到http://localhost:8000/chain 查询当前8000节点的区块链如下,我们看到8000已经和节点8001达成共识:
如果你得到与我相同的运行结果,那么恭喜你,你正确完成并亲自完成了一个区块链系统的创建与运行。在后续的教程中,我将继续完善、扩展这个区块链系统的代码,使其具备交易验证等更丰富的功能,敬请期待!
欢迎朋友在留言区留言交流,如有疏漏还请指正!
----全栈PM Keyones
领取专属 10元无门槛券
私享最新 技术干货