1. 首页 > 快讯

挖矿是什么意思?深度解析挖矿的逻辑和技术实现

3. 构建区块头,利用Version,Previousblockhash,Bits以及Curtime分别填充区块头对应字段,nNonce字段可默认置0。

4. 挖矿,矿工可在由nNonce,nTime,hashMerkleRoot提供的搜索空间里设计自己的挖矿策略。

5. 上交数据,当矿工挖到一个块后当立即使用submitblock接口将区块完整数据提交给节点客户端,由节点客户端验证并广播。

需要注意的是,与上文提到的GPU采用getwork挖矿一样,虽然getblocktemplate给矿工提供了巨大搜索空间,但矿工不应对一份请求数据挖矿太久,而应循环适时向节点索要最新区块和最新交易信息,以提高挖矿收益。

POOL

挖矿有两种方式,一种叫SOLO挖矿,另一种是去矿池挖矿。前文所述的在节点客户端直接启动CPU挖矿,以及依靠getwork+cgminer驱动显卡直接连接节点客户端挖矿,都是SOLO挖矿,SOLO好比自己独资买彩票,不轻易中奖,中奖则收益全部归自己所有。去矿池挖矿好比合买彩票,大家一起出钱,能买一堆彩票,中奖后按出资比率分配收益。理论上,矿机可以借助getblocktemplate协议链接节点客户端SOLO挖矿,但其实早已没有矿工会那么做,在写这篇文章时,比特币全网算力1600P+,而当前最先进的矿机算力10T左右,如此算来,单台矿机SOLO挖到一个块的概率不到16万分之一,矿工(人)投入真金白银购买矿机、交付电费,不会做风险那么高的投资,显然投入矿池抱团挖矿以降低风险,获得稳定收益更加适合。因此矿池的出现是必然,也不可消除,无论是否破坏系统的去中心化原则。

矿池的核心工作是给矿工分配任务,统计工作量并分发收益。矿池将区块难度分成很多难度更小的任务下发给矿工计算,矿工完成一个任务后将工作量提交给矿池,叫提交一个share。假如全网区块难度要求Hash运算结果的前70个比特位都是0,那么矿池给矿工分配的任务可能只要求前30位是0(根据矿工算力调节),矿工完成指定难度任务后上交share,矿池再检测在满足前30位为0的基础上,看看是否碰巧前70位都是0。

矿池会根据每个矿工的算力情况分配不同难度的任务,矿池是如何判断矿工算力大小以分配合适的任务难度呢?调节思路和比特币区块难度一样,矿池需要借助矿工的share率,矿池希望给每个矿工分配的任务都足够让矿工运算一定时间,比如说1秒,如果矿工在一秒之内完成了几次任务,说明矿池当前给到的难度低了,需要调高,反之。如此下来,经过一段时间调节,矿池能给矿工分配合理难度,并计算出矿工的算力。

矿池一直都是一个矛盾的存在,毫无疑问,矿池是中心化的,如上图所示,全网算力集中在几个矿池手里,网络虽然几千个节点同时在线,但只有矿池链接的几个点击拥有投票权,其他节点都只能行使监督权。矿池再一次将矿工至于“黑暗”之中,矿工对于区块链再次变得一无所知,他们只知道完成矿池分配的任务。

关于矿池,还有一个小插曲,在矿池刚出现时,反对声特别强烈,很多人悲观的认为矿池最终会导致算力集中,危及系统安全,甚至置比特币于死地。于是有人设计并实现了P2P矿池,力图将“抱团挖矿”去中心化,代码也都是开源的,但由于效率远不如中心化的矿池没能吸引太多算力,所谓理想很丰满,现实很骨感。

推荐几个比较成熟的开源矿池项目,有兴趣的读者可自行研究:

u PHP-MPOS,早期非常经典的矿池,很稳定,被使用最多,尤其山寨币矿池,后端使用Stratum Ming协议,源码地址https://github.com/MPOS/php-mpos

