主页 > imtoken官网app > 挖矿是什么意思?深度剖析挖矿逻辑与技术实现

挖矿是什么意思?深度剖析挖矿逻辑与技术实现

imtoken官网app 2023-06-05 05:57:38

挖矿是进行算法计算的过程。 从计算机和代码的角度来看,它是一个重复执行Hash函数并检测执行结果的具体过程。 和讨论算法一样,挖矿也是在采用POW共识机制的前提下讨论的。 大家已经很清楚,挖矿是从CPU挖矿开始,过渡到GPU挖矿,最后演变成现在的ASIC(专业矿机)挖矿时代。 本文分析了逻辑设计和技术实现。

挖矿的进化是硬件进化的过程,也是软件进化的过程,尤其是软硬件对接协议的完善过程。 因此,本文直接将与挖矿相关的几个核心协议作为字幕,逐条展开讨论。

在查看文章时,我发现“矿工”一词使用含糊不清。 这种情况在英国文学中也类似。 在日常交流中,一般指拥有矿机的人。 本文关注区块链,挖矿程序或机器统称为矿工。

矿业

本节讨论挖矿原理。 首先分析比特币的区块头(Blockheader)结构。 我们说挖矿的本质就是执行Hash函数的过程,而Hash函数是单输入单输出的函数。 输入数据是区块头。 比特币区块头中有 6 个字段:

int32_t n版本; //版本号,4字节

uint256 hashPrevBlock; //上一个区块的区块头哈希值,32字节

uint256 hashMerkleRoot; // 该区块包含的所有交易结构的 Merkle 根,32 字节

uint32_t nTime; //Unix时间戳,4字节

uint32_t nBits; //记录这个区块的难度,4字节

uint32_t nNonce; //随机数,4字节

如上,每次比特币挖矿都是对这80个字节进行两次连续的SHA256运算(SHA256D)比特币控制台怎么输入命令,运算结果是固定的32个字节(二进制256位)。

以上6个字段的情况不同,

nVersion,块版本号,只会在升级时改变。

hashPrevBlock,由前一个块决定。

nBits,由全网共同决定,每2016个区块重新调整一次,调整算法是固定的。

所以,以上三个字段可以理解为固定的,对每个矿工来说都是一样的。 矿工可以自由调整的地方就是剩下的3个字段,

nNonce,提供 2^32 个可能的值

nTime,其实这个字段能提供的取值空间是非常有限的,因为合理出块时间是有一个范围的,这个范围是根据上一个出块时间来确定的,如果太早或者太超前上一个块时间,它会被其他节点拒绝拒绝。 值得一提的是,后一个区块的出块时间略早于前一个区块的时间,这是允许的。 一般来说,矿工会直接使用机器当前的时间戳。

hashMerkleRoot,理论上提供了2^256种可能性。 该字段的变化来自于添加或删除包含在区块中的交易,或更改顺序,或修改 Coinbase 交易的输入字段。

根据Hash函数的特点,即使三个字段中的任何一个发生变化,都会引起Hash运算结果的巨大变化。 在 CPU 挖矿时代,搜索空间主要由 nNonce 提供。 在矿机时代,nNonce提供的4个字节远远不够,搜索空间转向hashMerkleRoot。

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

比特币挖矿的逻辑过程如下:

1. 打包交易,检索待确认的交易内存池,选择待入块的交易。 矿工可以任意选择,甚至不选择(空块),因为每个块都有容量限制(目前1M),所以矿工不能无限选择。 对于矿工来说,最合理的策略是先将待确认的交易集合按照手续费排序,然后从高到低尽量包含最多的交易。

2. 构建Coinbase,确定区块包含的交易集后,即可统计出该区块的交易总手续费。 结合产出规则,矿工可以计算出自己在该区块的收益。

3.构造hashMerkleRoot,为所有交易构造Merkle数。

4.填写其他字段以获得完整的区块头。

