硬盘危机——Chia 挖矿背后的原理与技术细节(二)

本文是《硬盘危机——Chia 挖矿背后的原理与技术细节》系列的第二篇文章

当一种虚拟资产要进行初始分派时,通常会要求参与者消耗现实世界中的稀缺有限资源(见上一篇文章)来换取获得该资产的机会。对于 Bitcoin 网络来说,稀缺有限资源即为计算哈希值的算力。而 Chia 则选择了存储空间时间作为稀缺有限资源。

在本篇文章中,我们将深入探讨 Chia 网络中的共识算法,了解 Chia 网络是通过何种方法向参与其中的用户分配虚拟资产的。本文主要参考自《Chia Consensus Algorithm》白皮书文档。

网络中的参与者们

要在虚拟资产网络中分配初始资产,需要制定网络中的共识算法(Consensus Algorithm),即通过何种协调手段,能够向消耗稀缺有限资源的、参与网络的用户分配初始虚拟资产。

在 Chia 网络中,用户可以作为不同角色参与其中,以不同身份参与网络的动机与收益略有不同。

农夫(矿工)

由于参与 Chia 网络不再以消耗计算能力作为必要条件,因此参与网络“挖矿”行为的用户被称为农夫(Farmers)。这些被称为农夫的节点通过存储绘图(plot)文件、完成存储空间证明的方式参与到共识算法之中。当然,这些节点也能获得相应的虚拟资产作为奖励。

农夫节点需要与完全节点(通常部署在同一台机器上,见下方)进行通信。同样,他们也需要与作为服务提供的其他收获者(Harvesters)通信。收获者通过查找绘图文件,以完成对于存储空间的证明过程。

时间领主(验证者)

在上一篇文章中,我们曾经介绍过一种可以被验证执行过一定次数的顺序函数——可验证延迟函数(Verifiable Delay Function,VDF)。时间领主的作用是创建时间证明,并且将区块信息注入 VDF 中,以参与共识算法。

完全节点

完全节点(Full Nodes)既可以是农夫,也可以是时间领主,或者可以单独地作为完全节点存在。完全节点负责广播存储空间与时间证明、创建新的区块、维护挂起交易池以及将区块提供给其他节点(以及用户使用的钱包)。

那么,问题来了……

Chia 网络中的共识算法为网络中的参与者提出了这样一个问题:在每次约隔 10 分钟的较小时间片(被称为“子槽”,sub-slots)内发布一次质询,在每次质询期间,所有农夫将会检查他们的 plot 文件是否可以构造相应的存储空间证明,就像摸彩票一样。当农夫找到符合要求的证明后,他们会向网络广播他们的证明方式。最终,当该子槽(时间片)中累计拥有 32 个获胜的证明后,该次质询便结束,网络将会开启下一次质询过程。网络会定期调整挑战的难度,以维持每次质询大约持续 10 分钟左右。

由于网络中的证明过程是在所有农夫间同步进行的,在每次质询时,这些不同的证明会被陆续注入到 VDF(可验证延迟函数) 中。农夫会跟随最“重”的链条,也就是上边拥有最多累积难度的链条(一般来说,就是最长的那条链)。

表示 Chia 网络中子槽运行原理的插图,在每个时刻同时存在三条链:质询链、注入的质询链以及奖励链。其中,注入的质询链被质询链以及奖励链指向。
图中展示了三个子槽,其中横轴代表时间(的流逝),虚线代表 VDF 的执行,即在时间轴上从左向右推进,箭头代表哈希依赖关系(一个指向另一个对象的对象包括第二个对象的哈希)

如上图,我们可以看到存在三次质询 \(c_1\)、\(c_2\) 与 \(c_3\)。在这些时间点,网络中的时间领主(timelord)会构造一些当作输入提供给 VDFs 的质询,质询为一些 256 位的哈希值。时间领主拿着这些哈希值,进行指定的迭代次数,开始为这次的挑战计算出一个 VDF。在图中,每个子槽之间进行了 1 亿次循环(迭代)。当 VDF 完成后,时间领主公布新的挑战和 VDF 的证明。每次子槽结束后,都会将信息注入到槽底中。

子槽(sub-slot)就是一定数量的 VDF 的迭代过程,可以理解成一段时间片,由于网络中会定期调整难度,每个子槽的持续时间总在 10 分钟左右。

子槽迭代次数:一个受难度定期调整的常数,决定了每个子槽中需要经过多少次迭代。

质询:一个 256 位的常数,作为 SHA-256 的输出,作为农夫针对绘图进行存储空间证明时使用。在相关的技术文章中,有时也被称为“质询哈希”(challenge hash)。