u node-open-mining-portal,支持多币种挖矿,源码地址https://github.com/zone117x/node-open-mining-portal

u Powerpool,支持混合挖矿,源码地址https://github.com/sigwo/powerpool

运行一个矿池需要考虑的问题很多,比如为了得到最及时的全网信息,矿池一般对接几个网络节点,而且最好分布在地球的几大洲。另外提高出块率,降低孤块率,降低空块率等都是矿池的核心技术问题,本文不能一一展开讨论,接下来只详细讨论一个问题,即矿池与矿工的具体配合工作方式——stratum协议。

STRATUM

矿池通过getblocktemplate协议与网络节点交互,以获得区块链的最新信息,通过stratum协议与矿工交互。此外,为了让之前用getwork协议挖矿的软件也可以连接到矿池挖矿,矿池一般也支持getwork协议,通过阶层挖矿代理机制实现(Stratum mining proxy)。须知在矿池刚出现时,显卡挖矿还是主力,getwork用起来非常方便,另外早期的FPGA矿机有些是用getwork实现的,stratum与矿池采用TCP方式通信,数据使用JSON封装格式。

先来说一下getblocktemplate遗留下来的几个问题:

矿工驱动:在getblocktemplate协议里,依然是由矿工主动通过HTTP方式调用RPC接口向节点申请挖矿数据,这就意味着,网络最新区块的变动无法及时告知矿工,造成算力损失。

数据负载:如上所述,如今正常的一次getblocktemplate调用节点都会反馈回1.5M左右的数据,其中主要数据是交易列表,矿工与矿池需频繁交互数据,显然不能每次分配工作都要给矿工附带那么多信息。再者巨大的内存需求将大大影响矿机性能,增加成本。

Stratum协议彻底解决了以上问题。

Stratum协议采用主动分配任务的方式,也就是说,矿池任何时候都可以给矿工指派新任务,对于矿工来说,如果收到矿池指派的新任务,应立即无条件转向新任务;矿工也可以主动跟矿池申请新任务。

现在最核心的问题是如何让矿工获得更大的搜索空间,如果参照getwork协议,仅仅给矿工可以改变nNonce和nTime字段,则交互的数据量很少,但这点搜索空间肯定是不够的。想增加搜索空间,只能在hashMerkleroot下功夫,如果让矿工自己构造coinbase,那么搜索空间的问题将迎刃而解,但代价是必要要把区块包含的所有交易都交给矿工,矿工才能构造交易列表的Merkleroot,这对于矿工来说压力更大,对于矿池带宽要求也更高。

Stratum协议巧妙解决了这个问题,成功实现既可以给矿工增加足够的搜索空间,又只需要交互很少的数据量,这也是Stratum协议最具创新的地方。

再来回顾一下区块头的6个字段80字节,这个很关键,nVersion,nBits,hashPrevBlock这3个字段是固定的,nNonce,nTime这两个字段是矿工现在就可以改变的。增加搜索空间只能从hashMerkleroot下手,这个绕不过去。Stratum协议让矿工自己构造coinbase交易,coinbase的scriptSig字段有很多字节可以让矿工自由填充,而coinbase的改动意味着hashMerkleroot的改变。从coinbase构造hashMerkleroot无需全部交易,如上图所示,假如区块将包含13笔交易,矿池先对这13笔交易进行处理,最后只要把图中的4个黑点(Hash值)交付给矿工,同时将构造coinbase需要的信息交付给矿工,矿工就可以自己构造hashMerkleroot(图中的绿点都是矿工自行计算获得,两两合并Hash时,规定下一个黑点代表的hash值总是放在右边)。按照这种方式,假如区块包含N笔交易,矿池可以浓缩成log2(N)个hash值交付给矿工,这大大降低了矿池和矿工交互的数据量。