5.哈希运算,对区块头进行SHA256D运算。

6、如果验证结果遇到困难,将向全网广播,挖出下一个区块; 如果不满足难度,根据一定的策略改变上述其中一个字段,再进行Hash计算和验证。

合格区块条件如下:

SHA256D(Blockherder) < F(nBits)

其中SHA256D(Blockherder)为挖矿结果,F(nBits)为难度对应的目标值,均为256位,均视为大整数,直接比较大小判断是否满足难度要求。

为了节省区块链存储空间,将256位的目标值通过一定的变换和无损压缩存储在32位的nBits字段中。 具体转换方法是拆分使用4个字节的nBits。 第一个字节表示右移的位数,用V1表示,后3个字节记录数值,用V3表示,则:

另外,难度是有下限的,也就是说有一个最大值。 比特币的最小难度值为nBits=0x1d00ffff,对应的最大目标值为:0x00000000FFFF00000000000000000000000000000000000000000000000000

因此,挖矿可以比作抛硬币。 例如,有 256 个硬币,给定数字 1、2、3...256。 每进行一次哈希运算,就如同抛硬币一样。 同时抛出256个硬币,落地后要求个数前n个硬币全部正面朝上。

设置生成

Setgenerate 协议接口代表了 CPU 挖掘的时代。

中本聪在他的论文中描述了“1 CPU 1 Vote”的理想数字民主概念。 在初始版本中,客户端具有挖矿功能。 客户端挖掘非常简单。 当然,需要先完成同步数据,才能进行挖矿。 有很多算力很低的山寨币,还是直接用客户端挖的。 有两种方式开始挖矿:

1)在配置文件中设置gen=1,然后启动客户端,节点会自行开始挖矿。

2)客户端启动后,使用RPC接口setgenerate控制挖矿。

如果使用经典QT客户端,点击“帮助”菜单,打开“调试窗口”,在“控制台”中输入以下命令:setgenerate true 2,然后回车,客户端开始挖矿,数量behind代表挖矿线程如果想关闭挖矿,在控制台使用如下命令:setgenerate false,可以使用getmininginfo命令查看挖矿状态。

节点挖矿过程也很简单:

构造区块,初始化区块头的各个字段,计算Hash并对区块进行验证,如果失败则自增nNonce,然后进行计算验证等。 在CPU挖矿时代,nNonce提供的4字节搜索空间完全够用(4字节有4G的可能,单核CPU的SHA256D算力一般在2M左右)。 事实上,nNonce 只是在遍历了两个字节后才返回。 转到重构块。

找份工作

getwork协议代表GPU挖矿时代。 需求主要是由于挖矿程序与节点客户端的分离,以及区块链数据与挖矿组件的分离。

使用客户端节点直接挖矿需要同步完整的区块链,数据和程序紧密结合。 也就是说,如果有多台电脑进行挖矿,每台电脑都需要同步一个单独的区块链数据。 这实际上是没有必要的。 对于矿工来说,至少一个全节点就够了。 同时,GPU挖矿时代的到来也需要一种协议来与客户端节点进行交互。

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

getwork的核心设计思想是:

区块由节点客户端构建,然后将区块头数据交给外部挖矿程序。 挖矿程序遍历nNonce进行挖矿,验证通过后返回给节点客户端。 节点客户端验证通过后向全网广播。

如前所述,区块头一共有80个字节。 由于没有待确认的区块链数据和交易池,nVersion、hashPrevBlock、nBits和hashMerkleRoot这4个字段必须由节点客户端提供共计72字节。 挖矿程序主要是增量遍历nNonce,必要时可以微调nTime字段。

