天天看点

《区块链开发指南》一一1.3 挖矿、矿池

本节书摘来自华章计算机《区块链开发指南》一书中的第1章,第1.3节,作者:申屠青春 主编 宋 波 张 鹏 汪晓明 季宙栋 左川民 编著更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.3.1 挖矿原理与区块的产生

比特币的挖矿和节点软件是基于对等网络、数字签名来发起和验证交易的。节点向网络广播交易,这些广播出来的交易需要经过矿工的验证,矿工们会用自己的工作证明结果来表达确认,确认后的交易会被打包到数据块中,数据块会串起来形成连续的数据块链。中本聪本人设计了第一版的比特币挖矿程序,这一程序随后被开发为广泛使用的第一代挖矿软件bitcoind,这一代软件在2009年到2010年期间都比较流行。

每一个比特币的节点都会收集所有尚未确认的交易,并且会将其归集到一个数据块中,这个数据块将和前面一个数据块集成在一起。矿工节点会附加一个随机调整数,并计算前一个数据块的sha-256 hash运算值。挖矿节点不断进行重复尝试,直到它找到的随机调整数使得产生的hash值低于某个特定的目标为止。由于hash运算是不可逆的,因此寻找到符合要求的随机调整数将会非常困难,因此需要一个可以预计总次数的不断试错的过程。这时,工作量证明机制就发挥作用了。当一个节点找到了符合要求的解,那么它就可以向全网广播自己的结果。其他节点就可以接收这个新解出来的数据块,并检验其是否符合规则。只要其他节点通过计算hash值发现其确实满足要求(比特币要求的运算目标),那么该数据块就是有效的,其他的节点就会接受该数据块,并将其附加在自己已有的链条之后。

当挖矿时,你会经常对区块头进行散列,你正在挖的区块也会时常进行更新,一个区块头如上文所述的表1-3所示。

表1-3中的大部分数据项对所有用户都是一致的,不过,在时间戳上可能会有些区别。如果当前区块的时间戳大于前11个区块的平均时间戳,并且小于“网络调整时间(network-adjusted time)”+ 2小时,则认为该时间戳是有效的。其中的“网络调整时间”是指与你相连接的所有节点的平均时间。当节点a连接到节点b时,a将从b处得到一个utc的时间戳,a先将其转换成本地utc并保存起来,网络调整时间等于所有节点的本地utc时间+所有相连节点的偏移量平均值,然而,该网络时间永远不会调整到超过本地系统时间70分钟以上。

nonce随机数通常都不会相同,但是它以严格的线性方式在增长,从0开始,每次执行散列时都会增长,当nonce溢出时(此事经常发生),挖矿交易的extranonce项就会增长,其将改变merkle树的根节点。

假定针对这些数据项,人们经常会独自产生同样序列号的hash值,那么,最快的矿机通常会赢。然而,两人产生同样的merkle根节点基本上(或几乎)是不可能的,因为区块中的第一个交易是挖矿交易并且会“发送”到你独一无二的比特币地址上。而你的区块与其他人的区块是有区别的,因此,产生的hash值肯定也会(几乎可以肯定)不同,你计算的每个hash值和网络中的其他人一样,都有同样的获胜机会。

比特币使用sha256(sha256(区块头))计算hash值,但要注意字节序。例如,以下的python代码用于计算某一区块的hash值,使用的是2011年6月区块号为125552的最小hash值。该区块头建立在表1-3所示的6个数据项之上,并且以十六进制的小端结尾方式连接在一起。

实际的hash值是一串256位的数值,首部有许多零。当以大头端十六进制常数方式打印或存储时,它的首部有许多零;如果它以小头端打印或存储时,零就会变换到尾部。例如,如果表示成字节串-最低(或者开头)的字节串地址显示最小位的数,那么这就是小头端表示。blockexplorer的输出把hash值显示为大头端表示的数值,因为数字的表示通常是首部数字是最大的数字(从左向右读)。

1.3.2 挖矿难度

挖矿难度是对挖矿困难程度的度量,即指计算符合给定目标的一个hash值的困难程度。比特币网络有一个全局的区块难度,有效的区域必须有一个hash值,该hash值必须小于给定的目标hash值。矿池也会有一个自定义的共享难度,用来设定产生股份的最低难度限制。

难度每过2016块就会改变一次,计算公式为:

difficulty = difficulty_1_target/current_target

其中,目标(target)是一个256位长的数值。

测量难度有许多不同的方法,通过这些方法得到的diff?iculty_1_target有可能也会不同。传统情况下,它表示一个hash值,前32位为0,后续部分为1(称之为矿池难度或pdiff),比特币协议把目标hash值表示成一个固定精度的自定义浮点类型,因而,比特币客户端用该值来估计难度(称之为bdiff)。

难度经常被存储在区块中,每个块存储一个十六制的目标hash值的压缩表达式(称之为bits),目标hash值可以通过预先定义的公式计算出来。例如:如果区块中压缩的目标hash值为0x1b0404cb,那么十六进制的目标hash值就为如下形式:

因而目标hash值为0x1b0404cb时,难度为:

或者:

其中:0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff是挖矿机使用的最大目标hash值。而0x00000000ffff00000000000000000000000000000

00000000000000000000000则是比特币网络使用的浮点编码类型,后面的位数被缩短了。