另外,上图中还提供给了我们一个额外的细节信息,即每一时刻同时存在三个 VDF(即图中的三条虚线)在同时运行。即挑战(Challenge)、注入挑战(Infused challenge)与奖励(Reward)链。每个 VDF 都出于不同的目的运行。我们将在下文中进行介绍。

标识与注入

我们来思考一个问题:每个子槽将持续 10 分钟左右,在这段时间内将会向 VDF 中注入 32 个获胜的证明(上文有提到)。然而任何基于共识的网络在运行时都会存在分叉(即,网络中的不同参与者获取到的网络的信息不同),分叉会导致一部分参与者的工作量最终被淘汰。如果我们以 10 分钟为粒度进行同步(Bitcoin 网络就是以 10 分钟为单位构建区块的),势必会造成一些工作量的浪费。该如何减小这部分浪费的工作量呢?一个直观的方法就是减小每次链上同步时间的间隔,Chia 网络提出了这样的解决方法……

挑战奖励链中,每个子槽都被分为 64 个更小的 VDF,每两个 VDF 之间存在一个点,被称为标识点(Signage point)。时间领主在到达每一个标识点时公布 VDF 的输出与证明,这样,全网在 64 个更小的区间内就拥有了同步的信息。除了注入挑战链以外,其余两个链都有标识点。每两个标识点之间的迭代次数 \(\frac{iterations}{64}\) 被称为“\(sp\) 迭代次数”。

当子槽开始时,开始时刻的点也是标识点。当网络中的进展到达 64 个点之中的任意一个标识点时,就会由完全节点与时间领主节点向网络中广播这些信息。农夫节点收到这些信息后,将会根据标识点的信息来生成针对 plot 的过滤器,农夫额外用来参考的信息包括绘图 ID 与该子槽中正在进行的具体的质询内容。如果该过滤器的二进制位以 9 个 0 开始,该 plot 就通过了该标识点的过滤器,并可以继续进行处理。对该标识点而言,这样就能过滤出网络中所有 plot 文件的 \(\frac{2^{9}-1}{2^{9}}\) 个,即 \(\frac{511}{512}\) 个 plot。

plot filter bits = sha256(plot id + sub slot challenge + signage point)

存储空间质询的证明是通过绘图过滤器的哈希值计算得出的:

pos challenge = sha256(plot filter bits)

利用这种质询方式,农夫从磁盘中为每个通过过滤器的绘图文件取回质量字符串(Quality string)。这个过程几乎是瞬间完成的,标识点是由存储空间证明的一部分衍生出来的哈希值(但整个存储空间证明还没有被检索出来)。

农夫计算每个存储空间证明所需的迭代次数,如果所需的迭代次数 \(<\) \(sp\)迭代次数,则存储空间证明有资格被纳入区块链,相当于这个节点摸彩票中奖了。此时,农夫节点从硬盘中获取整个存储空间证明(这比只获取质量要长),创建一个未完成的字块,并将其广播到网络中。绝大多数的证明需要的迭代次数都会很多,因为平均每个子槽中只有 32 个证明符合整个网络的要求。这个过程非常随机,所以存在大量的证明有可能合格,但是可能性非常小。标识点迭代次数是指从子槽开始到标识点的迭代次数。

接下来我们讨论注入质询链上发生的事情,注入迭代(Infusion iteration)是指一个子槽的开始,具有上述质量的区块可以被纳入区块链的迭代次数,计算方法为:

infusion iterations = (signage point iterations + 3 * sp interval iterations + required iterations) % sub slot iterations

注入迭代将在标识点之后的 3 到 4 个标识点之间进行。农夫必须在达到注入点之前提交他们的证明和区块。上述公式中的取模操作(%)是为了在网络进展到一个子槽的末端时将注入操作推迟到下一个子槽中进行。这一点将在后面展开。

在注入发生时,农夫的区块将与 VDF 的输出相组合,为 VDF 构造一个新的输入。我们以这种方式将农夫的区块注入 VDF 中。因此,该区块只有到达注入迭代的次数之后才变得完全有效,因为此时 VDF 的证明已经附着在该区块上。

表示注入点的演示图片。在图片中,从点 r1 开始经过了 32 个标识点(每个标识点之间的间隔为 sp迭代间隔)后到达了一次标识点迭代,在该迭代后的三个标识点后即到达注入迭代。
时间领主为标识点和注入点都创建了证明,但只为后者注入(更改 VDF 类组)。图中的矩形代表一次注入操作,从每个矩形处开始一个新的 VDF。图中 Sp_iterval_iterrs=3.125M