对于显卡GPU,不用担心nNonce缺少4字节搜索空间,而且挖矿程序从节点客户端拿到一条数据后,不要工作太久,否则就是这个区块很可能已被其他矿工占用。 人挖,继续挖只能做无用功。 对于比特币来说,虽然它被设计为每 10 分钟出一个块,但一个好的策略应该是在几秒钟内重新向节点申请新的挖矿数据。 对于显卡来说,运行SHA256D的算力一般在200M~1G之间。 nNonce提供了4G的搜索空间,也就是说再好的显卡也能支持4秒左右。 调整一次nTime可以再挖4秒。 这一次绰绰有余。

节点提供RPC接口getwork,有一个可选参数。 如果没有参数,就是申请挖矿数据。 如果有参数,就是提交挖出的区块数据。

不带参数调用getwork,返回数据如下:

{

“中间状态”:“9226a024e0b77f61d49fd5ffdf828c6b5c4330c61ea2778c606a8e49d4ad8bd6”,

“data” :”00000002e9337bac28ee28a949d2140f9fb0a0ab740acfd739d7bcf67ca31c2301db858ad2ca54d92c8c1cded715922c4df2b07d9f10fa1a6cf3db7e949b320615761ed4581c76f21b12d87500000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000”,

“hash1”:“000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000

“目标”:“0000000000000000000000000000000000000000000000075d8120000000000”

}

数据字段

一共128字节(80个块头字节+48个完成字节),因为SHA256将输入数据分成固定长度的切片,每个切片为64字节,输入总长度必须是64字节的整数倍,输入length一般不满足要求,按照一定的规则在metadata末尾完成数据。 其实对于挖矿来说,补全数据是固定的,这里不需要提供。 外部挖矿软件可以自行完成。 连nNonce字段都不需要提供,数据只需要提供至少前76个字节即可。 nTime字段也是必不可少的,外部挖矿程序需要参考节点提供的出块时间来调整nTime。

目标领域

即当前区块难度目标值,采用小端字节序,需要翻转后才能使用。

其实对于外部挖矿程序来说,data和target这两个字段就可以正常挖矿,但是getwork协议充分考虑了各种情况,尽量帮助外部挖矿程序做力所能及的事情,额外提供了两个字段,data字段返回完整补全数据也是基于这个思路。

中州油田

如上所述,SHA256 对输入数据进行拆分。 矿工拿到数据后,第一次分裂(前64字节)是固定的,midstate就是第一次分裂的计算结果。 该节点有助于计算 Out。

因此,借助midstate字段,外部挖矿程序仅用44字节的数据就可以正常挖矿:32字节的midstate + 第一个分片中剩余的12(76-64)字节的数据。

Hash1字段

比特币挖矿每次都需要连续执行两次 SHA256。 第一次执行的结果是32字节,需要补充32字节的数据,组成64字节作为第二次执行SHA256的输入。 hash1为补全数据,同理hash1也是固定的。

外部挖矿程序挖出合格区块后,再次调用getwork接口,将修改后的数据字段提交给节点客户端。 节点客户端返回的数据也必须是128字节。

每次外部调用不带参数的getwork,节点客户端都会构造一个新块。 在返回数据之前,新区块必须完全存储在内存中,并使用 hashMerkleRoot 作为唯一标识符。 节点使用一个Map来存储所有构造的Block,当下一个block已经被别人挖出后,立即清除Map。

getwork接收到一个参数后,首先从参数中提取出hashMerkleRoot,在Map中找到之前保存的区块,然后从参数中提取出nNonce和nTime填充到区块对应的字段中,即可对区块进行验证. 如果难度满足要求,说明已经挖出一个区块,节点向全网广播。

getwork协议是最早版本的挖矿协议,实现了节点与挖矿的分离。 经典的 GPU 挖矿驱动 cgminer、sgminer 和 cpuminer 都使用 getwork 协议进行挖矿。 Getwork + cgminer一直是非常经典的组合。 许多新算法上线后,很快就被移植到cgminer中。 即使是现在,除了 BTC 和 LTC 之外,许多其他山寨币仍在使用 getwork 协议进行挖矿。 矿机出现后,挖矿速度有了很大的提升,目前比特币矿机算力已经达到10T/s的水平。 而getwork只为外部挖矿程序提供了总共4G的32字节的搜索空间。 如果继续使用getwork协议,矿机需要频繁调用RPC接口,显然是行不通的。 现在BTC和LTC节点都禁用了getwork协议,转而使用更新更高效的getblocktemplate协议。