下面是一个快速计算比特币难度的方法,这里使用的算法是修改的泰勒序列(读者可以参考wikipedia上的教程),并且依赖记录来转换难度计算。

如果想要了解难度计算的数学原理,那么可以看看如下的python代码:

最大难度大约等于maximum_target/1(因为0会导致无穷大),这是一个非常大的数值,大约为2224;当maximum_target为最小值1时,则最小难度值也为1。

难度可根据以前2016个区块的产生时间而定,每过2016块就会改变一次。预计每隔10分钟产生一个区块,因而产生2016个区块要花费2周时间。如果前2016个区块的产生时间多于两周,则难度会降低;否则难度就会增加。

为了找到新区块,该区块的hash值必须小于目标hash值,实际上它是一个在0到2256-1之间的随机数,难度1的偏移量是:

0xffff×2208

难度d的偏移量是:

(0xffff×2208)/d

在难度d下,为了找到新区块,预期要计算的hash数量是:

d×2256/(0xffff×2208)

或者只是:

d×248/0xffff

难度的设定,是为了以每10分钟一个区块的速度产生2016个区块,因而,在600秒内计算(d×248/0xffff)个hash,这就意味着产生2016个区块的网络hash速率(算力)是:

d×248/0xffff/600

可以进一步简化为:

d×232/600

以上公式有较好的精度。

在难度为1时,算力是7mhashes/秒,难度是5?006?860?589,这就意味着以前2016个区块被找到,其平均算力是:35.840phash/s。

5?006?860?589×232/600≈35.840 phash/s

发现一个区块的平均时间,可以用以下公式估计:

时间=难度×232/算力

其中,难度是当前的难度,算力是指矿机的计算能力,以hashes/s为单位,时间是你找到两个区块的平均时间。举例来说,使用python计算,算力为1ghashes/s的矿机,难度在20?000时,产生一个新区块的时间,计算式如下:

$ python -c "print 20000 * 232 / 109 / 60 / 60.0"

23.85

其中**表示指数,该语句运算的结果就是:找到一个新区块要花费近1天的时间。

1.3.3 矿池原理与商业模式

随着生成区块的难度逐步增加,挖矿变成了一个碰运气的事情,单一节点要生成一个区块需要花费数年的时间(除非这个单一节点拥有大量的计算力)。为了激励计算力较低的用户继续参与挖矿,矿池就出现了。在一个矿池里,许多不同的人贡献出自己的计算力来生成一个区块,然后再根据每个人的贡献比例来分发奖励。通过这种方式,要得到那个50个比特币的奖励就不必等待数年的时间了,小矿工能定期得到属于他们那部分的比特币奖励。一个share(贡献/股份)为一个矿池给客户端的一个合法的工作证明,同时这也是用来生成区块的工作证明,但是没有这么复杂,只需要很少的时间就能达到一个share。

矿池是比特币(bitcoin)等p2p密码学虚拟货币开采所必需的基础设施,一般是对外开放的团队开采服务器。其存在的意义是提升比特币开采的稳定性,使矿工薪酬趋于稳定,目前国内较为著名的比特币商业矿池有f2pool、btcc pool、bw pool、btc.com等。

关于矿池挖矿的方式,目前存在如下几种不同的方式。

slush方式:slush矿池基于积分制,较老的shares将比新的shares拥有更低的权重,以减少一轮中切换矿池的投机分子。

pay-per-share方式:该方式为立即为每一个share支付报酬。该支出来源于矿池现有的比特币资金,因此可以立即取现,而不用等待区块生成完毕或确认之后。这样可以避免矿池运营者幕后操纵。这种方法减少了矿工的风险,但将风险转移给了矿池的运营者。运营者可以收取手续费来弥补这些风险可能造成的损失。

luke-jr方式:该方式借用了其他方式的长处,如slush方式一样,矿工需要提供工作证明来获得shares,如puddinpop方式一样,当区块生成时马上进行支付。但是不像之前的方式,一个区块的shares,会被再次利用来生成下一个区块。为了区分参与矿工的交易传输费用,只有当矿工的余额超过1btc时才进行支付。如果没有达到1btc,那么将在下一个区块生成时进行累计。如果矿工在一周内没有提供一个share,那么矿池会将剩下的余额进行支付,不管余额是多少。

triplemining方式:该方式是将一些中等大小矿池的计算力合并起来,然后将获得奖励的1%按照各个矿池计算力的比例分发给矿池运营者。

p2pool方式:p2pool的挖矿节点工作在类似于比特币区块链的一种shares链上。由于没有中心,所以也不会受到dos攻击。和其他现有的矿池技术都不一样,在该方式下,每个节点工作的区块,都包括支付给前期shares的所有者及该节点自己的比特币。99%的奖励(50btc +交易费用)会平均分配给矿工,另外0.5%会奖励给生成区块的人。

puddinpop方式:一种使用“元哈希”技术的方式,使用特定的puddinpop挖矿软件,现在已经没有矿池使用这种方式了。

目前使用较多的方式为pay-per-share(pps),矿工使用起来也比较方便。

但从去中心化的角度来说,还是推荐p2pool,在避免dos攻击的同时,也能防止个别矿池拥有超大的计算力而对比特币网络造成威胁。不过p2pool的使用方式较pps更为复杂。

继续阅读