在图中,为了使 \(b_1\) 区块有效(完成),必须为该区块包含两个 VDF 证明:一个从 \(r_1\) 到标识点,另一个从标识点到 \(b_1\)。但别忘了,图中只是展示了一个 VDF,但实际上有更多(因为有三个 VDF 链,还记得吗)。农夫在到达标识点时即创建证明 \(b_1’\),但此时证明尚未完成,因为直到到达 VDF 的注入点时,证明才会被注入。在到达注入点后,农夫将证明 \(b_1’\) 注入到 VDF 中,成为 \(b_1\),以完成区块的构造。

图中的子槽间隔为 200M,\(sp\) 间隔为 3.125M。我们以农夫拥有 1000 个绘图文件为例:对于每 64 个标识点来说,当它们每隔约 9 秒(即每 3.125 兆次迭代)被释放到网络中时,农夫计算出绘图过滤器,然后检索有多少绘图通过了过滤器。对于每个标识点通过过滤器的绘图,农夫计算(其)所需的迭代次数。本例中,农夫在整个子槽(2.2879M)中只有一次得到所需迭代次数\(<\)3.125M。上图中,这是第 14 个标识点,注入迭代次数的计算方法是:

infusion iterations = signage point iterations + 3 * sp interval iterations + required iterations
= 14*3.125M + 3 * 3.125M + 2.2879M
= 55.4129M

当(有幸!)在第 14 个注入点处中奖之后,农夫即构造整个存储空间证明、构造区块、选取要记录其中的交易并将区块广播到网络中。之后过了几秒钟的时间(最多为一次注入间隔的时间),信息便到达了时间领主节点处,时间领主节点负责将其注入到区块中,创建注入点 VDFs。有了这些 VDFs,区块即构造完成,并添加到区块链中。

\(sp\) 间隔迭代次数:定义为 floor(sub-slot iterations / 64)

标识点:在一次子槽中质询与奖励链上的 64 个间隔点,用于 VDF 的周期性释放之用。在每个标识点上,一个 VDF 输出被创建且被广播到网络中。子槽中的第一个标识点的内容是质询本身。区块中的存储空间证明必须满足区块中的一个标识点的要求。

所需迭代次数:使用质量字符串算出的一个数字,用于选取用于合格构造区块的存储空间证明。存储空间证明中的最大变数是所需的迭代次数太高,以至于没有符合条件以注入区块的证明。这个数字也被用于计算注入点。

注入点:从质询点处经过注入迭代次数后到达的点,用于特定质询以及注入迭代次数下的存储空间证明。在该点处,农夫的区块得以注入奖励链中的 VDF。区块中的注入点总是在区块中的标识点后 3-4 个标识点远处,即计算方式为:signage point iterations + 3 * sp interval iterations + required iterations

标注点与注入点之间的间隔是被故意设计的。这样有许多好处:包括防范鼓励和自私的耕种(Farming)行为,以及没有 VDF 暂停。这个大约为 30 秒的延迟窗口给予农夫足够的时间来签名,而不用耽误子槽 VDF 的运行。遵循规范的农夫在每次存储空间证明中只签署一个标识点,意味着攻击者不易重新组织区块链。

链的权重

链的权重(Weight)是该链上的难度之和,通过当前区块的难度以及所有历史区块的难度相加得到。诚实的全节点必须选择最长的链,即已知权重最大的链。这是一个至关重要的要求,与 Bitcoin 的最重链规则相同。由于这一规则,拥有不到 50% 空间且没有 VDF 优势的攻击者将难以赚取超过其公平份额的收益,因为他们必须幸运地创造出比诚实链更多的奖励链块。此外,农民只应在对应于最重链的质询上耕作。

VDF 速度和总空间量对重量来说都很重要,这些方面的变化会引发难度调整。如果空间量增加,每个槽会创建超过 32 个块,所以难度要相应增加。如果网络 VDF 速度增加,每 10 分钟就会创建超过 32 个块,因此难度(和子槽迭代数)必须增加。

然而,一个独享稍快 VDF 的农民不可能轻易获得比拥有正常速度 VDF 的农民更多的奖励。如果攻击者试图将链上的一个区块变成孤儿区块,拥有更快的 VDF 也无济于事,因为攻击者的链上的区块会更少(因此重量也更低)。农民必须在他们所建造的区块上签名,而且他们只会在最高重量的链上建造。

总结

在本篇文章中,我们主要讨论了在质询奖励链上标识点与注入点的运作方式,以及用于防范 51% 攻击的链的权重的概念。

在后续文章中,我们将继续讨论有关 Chia 网络的参与者在多条链上配合参与的行为。以及用于进行存储空间证明的重要计算方式和概念。