获取块模板

getblocktemplate协议诞生于2012年年中,当时矿池已经出现。 矿池使用getblocktemplate协议与节点客户端交互,使用stratum协议与矿工交互。 这是最典型的矿池建设模式。

与getwork相比,getblocktemplate协议最大的不同在于getblocktemplate协议允许矿工自行构建区块。 这样,节点和挖矿就完全分离了。 对于getwork来说,区块链是黑暗的,getwork对区块链一无所知,他只知道修改data字段的4个字节。 对于getblocktemplate来说,整个区块链是透明的,getblocktemplate掌握了区块链上所有与挖矿相关的信息,包括需要确认的交易池,getblocktemplate可以选择包含在区块中的交易。

getblocktemplate开发出来之后,就不是一成不变的了。 客户端在后续的每个版本都进行了升级和改动,主要是增加了一些字段,但核心概念和核心字段没有变化。 目前比特币客户端返回的数据如下。 考虑到空间限制,交易字段(transactions)只保留一笔交易数据。 事实上,根据目前的实际情况,交易池中有数万笔交易需要实时确认。 满了(1M容量限制),再加上附加信息,所以每次调用getblocktemplate基本上返回1.5M左右的数据,和getwork的几百字节不一样。

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

下面简单分析一下其中的一些核心领域,

Version、Previous blockhash、Bits这三个字段分别是指区块版本号、前一个区块Hash、难度。 矿工可以直接用值填充区块头的相应字段。

Transactions,交易的集合,不仅给出了每笔交易的十六进制数据,还给出了hash、交易手续费等信息。

Coinbaseaux,如果有要写入区块链的信息,就放在这个字段里,类似于中本聪的创世块声明。

Coinbasevalue,挖出下一个区块的最大利润值,包括新币的发行量和交易手续费,如果矿工在Transactions字段中包含所有交易,则可以直接用这个值作为coinbase输出。

Target,区块难度目标值。

Mintime是指下一个区块时间戳的最小值,Curtime是指当前时间。 这两个时间作为矿工调整nTime字段的参考。

Height,下一个区块的难度,目前的协议规定这个值要写入coinbase的指定位置。

矿工拿到数据后,挖矿步骤如下:

1.构造coinbase交易,涉及到的字段包括Coinbaseaux、Coinbasevalue、Transactions、Height等,当然最重要的是指定一个收益地址。

2. 构建hashMerkleRoot,将coinbase放在transactions字段包含的交易列表之前,然后对相邻的交易成对进行SHA256D计算,最后构建交易的Merkle树。 由于coinbase有大量字节可供矿工自由使用,交易列表也可以随意交换或增删,因此hashMerkleRoot的价值空间几乎可以说是无限的。 事实上,getblocktemplate 协议设计的主要目标就是让矿工获得这个巨大的搜索空间。

3.构建区块头,使用Version、Previousblockhash、Bits和Curtime分别填充区块头的相应字段,nNonce字段可以默认设置为0。

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

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

需要注意的是,类似于上述使用getwork挖矿的GPU,虽然getblocktemplate为矿工提供了巨大的搜索空间,但矿工不应挖太长时间的请求,而应循环并及时向节点询问最新的区块Block和最新交易信息,增加挖矿收益。

水池