Stratum协议严格规定了矿工和矿池交互的接口数据结构和交互逻辑,具体如下:

1. 矿工订阅任务

启动挖矿机器,使用mining.subscribe方法链接矿池

{“id”: 1, “method”: “mining.subscribe”, “params”: []}n //申请链接

{“id”: 1, “result”: [ [ ["mining.set_difficulty", "b4b6693b72a50c7116db18d6497cac52"], ["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"]], “08000002″, 4], “error”: null}n //返回数据

返回数据很重要,矿工需本地记录,在整个挖矿过程中都用到,其中:

u b4b6693b72a50c7116db18d6497cac52:给矿工指定初始难度,

u ae6812eb4cd7735a302a8a9dd95cf71f:订阅号ID

u 08000002:学名Extranonce1 ,用于构造coinbase交易

u 4:学名Extranonce2_size ,即Extranonce2的长度,这里指定4个字节

Extranonce1,和 Extranonce2对于挖矿很重要,增加的搜索空间就在这里,现在,我们至少有了8个字节的搜索空间,即nNonce的4个字节,以及 Extranonce2的4个字节。

2. 矿池授权

在矿池注册一个账号 ,添加矿工,矿池允许每个账号任意添加矿工数,并取不同名字以区分。矿工使用mining.authorize 方法申请授权,只有被矿池授权的矿工才能收到矿池指派任务。

{“params”: ["slush.miner1", "password"], “id”: 2, “method”: “mining.authorize”}n

{“error”: null, “id”: 2, “result”: true}n

3. 矿池分配任务

{“params”: ["bf", "4d16b6f85af6e2198f44ae2a6de67f78487ae5611b77c6c0440b921e00000000",

"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff20020862062f503253482f04b8864e5008",

"072f736c7573682f000000000100f2052a010000001976a914d23fcdf86f7e756a64a7a9688ef9903327048ed988ac00000000", ["76cffd68bba7ea661512b68ec6414438191b08aaeaec23608de26ac87820cbd02016","e5a796c0b88fe695949a3e7b0b7b1948a327b2f28c5dbe8f36f0a18f96b2ffef2016"],

“00000002”, “1c2ac4af”, “504e86b9”, false], “id”: null, “method”: “mining.notify”}

以上每个字段信息都是必不可少,其中:

u bf:任务号ID,每一次任务都有唯一标识符

u 4d16b6f85af6e2198f44ae2a6de67f78487ae5611b77c6c0440b921e00000000:前一个区块hash值,hashPrevBlock

u 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff20020862062f503253482f04b8864e5008:学名coinb1 ,构造coinbase的第一部分序列数据

u 072f736c7573682f000000000100f2052a010000001976a914d23fcdf86f7e756a64a7a9688ef9903327048ed988ac00000000:学名coinb2 ,构造coinbase的第二部分序列数据,

u ["76cffd68bba7ea661512b68ec6414438191b08aaeaec23608de26ac87820cbd02016","e5a796c0b88fe695949a3e7b0b7b1948a327b2f28c5dbe8f36f0a18f96b2ffef2016"]:学名merkle_branch,交易列表的压缩表示方式,即上图的黑点

u 00000002:区块版本号,nVersion

u 1c2ac4af:区块难度nBits

u 504e86b9:当前时间戳,nTime

有了以上信息,再加上之前拿到的Extranonce1 和Extranonce2_size,就可以挖矿了。

4. 挖矿

1) 构造coinbase交易

用到的信息包括Coinb1, Extranonce1, Extranonce2_size 以及Coinb2,构造很简单:

Coinbase=Coinb1 + Extranonce1 + Extranonce2 + Coinb2

