门罗币的工作量证明如何运作

What is Monero? A Beginner's Guide | TechFunnel

Monero 的工作量证明称为 RandomX

Monero 并不要求矿工一遍又一遍地运行同一个微小的哈希函数。它要求他们在虚拟机上运行一个小型随机程序,在执行过程中高强度访问内存,然后再对结果进行哈希。

Bitcoin 的工作量证明非常适合专用芯片,因为其计算任务始终不变。RandomX 的设计目标恰恰相反。它试图让高效挖矿尽可能接近普通 CPU 的工作负载。

简要版

以下是最简明且有用的概述:

  1. Monero 使用候选区块头加上一个随机数。
  2. 它还使用较早的区块哈希作为中期密钥。
  3. 该密钥会构建一个大型共享内存数据集。
  4. 候选区块输入会被哈希,以生成一个特殊虚拟机的种子。
  5. 虚拟机在 8 个链式程序中执行整数运算、浮点运算、分支以及大量内存访问。
  6. 最终的机器状态会被哈希为一个 256 位输出。
  7. 如果该输出低于网络目标值,该区块即为有效。

有意思的并不是最后那个“是或否”的判定规则。每个工作量证明系统都有这一点。真正有意思的是,Monero 如何让每一次哈希尝试都在普通 CPU 擅长、而定制芯片难以应对的方面变得代价高昂。

为什么 Monero 不使用简单的哈希

如果你的工作量证明只是“对新的输入反复运行这个固定函数,直到得到一个足够幸运的输出”,那么硬件设计者的任务就很明确:制造出能够以尽可能低的成本、尽可能快的速度运行这一确切函数的芯片。

这正是 Bitcoin 在 SHA-256 ASIC 出现后所发生的情况。

Monero 不希望走上这条道路。早在 RandomX 之前,该项目就已明确表示,专用挖矿硬件会带来中心化压力。制造商越少,它们的影响力就越大。大型矿场越重要,普通用户就越无足轻重。

Monero 早期的应对方案是 CryptoNight 系列。后来在 2019 年末,Monero 切换到 RandomX,其自身发布说明将其描述为一种新的工作量证明机制,“基于随机指令,并针对 CPU 进行了优化”。

因此,设计目标也从让内存变得重要转变为让整个 CPU 都变得重要 

RandomX 背后的核心思想

RandomX 基于这样一个观察:CPU 不只是算术运算盒子。它们是灵活的机器,旨在运行不断变化的代码,并同时调度大量硬件特性。

现代 CPU 具有:

  • 多级缓存
  • 整数运算单元
  • 浮点运算单元
  • 分支处理
  • 乱序执行
  • 推测执行
  • 内存控制器

普通加密哈希并不会利用那么多样化的能力。它们大多只是将数据送入一条固定的流水线。

RandomX 试图将工作量证明与这些更广泛的 CPU 优势绑定起来。其设计文档指出,这项工作必须是动态的 。这意味着,矿工不只是输入新的数据,矿工还会获得需要运行的新代码。

这就是为什么 RandomX 建立在随机代码执行之上。

矿工实际在计算什么

在 Monero 层面,RandomX 接收两个重要输入:

  • 一个密钥 K
  • 一个哈希输入 H

对于 Monero 而言,K 来自较早的一个区块哈希,称为密钥区块 。RandomX 参考实现的 README 建议每 2048 个区块更换一次该密钥,并设置 64 个区块的延迟 ,Monero 也正是这样接入的。

这一细节很重要,因为矿工不会为每个 nonce 都重建那些庞大的共享内存结构。只有在密钥发生变化时,他们才会重建,而这大约每 2.8 天才会发生一次。

H 是带有所选 nonce 的候选区块哈希数据块。这正是矿工反复不断更改的部分。

因此,你可以这样理解 Monero 挖矿:

  • 网络通过 K 为你提供一个中期环境
  • 你的候选区块会通过 H 为你提供每次尝试的输入

环境变化缓慢。尝试则不断变化。

步骤 1:从密钥构建缓存

RandomX 首先获取密钥 K,并对其运行 Argon2d

Argon2d 更广为人知的用途是密码哈希和密钥派生函数。它在这里之所以有用,原因与其在那里的用途相同:它是内存硬的。它会迫使机器以一种难以取巧的方式访问大量内存。

在 RandomX 的默认参数下,这会生成一个 256 MiB 缓存 

