原帖地址:

前言

上文中我们构建了简单的数据结构,这个结构是区块链数据库的核心。我们还为这个数据结构添加了链式关系:每个块都链上了前一个块。但是我们的区块链有重大瑕疵:添加块非常容易。这区块链的核心特性之一就是加快的高代价,本节我们修正这个瑕疵。

POW

区块链的核心思想是任何人要在链上添加新的数据时都需要付出辛勤的劳动。这保证了区块链的安全和一致性。同时,劳动可以获得相应的报酬。(这就是挖矿)

这个机制非常类似于现实生活,人们必须努力工作赚钱维持生活。在区块链上,一些参与者(矿工)工作来支撑整个网络,为其添加新的区块,并获得酬劳。作为他们工作的结果,新的区块以一种安全的方式被加入到链上,这维护了整个区块链数据库的稳定性。值得注意的是,一个人完成工作必须证明

这整个机制称为POW(译注:工作量证明)。 POW需要大量的计算。此外,计算难度会随着时间而增加,保证每小时6个块。在比特币中, 这些工作的目的是找到区块的hash值,这个hash值满足一些预设条件。 这些hash值被视为证明。

最后,值得注意的是,POW算法必须满足一个条件,找到hash困难,验证hash容易。

HASH

该段中,我们将讨论hash,如果你熟悉此概念,可以略去。

HASHING是一个为一个指定数据获取hash的过程。一个hash唯一代表了一些数据。hash函数的输入参数是一个任意长的数据块并输出固定长度的hash值,下面是hashing一些特征:

1、原始数据不可以通过hash值回复。因此,hash不是加密

2、特定数据产生唯一hash值

3、一个byte的改变会改变整个hash值


hash函数广泛应用于数据一致性的检测。一些软件在软件包中提供公开的checksum。在下载文件后,你可以使用checksum生成hash值与软件包提供的hash值进行比对,保证数据未被篡改。

在区块链中,hashing用来保证区块的一致性。hashing算法的输入包含了上一个区块的hash值,保证了区块的不可篡改行:若要篡改某个区块必须重新计算该区块的hash值和后面每个区块的hash值。

Hashcash

比特币使用Hashcash, 一个用来防止诈骗邮件的pow算法。 包含如下步骤:

1、输入一些公开数据(对于邮件系统是接收者的邮件地址,对于比特币的是区块头)

2、对其计数,初始为0.

3、获取数据+计数的hash值

4、检查hash是否满足某个确定要求

(1)如满足,ok,否则转2

(2)增加计数并重复步骤3和步骤4

因此,这个算法很粗暴:一旦计数改变,就必须重新计算hash然后检查。这是其是计算密集型的原因。

现在让我们来进一步看看hash条件。在原始的hashcash中,条件是 前20bit位是0。在比特币中,这个条件随着时间的增长而调整,因为在比特币的设计中,系统每10分钟产生一个区块。当有更多的算力投入挖矿时计算难度必须相应的调整。

为了表示该算法,我用了刚才的例子(“I like donuts”)并为其找一个前3位为0的hash:

ca07ca

执行

目前理论已经完成,让我们来写代码,首先定义挖矿难度:

在比特币中,“target bits”是保存在被挖的区块区块头难度参数。我们当前并不实现难度调整算法,所以将难度定义为常量。24是随便选的,我们的目标是使得target在内存中不超过256位。我们不希望难度太大,导致挖矿困难。

NewProofOfWorkbig.Int256 - targetBits256

此处实现了ProofOfWork结构,结构中保存了一个指向区块的指针和一个难度指针。target即前文描述的必要满足条件。此处使用big integer类型是因为我们要将它和一个hash值对比:我们将把hash转化为一个big integer并检查它是否小于target。

在NewProofOfWork函数中,我们用1初始化了一个http://big.Int,并将其左移256位作为targetbit。256是sha-256hash算法的长度,我们将使用sha-256算法。16进制表示如下:

And it occupies 29 bytes in memory. And here’s its visual comparison with the hashes from the previous examples:

target在内存中占据29字节。下图是一个hash值的直观比较

第一个是I like donus的hash值,它比target大,因此它是无效的。第二个是I like donutsca07ca的hash值,它比target小。因此其有效。

你可以将target当作一个上界,若一个数字小于该界限,则有效,反之亦然。小的上届意味着更少满足条件的数字,因此,计算难度越高。

现在,我们需要data来做hash,如下:


该段代码比较清晰:我们将区块字段和nonce合并,none是hashcash中所要求的计数器,是密码学中的专业术语。

Ok,准备工作到此位置,我们来看看POW算法的核心:

首先,我们初始化变量:hashint是hash的整数形式;nonce是计数器。接下来,我们将运行一个有限的循环,其次数被maxnonce所限制,maxnonce是math.MaxInt64, 用来避免溢出。虽然我们的挖矿难度很低,溢出的概率很小,但还是有必要以防万一。

在循环中,我们做了如下事情:

1、准备数据

2、使用SHA-256计算hash

3、将hash转换为big integet

4、将integer和target进行比较

我们删除SetHash方法并重写NewBlock函数:

如上可见,nonce被保存为区块的属性,用于工作量证明。当前块结构如下所示:

现在让我们一起来跑代码看看运行结果:

可以看到所有的hash值前面都有3字节的0位,这是花费了一定时间计算出来的。

目前为止还差一件事,pow的验证机制:

这是我们保存nonce的地方

让我们再检查一下看看代码是否ok

输出:

结论

我们的区块链越发接近于实际系统:添加新的区块需要大量工作,节点可以参与挖矿。但它仍然却反核心特性:区块链数据不持久化,没有钱包 地址 交易,也没有共识机制。后文都会一起讨论,挖矿愉快!