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

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个数据项之上,并且以十六进制的小端结尾方式连接在一起。

>>> import hashlib
>>> header_hex = ("01000000" +
    "81cd02ab7e569e8bcd9317e2fe99f2de44d49ab2b8851ba4a308000000000000" +
    "e320b6c2fffc8d750423db8b1eb942ae710e951ed797f7affc8892b0f1fc122b" +
    "c7f5d74d" +
    "f2b9441a" +
     "42a14695")
>>> header_bin = header_hex.decode('hex')
>>> hash = hashlib.sha256(hashlib.sha256(header_bin).digest()).digest()
>>> hash.encode('hex_codec')
  '1dbd981fe6985776b644b173a4d0385ddc1aa2a829688d1e0000000000000000'
>>> hash[::-1].encode('hex_codec')
  '00000000000000001e8d6829a8a21adc5d38d0a473b144b6765798e61f98bd1d'

实际的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值就为如下形式:

0x0404cb×28×(0x1b - 3) = 0x00000000000404CB000000000000000000000
  ???  000000000000000000000000000

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

0x00000000FFFF0000000000000000000000000000000000000000000000000000/
0x00000000000404CB000000000000000000000000000000000000000000000000
= 16307.420938523983 (bdiff)

或者:

0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF/
0x00000000000404CB000000000000000000000000000000000000000000000000
= 16307.669773817162 (pdiff)

其中:0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF是挖矿机使用的最大目标Hash值。而0x00000000FFFF00000000000000000000000000000
00000000000000000000000则是比特币网络使用的浮点编码类型,后面的位数被缩短了。
下面是一个快速计算比特币难度的方法,这里使用的算法是修改的泰勒序列(读者可以参考wikipedia上的教程),并且依赖记录来转换难度计算。

#include <iostream>
#include <cmath>
inline float fast_log(float val)
{
  int * const exp_ptr = reinterpret_cast <int *>(&val);
  int x = *exp_ptr;
  const int log_2 = ((x >> 23) & 255) - 128;
  x &= ~(255 << 23);
  x += 127 << 23;
  *exp_ptr = x;

  val = ((-1.0f/3) * val + 2) * val - 2.0f/3;
  return ((val + log_2) * 0.69314718f);
} 

float difficulty(unsigned int bits)
{
    static double max_body = fast_log(0x00ffff), scaland = fast_log(256);
    return exp(max_body - fast_log(bits & 0x00ffffff) + scaland * (0x1d - ((bits & 0xff000000) >> 24)));
}

int main()
{
    std::cout << difficulty(0x1b0404cb) << std::endl;
    return 0;
}

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

import decimal, math
l = math.log
e = math.e
print 0x00ffff * 2**(8*(0x1d - 3)) / float(0x0404cb * 2**(8*(0x1b - 3)))
print l(0x00ffff * 2**(8*(0x1d - 3)) / float(0x0404cb * 2**(8*(0x1b - 3))))
print l(0x00ffff * 2**(8*(0x1d - 3))) - l(0x0404cb * 2**(8*(0x1b - 3)))
print l(0x00ffff) + l(2**(8*(0x1d - 3))) - l(0x0404cb) - l(2**(8*(0x1b - 3)))
print l(0x00ffff) + (8*(0x1d - 3))*l(2) - l(0x0404cb) - (8*(0x1b - 3))*l(2)
print l(0x00ffff / float(0x0404cb)) + (8*(0x1d - 3))*l(2) - (8*(0x1b - 3))*l(2)
print l(0x00ffff / float(0x0404cb)) + (0x1d - 0x1b)*l(2**8)

前难度可以通过http://blockexplorer.com/q/getdiff?iculty来获得,下一个难度可以通过http://blockexplorer.com/q/estimate来获得。难度的变化情况可以查看http://bitcoin.sipa.be/
最大难度大约等于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更为复杂。

时间: 2024-12-25 02:27:00

《区块链开发指南》一一1.3 挖矿、矿池的相关文章

《区块链开发指南》一一导读

前 言 Preface 比特币于2009年诞生,在很长一段时间内,人们只知比特币,不知区块链.从2015年开始,区块链像狂风一样席卷全球,倍受金融界和科技界的关注:2015年年底,区块链技术逐渐得到国内金融界和科技界的了解和认同. 区块链行业的蓬勃发展源于区块链有可能给各行业带来巨大的变革.麦肯锡在2016年年初发布报告,指出区块链技术将在未来五年内颠覆众多行业,特别是银行业和保险业:埃森哲预测到2025年,区块链技术每年可帮助全球8大投资银行节省80亿美元至120亿美元的基础设施成本. 全球金

《区块链开发指南》一一2.3 挖矿算法解析

