本章源代码地址:[https://github.com/daleboy/blockchain2]
# 前言
区块链的一个关键点就是,一个人必须经过一系列困难的工作,才能将数据放入到区块链中。正是这种困难的工作,才使得区块链是安全和一致的。此外,完成这个工作的人也会获得奖励(这也就是通过挖矿获得币)。第一版的区块链加入区块太容易了,这会导致上链变得轻而易举,那么各种无意义的区块将充斥于区块链,使得区块链无意义地膨胀,反而会挤压有效区块的空间和计算资源。
整个 “努力工作并进行证明” 的机制,就叫做工作量证明(proof-of-work)。寻求证明(寻找有效哈希),就是实际要做的事情,在比特币中,称为“挖矿”。
# 哈希计算
在区块链中,哈希被用于保证一个块的一致性。哈希算法的输入数据包含了前一个块的哈希,因此使得不太可能(或者,至少很困难)去修改链中的一个块:因为如果一个人想要修改前面一个块的哈希,那么他必须要重新计算这个块以及后面所有块的哈希。
比特币使用[Hashcash](https://link.jianshu.com/?t=https://en.wikipedia.org/wiki/Hashcash),一个最初用来防止垃圾邮件的工作量证明算法。它可以被分解为以下步骤:
1. 取一些公开的数据(比如,如果是 email 的话,它可以是接收者的邮件地址;在比特币中,它是区块头)
2. 给这个公开数据添加一个计数器。计数器默认从 0 开始
3. 将 data(数据) 和 counter(计数器) 组合到一起,获得一个哈希
4. 检查哈希是否符合一定的条件: 1.如果符合条件,结束 2.如果不符合,增加计数器,重复步骤 3-4
因此,这是一个暴力算法:改变计数器,计算一个新的哈希,检查,增加计数器,计算一个哈希,检查,如此反复。这也是为什么说它是在计算上是非常昂贵的,因为这一步需要如此反复不断地计算和检查。
现在,让我们来仔细看一下一个哈希要满足的必要条件。在原始的 Hashcash 实现中,它的要求是 “一个哈希的前 20 位必须是 0”。在比特币中,这个要求会随着时间而不断变化。因为按照设计,必须保证每 10 分钟生成一个块,而不论计算能力会随着时间增长,或者是会有越来越多的矿工进入网络,所以需要动态调整这个必要条件。
# POW实现
我们看看一个第一版的block的结构:
![](https://img.kancloud.cn/11/20/1120f4a1b1bbc8808fd3a7beaa4b945b_676x256.png)
(1)Timestamp,为时间戳,容易获得
(2)Data,为实际有效信息,上链前由上链者提交
(3)PrevBlockHash,上一个区块的哈希,已经存在,直接获得
(4)Hash,本区块的哈希
显然要增加创建区块的难度,只有在计算本区块的哈希上做文章,为此,区块的结构添加一个计数器nonce。假设将挖矿难度设定为24bit为0,那么挖矿者在获得上链者提交的交易信息后,通过不断调整计数器,计算Timestamp+Data+PrevBlockHash+nonce的哈希,直到该哈希的前三个字节全是0(16进制)的哈希,然后将block提交到链上。
当然,在矿工将block提交到链上之前,检验节点可以进行验证,所挖的矿是否是合格的。本版本的POW实现提供验证方法,在主程序中可以进行验证。
新的block结构:
![](https://img.kancloud.cn/c6/5c/c65cf82c8ddc63d2719a38c309650868_960x376.png)
## **1、proofofwork.go**
POW即挖矿,其实现是关键。我们首先要定义挖矿难度系数:
![](https://img.kancloud.cn/c5/2c/c52c9cc69f1b83eedc83109fe059132b_930x121.png)
即:要求区块计算出的哈希值转为大整数后,其前24bit为0
挖矿当然是以包含交易数据的区块和需要满足的条件为基础来工作,我们定义下这个工作基础,即结构体ProofOfWork:
![](https://img.kancloud.cn/38/7c/387c52a55e5ca16b281139f7e01046a4_930x285.png)
这里的必要条件,等价于前面定义的targetBits。
接下来,我们需要构建一个pow实例,用于后面挖矿用。这里主要是在构建pow实例时候确定target条件。
![](https://img.kancloud.cn/5d/8a/5d8add83361b581c41de76cc728ae737_1626x896.png)
代码的注释非常清楚,将数字1左移256-targetBits位,即为target,这个target扩展到宽度为256位后,前置0为23位,第24位为1,所以只要矿工挖矿中找到的哈希值转为大整数后小于target,那么前24位必为0,满足挖矿条件要求!
我们开始要准备挖矿了,首先需要准备计算哈希的数据:
![](https://img.kancloud.cn/78/61/7861ef646a5005b10b73d714ce4b0617_963x556.png)
我们开发了一个IntToHex函数,将整型转为byte数组。详细见utils.go。
需要注意,这次计算哈希,新增了一个nonce。
接下来是关键的挖矿核心算法:
![](https://img.kancloud.cn/e5/b1/e5b1299cb9bdc8d69385d7d47accdba8_1265x971.png)
挖矿中,通过递增计数器nonce(为防止溢出,定义了一个maxNonce),修改data,直到找到的哈希值符合条件,返回找到的哈希值和对应的nonce。
这里可能需要计算非常多次,如果处理不好,不仅耗费cpu,还耗费内存。在循环中,我们以hash数组为参数调用函数时候,是通过哈希创建的切片进行,相当于引用数组,而不是直接调用hash数组,就是为了减小内存的开销。
当然,挖矿也可能失败,即没有挖出满足条件的区块,因此,需要对挖出的区块进行验证:
![](https://img.kancloud.cn/49/30/4930b4a178d68d9c55f947502be9d77f_1365x418.png)
一般来说,只有通过验证的区块,才可以加入到区块链中。
## **2、block.go**
需要对block进行修改,去掉SetHash,修改NewBlock:
![](https://img.kancloud.cn/6b/79/6b79fcba4694dd72e6c78f5ef6d7c28a_1181x489.png)
区块不再是直接创建,而是通过挖矿产生。
当然创始区块也是通过挖矿产生:
![](https://img.kancloud.cn/7f/d0/7fd0eb2bf87d68bd5de0f857cbc5c840_820x167.png)
## **3、blockchain.go**
这个跟上一版本没有变化。
当然AddBlock需要改进,这里是挖矿+加入区块链一起完成,最好是在加入区块链之前,先验证下。
## **4、main.go**
好了,我们现在开始进行测试了,看看是否能够验证我们的想法:
![](https://img.kancloud.cn/48/ff/48ff011034b02218e89b4708d97aa158_1030x739.png)
运行后,需要些时间:
![](https://img.kancloud.cn/73/e7/73e744deb51b06908914c6941e330936_1127x703.png)
可以看到,无论是挖出的创始区块,还是普通区块,哈希值均为64位(16进制),前面六位的值是0,也就是二进制的24个0,满足我们最开始设置的挖矿条件。
- 重要更新说明
- linechain发布
- linechain新版设计
- 引言一
- 引言二
- 引言三
- vs-code设置及开发环境设置
- BoltDB数据库应用
- 关于Go语言、VS-code的一些Tips
- 区块链的架构
- 网络通信与区块链
- 单元测试
- 比特币脚本语言
- 关于区块链的一些概念
- 区块链组件
- 区块链第一版:基本原型
- 区块链第二版:增加工作量证明
- 区块链第三版:持久化
- 区块链第四版:交易
- 区块链第五版:实现钱包
- 区块链第六版:实现UTXO集
- 区块链第七版:网络
- 阶段小结
- 区块链第八版:P2P
- P2P网络架构
- 区块链网络层
- P2P区块链最简体验
- libp2p建立P2P网络的关键概念
- 区块链结构层设计与实现
- 用户交互层设计与实现
- 网络层设计与实现
- 建立节点发现机制
- 向区块链网络请求区块信息
- 向区块链网络发布消息
- 运行区块链
- LineChain
- 系统运行流程
- Multihash
- 区块链网络的节点发现机制深入探讨
- DHT
- Bootstrap
- 连接到所有引导节点
- Advertise
- 搜索其它peers
- 连接到搜到的其它peers
- 区块链网络的消息订发布-订阅机制深入探讨
- LineChain:适用于智能合约编程的脚本语言支持
- LineChain:解决分叉问题
- LineChain:多重签名
- libp2p升级到v0.22版本
- 以太坊基础
- 重温以太坊的树结构
- 世界状态树
- (智能合约)账户存储树
- 交易树
- 交易收据树
- 小结
- 以太坊的存储结构
- 以太坊状态数据库
- MPT
- 以太坊POW共识算法
- 智能合约存储
- Polygon Edge
- block结构
- transaction数据结构
- 数据结构小结
- 关于本区块链的一些说明
- UML工具-PlantUML
- libp2p介绍
- JSON-RPC
- docker制作:启动多个应用系统
- Dockerfile
- docker-entrypoint.sh
- supervisord.conf
- docker run
- nginx.conf
- docker基础操作整理
- jupyter计算交互环境
- git技巧一
- git技巧二
- 使用github项目的最佳实践
- windows下package管理工具