挖矿有两种方式,一种叫SOLO挖矿,一种叫矿池挖矿。 上面说了在节点客户端直接启动CPU挖矿,依靠getwork+cgminer驱动显卡直接连接节点客户端挖矿,都是SOLO挖矿。 SOLO就像是用自己的钱买彩票。 赢得奖品并不容易。 自己的。 去矿池挖矿就像一起买彩票一样。 大家一起出钱比特币控制台怎么输入命令,可以买一堆彩票。 中奖后,按照投资比例分配收益。 理论上矿机可以使用getblocktemplate协议链接节点客户端进行SOLO挖矿,但实际上没有矿工会这么做。 写这篇文章时,比特币全网算力为1600P+,目前最先进的矿机算力在10T左右,如此计算,单台矿机SOLO挖到一个区块的概率为不到 160,000 分之一。 矿工(人)投入真金白银买矿机、交电费,不会做这种高风险的投资。 显然,更适合投资矿池群挖,以降低风险,获得稳定的收益。 因此,矿池的出现是必然的,也无法被淘汰,无论它是否破坏了系统去中心化的原则。

矿池的核心工作是给矿工分配任务,统计工作量,分配收益。 矿池将区块的难度分成许多难度较小的任务,发送给矿工进行计算。 矿工完成一个任务后,将工作量提交给矿池,称为提交份额。 如果全网出块难度要求Hash运算结果前70位为0,那么矿池分配给矿工的任务可能只要求前30位为0(根据矿工计算调整)力量)。 矿工完成指定难度任务后提交份额,矿池在前30位为0的基础上再次校验,看前70位是否偶然全为0。

矿池会根据每个矿工的算力分配不同难度的任务。 矿池如何判断矿工算力的大小来分配合适的任务难度? 调整思路与比特币区块难度相同。 矿池需要依赖矿工。 share rate,矿池希望给每个矿工分配的任务足够矿工计算一段时间,比如1秒,如果矿工在一秒内完成了几个任务,就说明当前给定的难度矿池很低。 它需要被提高,反之亦然。 这样,经过一段时间的调整,矿池就可以给矿工分配合理的难度,计算出矿工的算力。

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

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

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

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

u PHP-MPOS,早期非常经典的矿池,非常稳定,用的最多,尤其是山寨币矿池,后端采用Stratum Ming协议,源码地址

u node-open-mining-portal,支持多币种挖矿,源地址

u Powerpool,支持混合挖矿,源码地址

运行矿池需要考虑的问题很多。 例如,为了获取全网最及时的信息,矿池通常会连接多个网络节点,最好分布在地球的几大洲。 此外,提高出块率、降低孤块率、降低空块率都是矿池的核心技术问题。 本文无法一一讨论。 接下来我们只详细讨论一个问题,即矿池和矿工的具体工作方式。 - 层协议。

地层

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

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

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

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

数据负载:如前所述,今天正常调用getblocktemplate会返回1.5M左右的数据,主要数据为交易列表,矿工和矿池之间需要频繁交换数据。 这么多信息。 此外,巨大的内存需求将极大地影响矿机的性能并增加成本。

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

Stratum协议采用主动分配任务的方式,即矿池可以随时为矿工分配新的任务。 对于矿工而言,如果收到矿池分配的新任务,应立即无条件切换到新任务; 矿工也可以主动向矿池申请新的任务。

现在的核心问题是如何让矿工获得更大的搜索空间。 如果参考getwork协议,只允许矿工改变nNonce和nTime字段,交互数据量很小,但是这个搜索空间肯定不够用。 如果想增加搜索空间,只能在hashMerkleroot上下功夫。 如果让矿工自己构建coinbase,那么搜索空间的问题就解决了,但代价是必须把区块中包含的所有交易交给矿工,这样矿工才能构建自己的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值)交给矿工,同时time 将构建 coinbase 所需的信息传递给矿工,矿工可以自己构建 hashMerkleroot(图中绿点是矿工自己计算的,对)。 这样,如果区块中包含N笔交易,矿池可以将其压缩成log2(N)个哈希值交付给矿工,大大减少了矿池与矿工之间交换的数据量。

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