2.3 挖矿算法解析 2.3.1 PoW挖矿算法及分析PoW(Proof of Work),即工作证明.也就是说,你获得多少货币,取决于你挖矿贡献的有效工作,比如,你的电脑性能越好,分给你的矿就会越多,即根据你的工作证明来执行货币的分配.大部分的虚拟货币,比如比特币.莱特币等,都是基于PoW模式的虚拟货币(算力越高.挖矿时间越长,你获得的货币就会越多).第1章已经说过挖矿算法其实就是通过一个Hash函数找到一个满足当前难度的Nonce(包含在区块头里面)的值,Hash函数输入数据的长度是任意的,

《区块链开发指南》一一2.4 Sidechains

2.4 Sidechains Sidechains(侧链)实质上不是指特定的某个区块链,而是指那些遵守侧链协议的所有区块链,这个词是针对比特币主链来说的.侧链协议是指可以让比特币安全地从比特币主链转移到其他区块链,同时又可以让其他区块链上的货币安全返回到比特币主链的一种协议. 所以从某种程度上来说,现在市面上的所有区块链,例如以太坊.莱特币.狗狗币等区块链都可以成为侧链应用.侧链的实现具有重大意义,它意味着比特币可以在不同的区块链上流通,其应用范围和应用前景会更加广泛.一旦侧链的应用流行起来,有

《区块链开发指南》一一1.5 合约应用案例

1.5 合约应用案例 1.4节的脚本系统,详细说明了脚本的运行原理,本节将描述在实际应用场景中如何使用脚本系统构建合约应用.1.5.1 合约应用原理 每个比特币交易都有一个或多个输入和输出,每个输入或输出都有一个小的纯函数与之相关联,称为脚本,脚本可包含简化形式交易的签名. 每个交易都有一个锁定时间,使得该交易处于特定状态并且可被新交易替换,直至锁定时间来临.预定时间可以是块索引或时间戳(这两个因素使用同一个内存项,小于5亿是块索引,大于5亿是时间戳).当一个交易的锁定时间到了,则称之为终结.

《区块链开发指南》一一2.5 最新比特币技术

2.5 最新比特币技术 随着区块链的不断发展,区块链技术也在飞速发展,不仅已经有了相对成熟的应用,还有一些原型应用也被提出,下面将介绍几个现在比较流行的在区块链中的应用.2.5.1 IBLT 比特币系统(Bitcoin)需要矿工(或者说矿池)及全节点的分散化,以实现某些人认为的比特币核心属性:抗审查性(censorship resistance).因此,区块大小的争议也意味着是一种权衡.更大的区块,允许比特币网络可以承载更多的交易,但也会带来更多的问题,它需要更多的时间来传播交易,虽然这有利于大

《区块链开发指南》一一第1章 区块链基础

第1章 区块链基础 区块链究竟是什么?狭义地说,区块链就是比特币的底层技术:不过,经过7年的发展,区块链已经不再"依附于"比特币,而是独立地发展成为了一种革命性的技术,比特币则是区块链最大.最成功的应用.从技术层面来看,区块链是一个基于共识机制.去中心化的公开数据库.共识机制是指在分布式系统中保证数据一致性的算法:去中心化是指参与区块链的所有节点都是权力对等的,没有高低之分,同时也指所有人都可以平等自由地参与区块链网络,唯一的限制就是个人自己的选择:公开数据库则意味着所有人都可以看到过

《区块链开发指南》一一1.2 区块和区块链

1.2 区块和区块链 比特币网络中,数据会以文件的形式被永久记录,我们称这些文件为区块.一个区块是一些或所有最新比特币交易的记录集,且未被其他先前的区块记录.可以将区块想象为一个城市记录者其记录本上单独的一页纸(对房地产产权的变更记录),或者是股票交易所的总账本.在绝大多数情况下,新区块会被加入到记录的最后(在比特币中的名称为区块链),一旦写上,就再也不能改变或删除.每个区块记录了它被创建之前发生的所有事件.1.2.1 区块结构一个区块的结构如表1-2所示. 每个区块都包括了一个被称为"魔法数&

《区块链开发指南》一一第2章 区块链进阶 2.1 外带数据

第2章 区块链进阶 2.1 外带数据 区块链的外带数据是指那些保存在区块链上但不进行货币交易的信息,比如需要永久保存的信息,前面提到过存在于区块链上的每一笔交易都有一个输入和输出,区块链的外带数据也是采用类似的方式来存储的.如果有在比特币上永久存储数据的需求,那么你目前有两种选择,即:"OP_RETURN"和"Multi-Signatures".OP_RETURN是指在每个交易的公钥脚本中嵌入OP_RETURN操作码,之后放置外带数据:Multi-Signature

《区块链开发指南》一一2.2 Counterparty

2.2 Counterparty 合约币Counterparty是以合约币协议运行的全套金融工具,合约币协议建立在比特币块链的基础之上,把比特币块链当成可信的时间戳服务和可信的信息发布证明. 目前合约币已经实现了众多的技术创新点,比如燃烧证明.合约.去中心化XCP与BTC交易所.赌约或期货.资产或股份发行.分红等.Counterparty是建立在比特币协议上的传输层,用于建立和使用去中心化的财务工具协议.简单来说,可以将XCP理解为很多"小的BTC"即XCP="小的BTC&q