该缓存是 RandomX 中两大内存结构里较小的一个。对于希望获得最高速度的矿工来说,他们并不想直接使用这一结构。它的作用是用来构建更大的那一个。

第 2 步:将缓存扩展为数据集

RandomX 通过这 256 MiB 的缓存构建出数据集 

默认数据集大小为:

  • 2,147,483,648 字节基础大小
  • 33,554,368 字节额外大小

两者合计约为 2080 MiB,略高于 2 GiB

这个看起来有些奇怪的大小是刻意设计的。它大到足以溢出片上内存并进入 DRAM,而额外那段非 2 的幂的尾部也让硬件设计人员更为头疼。

该数据集在哈希计算期间是只读的。RandomX 利用它来强制产生规律性的 DRAM 访问流量。设计文档称,每次程序迭代会读取一个 64 字节的数据集项,而在一次完整的哈希结果计算过程中,这将累计为 16,384 次数据集读取 

这使得 RandomX 的一个主要瓶颈变成了内存访问,而不只是算术运算。

第3步:根据区块输入初始化暂存区

现在,RandomX 转向每次哈希的输入 H

它使用 Blake2b 计算 Hash512(H)。这个 64 字节的结果会为一个基于 AES 的生成器提供种子,由其填充暂存区 

暂存区是虚拟机的工作内存。与大型数据集不同,它的设计目标是驻留在 CPU 缓存中,而非 DRAM。

其默认大小为 2 MiB,并按 CPU 缓存层级进行划分:

  • 16 KiB L1 级缓存
  • 256 KiB L2 级缓存
  • 2 MiB L3 级缓存

这是 RandomX 最巧妙的设计之一。它同时使用了两种截然不同的内存结构:

  • 一个大型数据集 ,用于访问 DRAM
  • 一个较小的暂存区 ,以模拟缓存密集型代码的行为

这使其能够同时对内存子系统和 CPU 核心施加压力。

第 4 步:生成一个随机程序

当暂存区准备就绪后,RandomX 会为其虚拟机生成一个程序。

这不是 C 程序,也不是 JavaScript 程序。它是一个紧凑的虚拟机程序,拥有自己的指令集。

有两个细节至关重要:

  1. 每条指令的长度都是 8 字节 
  2. 任何8字节字都可以作为有效指令。

第二种选择意义重大。这意味着 RandomX 只需用随机字节填充缓冲区,就能生成程序。无需缓慢的解析器,也不需要复杂的语法检查。

每个程序包含 256 条指令 

这些指令被刻意设计得像是真实 CPU 所擅长处理的工作:

  • 整数运算
  • 64 位乘法
  • 浮点运算
  • 128位向量运算
  • 内存加载与存储
  • 偶发分支

浮点运算部分并非可有可无。RandomX 使用 IEEE 754 双精度运算,包括除法和平方根,并采用全部四种标准舍入模式。这使得该虚拟机更难被简化为一种微型的“以整数运算为主”的定制化设计。

第5步:运行程序循环

虚拟机以循环方式执行这段包含 256 条指令的程序,共 2048 次迭代 

在每次迭代中,它会:

  • 读取并写入暂存区内存
  • 预取并加载数据集条目
  • 混合整数和浮点寄存器状态
  • 当条件满足时,会走一条低概率分支

设计文档指出,平均每次迭代会从内存中读取约 504 字节 ,并写入约 256 字节 

这个数字揭示了 RandomX 真正的运行方式。它并不是“加了额外步骤的哈希”,而是在努力表现得像杂乱、混合、真实的软件。

分支存在的原因

分支很容易被忽视,但它们很重要。

如果代码完全是直线式执行,专用硬件就能对其中更多部分进行优化消除。分支会让静态简化变得更加困难。

RandomX 对分支的使用相当克制。分支被执行的概率约为 1/256,而且其设计有意让这些分支通常被预测为“不跳转”。这意味着在大多数情况下,它们在 CPU 上的代价很低,同时仍能阻碍那些过度优化的硬件捷径。

重点并不在于 RandomX 找到了某种神奇的分支预测技巧。它并没有。关键在于,即便只有少量真实的控制流,也会让这类工作负载看起来更像实际代码,而不再像一条规整的硬件流水线。

为什么每个哈希需要串联 8 个程序

仅有一个随机程序是不够的。

如果矿工只需要运行一个随机程序,那么一个不诚实的矿工可能会先检查这个程序,并跳过“坏的”程序。或者,一个定制芯片设计者可能只支持那些对他们的硬件容易的程序。