为啥可以这样,因为矿池帮矿工做了很多工作,矿池已经构建了coinbase交易,系列化后在指定位置分割成coinb1和coinb2,coinb1和coinb2包含指定信息,比如coinb1包含区块高度,coinb2包含了矿工的收益地址和收益额等信息,但是这些信息对于矿工来说无关紧要,矿工挖矿的地方只是Extranonce2 的4个字节。另外Extranonce1是矿池写入区块的指定信息,一般来说,每个矿池会写入自己矿池的信息,比如矿池名字或者域名,我们就是根据这个信息统计每个矿池在全网的算力比重。

2) 构建Merkleroot

利用coinbase和merkle_branch,按照上图方式构造hashMerkleroot字段。

3) 构建区块头

填充余下的5个字段,现在,矿池可以在nNonce和Extranonce2 里搜索进行挖矿,如果嫌搜索空间还不够,只要增加Extranonce2_size为多几个字节就可轻而易举解决。

5. 矿工提交工作量

当矿工找到一个符合难度的shares时,提交给矿池,提交的信息量很少,都是必不可少的字段:

{“params”: ["slush.miner1", "bf", "00000001", "504e86ed", "b2957c02"], “id”: 4, “method”: “mining.submit”}

{“error”: null, “id”: 4, “result”: true}

slush.miner1:矿工名字,矿池用以识别谁提交的工作量

bf:任务号ID,矿池在分配任务之前,构造了Coinbase等信息,用这个任务号唯一标识

00000001:Extranonce2

504e86ed:nTime字段

b2957c02:nNonce字段

矿池拿到以上5个字段后,首先根据任务号ID找出之前分配任务前存储的信息(主要是构建的coinbase交易以及包含的交易列表等),然后重构区块,再验证shares难度,对于符合难度要求的shares,再检测是否符合全网难度。

6. 矿池给矿工调节难度

矿池记录每个矿工的难度,并根据shares率不断调节以指定合适难度。矿池可以随时通过mining.set_difficulty方法给矿工发消息另其改变难度。

{ “id”: null, “method”: “mining.set_difficulty”, “params”: [2]}

如上,Stratum协议核心理念基本解析清楚,在getblocktemplate协议和Stratum协议的配合下,矿池终于可以大声的对矿工说,让算力来的更猛烈些吧。

AUXPOW

在挖矿的发展历史上,还出现了一个天马行空的事情,即混合挖矿(Merge Mining)。域名币(Namecoin)最先使用混合挖矿模式,挂靠在比特币链条上,矿工挖比特币时,可以同时挖域名币,后来狗狗币(Dogecoin)也支持混合挖矿,挂靠在莱特币(Litecoin)链条上。混合挖矿使用Auxiliary Proof-of-Work (AuxPOW)协议实现,虽然混合挖矿不怎么流行,但是协议设计的很精巧,最初看到协议时我不禁感叹社区的力量之伟大,这种都能想出来。

以域名币的混合挖矿举例,比特币作为父链(Parent Blockchain),域名币作为辅链(Auxiliary Blockchain),AuxPOW协议的实现无需改动父链(比特币当然不会为了域名币做任何改动),但辅链需要做针对性设计,比如狗狗币改为支持混合挖矿时就进行了硬分叉。

AuxPOW的实现得益于比特币Coinbase的输入字段,中本聪当初不知有意无意,在此处只规定了长度限制,留了一片未定义区域。这片区域后来对比特币的发展产生深远影响,很多升级和优化都盯上这片区域,比如上文讲的Stratum协议。中本聪类似的有意无意情况还有很多,比如交易的nSequence字段,也是因为没有明确定义,被黑客盯上引发的“延展性”问题成了“门头沟”倒闭的替罪羊。再比如说nNonce,如果一开始定义的字节大一些,你比方说32字节,挖矿的演进就不需要以上讨论的那么多协议。

 2/4   首页 上一页 1 2 3 4 下一页 尾页

本文采摘于网络,不代表本站立场,转载联系作者并注明出处:http://www.longfuchaju.com//kuaixun/3475.html

联系我们

在线咨询:点击这里给我发消息

微信号:wx123456