RandomX 通过将 8 个程序链接在一起来解决这个问题。

一个程序的输出状态成为下一个程序的种子。因此,一旦你开始,就无法提前知道整个链。你要么完成链,要么必须放弃已经付出的工作。

这是 RandomX 中最干净的想法之一。它将“也许我只会做简单的工作”变成了一个糟糕的策略。

第 6 步:将最终状态压缩为哈希

在 8 个程序中的最后一个执行完毕后,RandomX 仍需将所有这些机器状态转换为一个最终摘要。

它会做两件事:

  1. 它首先使用基于 AES 的哈希对整个暂存区进行指纹计算。
  2. 它将其与虚拟机寄存器文件结合,并运行最终的基于 Blake2b 的 256 位哈希 

这个最终的 256 位结果就是 RandomX 的输出。

在 Monero 层面,矿工会检查该结果是否低于当前难度目标。如果是,该区块胜出;如果不是,矿工就会更改随机数(nonce)并再次尝试。

快速模式与轻量模式

RandomX 有两种模式:

  • 快速模式 ,使用完整的 2080 MiB 数据集
  • 轻量模式 ,仅使用 256 MiB 缓存,并即时计算数据集条目

两种模式都会得出相同的结果 

核实必须与挖矿结果一致,但这两种模式所需的工作量并不相同。

快速模式用于挖矿。轻量模式用于核实。参考版 README 就是这么说的,而这种区分正是 RandomX 最出色的设计选择之一。

如果每个验证者仅为了核查一次工作量证明就需要超过 2 GiB,那就很糟糕了。如果轻量模式便宜到足以进行有竞争力的挖矿,那也同样糟糕。RandomX 试图保持在两者之间:核实应当切实可行,但又不能成为一种有吸引力的挖矿捷径。

设计文档明确旨在让轻量模式在内存—时间权衡上处于劣势。通俗地说,如果你节省了内存,就应当以额外的计算工作来偿还这一优势。

为什么它对 CPU 友好

“对 CPU 友好”听起来像是营销话术,因此有必要说得更具体一些。

RandomX 偏向于 CPU,因为它依赖于优秀 CPU 原本就具备的特性:

  • 大容量缓存
  • 可观的 DRAM 带宽
  • 硬件 AES
  • fast 64-bit math  
  • floating-point hardware  
  • 乱序执行
  • dynamic code generation  

The reference implementation even includes JIT compilers for x86-64ARM64, and RISCV64, so the VM programs can be translated into native machine code on the fly instead of being interpreted instruction by instruction.  

这并不意味着 ASIC 会永远变得不可能。任何严肃的表述都不应如此宣称。它真正做到的,是让定制芯片这项工作变得棘手得多。优秀的 RandomX ASIC 看起来不再像一台规整的固定功能哈希引擎,而更像是“构建一颗怪异而昂贵、还外挂大量内存的 CPU”。这会缩小其优势。

RandomX 试图为 Monero 买来什么

RandomX 主要并不是追求原始速度,而是关乎硬件经济学 

Monero 希望采用一种工作量证明机制,以实现:

  • 让挖矿对普通硬件更加开放
  • 削弱了固定功能 ASIC 的优势
  • 避免让共识受制于少数几家硬件供应商
  • 验证成本保持在相对合理的水平

这就是为什么,如果你期待的是一种常规的挖矿哈希,RandomX 看起来会如此不同寻常。

它并不试图在 Bitcoin 的意义上追求优雅,而是试图让专用化变得别扭。

最简单的理解方式

如果说 Bitcoin 挖矿是“让这台小机器永远运行下去”,那么 Monero 挖矿就是“不断生成带有 CPU 特性的微型工作负载,并扛住内存流量”。

这是我所知道的最简洁且仍然准确的思维模型。

在底层,RandomX 是一系列经过审慎权衡的设计选择:

  • 使用 Argon2d 构建缓存
  • 一个超过 2 GiB 的数据集,以强制访问 DRAM
  • 一个与缓存大小相当的暂存区,用于调动本地内存
  • 带有随机程序的虚拟机
  • 整数、浮点、向量和分支指令
  • 8个链式程序,阻止轻松的程序筛选
  • 为矿工提供快速模式,为验证者提供轻量模式

把这些组合在一起,就构成了如今 Monero 的工作量证明。

它并不简单。但其核心目标很简单: 让挖矿看起来像通用计算,从而压缩专用硬件主导网络的空间。

了解 RecodeX 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读