news 2026/9/15 18:35:55

EIP-1588 硬分叉 Meta 解析:Ethereum ProgPoW 的规范、参数与安全边界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
EIP-1588 硬分叉 Meta 解析:Ethereum ProgPoW 的规范、参数与安全边界

EIP-1588 硬分叉 Meta 解析:Ethereum ProgPoW 的规范、参数与安全边界

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

本文以 EIPS/eip-1588.md 为骨架,完整继承其硬分叉规范定义,并深度融合其所引用的 EIPS/eip-1057.md 算法规范与 assets/eip-1057/test-vectors.md 测试向量,带读者系统理解 ProgPoW 的动机、参数调优、核心代码流程与安全考量。读完后,你将能准确描述该硬分叉的激活条件与包含范围,理解 ProgPoW 相比 Ethash 的六大结构性改动,掌握算法参数表、关键伪代码与验证测试向量的使用方法,并了解其已公开的安全审计结论与未修复漏洞的边界。

一、EIP-1588:一次以 ProgPoW 为核心的备选硬分叉

EIP-1588 是一份Meta 类型的 EIP,其作用并非定义新的协议机制,而是"打包"一组改动,为它们赋予统一的代号、激活时机与范围。根据 EIPS/eip-1588.md 的 Abstract,它规范了名为Ethereum ProgPoW的备选以太坊硬分叉所包含的全部变更。

该文档的 Specification 非常简洁,全部内容如下:

  • 代号(Codename):Ethereum ProgPoW
  • 别名(Aliases):N/A(无)
  • 激活条件(Activation):以太坊主网Block >= 7280000
  • 包含的 EIP:EIP-1057——ProgPoW,一种程序化工作量证明算法(Programmatic Proof-of-Work)

这是一份典型的"薄壳"型 meta-EIP:所有实质性的技术内容都沉淀在被引用的 EIP-1057 中。EIP-1588 的意义在于为社区讨论与节点协调提供一个统一的指代——当开发者或研究者提到"Ethereum ProgPoW 分叉"时,指的是"在主网 7,280,000 区块高度激活 ProgPoW"这一整套方案。值得注意的是,该 EIP 当前状态为Stagnant(停滞),意味着它并未进入最终采纳流程,历史背景是 ProgPoW 的争议与以太坊最终转向 PoS 共识,但本文仍以仓库文档为准介绍其规范内容。

从仓库检索可以看到,ProgPoW 曾长期出现在以太坊核心开发者会议的候选清单中:EIPS/eip-2378.md 的 EIP-1057 状态表将其标记为ELIGIBLE(合格候选),并注明讨论日期为 2019-11-01,可见该提案曾是一条真实的、进入过治理流程的技术路线,而非纸面空想。

二、为什么需要 ProgPoW:ASIC 抗性之争

ProgPoW 的设计动机,源自以太坊黄皮书对 PoW 的两条根本诉求(引自 EIPS/eip-1057.md 的 Motivation 部分):

  1. 尽可能让更多人参与:尽量减少对专用、稀有硬件的需求或奖励,使"用电换以太"的转换率对全球任何人都大致公平;
  2. 杜绝超线性收益:不允许高资金壁垒的参与者攫取不成比例的算力与奖励,从而威胁网络安全。

Ethash 选择了"顺序内存硬"(sequential memory-hard)这条路线来对抗 ASIC——它要求确定 nonce 需要大量内存与带宽,使得内存无法并行服务多个 nonce 求解。但 EIP-1057 作者指出,以太坊区块链 5 年运行经验暴露了这条路线的新问题:Ethash 专用 ASIC(如 Antminer E3)已能提供高于 GPU 的效率,当时估计网络中可能多达40% 的算力由 ASIC 保障,这直接违背了黄皮书的两条设计目标。

ProgPoW 的应对思路是走黄皮书所说的"第二条路径"——让"专用硬件"的定义本身变成通用硬件:使算法的资源需求与市售 GPU 的特性完全匹配,从而让为算法定制的 ASIC 几乎没有效率优化空间。EIP-1057 给出的量化结论是:即便实现 ProgPoW 的定制 ASIC,其相对 GPU 的效率增益也仅有约1.1~1.2 倍,远低于 Ethash 的约 2 倍、CryptoNight 的约 50 倍、以及 SHA256 / Scrypt / X11 等算法的约 1000 倍。

2.1 ProgPoW 的六大核心元素

EIP-1057 将算法改动归纳为以下六点:

元素说明
替换 Keccak 变体从 keccak_f1600(64 位字)改为 keccak_f800(32 位字),降低功耗占比
增大 mix 状态增加算法中间混合状态的规模
随机数学序列主循环中加入随机序列的数学运算
小容量低延迟缓存读取新增对支持随机寻址的小型低延迟缓存的读取
扩大 DRAM 读取单次 DAG 读取从 128 字节提升到 256 字节
周期性程序更换随机序列每隔PROGPOW_PERIOD块更换一次(约 2~12 分钟,取决于配置)

其中"周期性程序更换"是 ProgPoW 的灵魂:随机序列变化时,挖矿软件在宿主机 CPU 上为这段序列生成源码并现场编译,GPU 执行的是"数学操作与 mix 状态均已解析完毕"的编译产物。由于程序每PROGPOW_PERIOD(默认 10 块,约 2 分钟)就更换一次,矿工没有足够时间手工优化特定随机序列——这正是"Programmatic(程序化)"一词的由来。

2.2 既有 PoW 算法的 ASIC 效率增益对比

EIP-1057 对主流 PoW 算法做了系统性比较,下表完整继承了原文档的估算数据("潜在 ASIC 效率增益"):

算法潜在 ASIC 效率增益原因
SHA256~1000X纯简单算术/逻辑/旋转指令序列,ASIC 上一颗哈希核只需少量晶体管与连线
Scrypt / NeoScrypt~1000X运算与 SHA 类似,且 scratchpad 仅 32~128KB,可轻易放进 ASIC 芯片内
X11 / X16R~1000X多个哈希核按固定流水线或简单状态机排序,单核效率与 SHA 相当
Equihash~100X~150MB 状态虽大但 ASIC 可实现,分箱/排序/比较可超高速完成
Cuckoo Cycle~100X专用图遍历核的增益与 SHA 计算核相当
CryptoNight~50X需完整 2MB scratchpad,片上 SRAM 主导实现,限制哈希核数量
Ethash~2XDAG 体积大需外部内存,但只需最小计算,ASIC 可退化为"内存接口 + 小计算引擎"
ProgPoW~1.1~1.2XGPU 绝大部分资源被算法用满,仅可移除图形管线与浮点单元等

EIP-1057 还给出了一个犀利的概念辨析:CPU 和 GPU 本身就是"通用 ASIC",任何能在其上运行的算法理论上都能被定制 ASIC 以略少的功能实现。因此"ASIC 抗性"的准确定义应是——专用硬件与广泛采用硬件之间的效率差距;差距越小,抗性越强,算法越好。这一度量标准是本文后续理解所有设计取舍的钥匙。

三、算法规范:参数、原语与核心流程

ProgPoW 的全部行为由一组可调参数决定(下表完整继承 EIPS/eip-1057.md 的参数定义):

参数含义
PROGPOW_PERIOD更换随机程序的块数间隔
PROGPOW_LANES协同计算单个哈希实例的并行 lane 数
PROGPOW_REGS寄存器文件使用规模
PROGPOW_DAG_LOADS每个 lane 从 DAG 加载的 uint32 数量
PROGPOW_CACHE_BYTES缓存大小(字节)
PROGPOW_CNT_DAGDAG 访问次数,即算法外层循环(64 与 Ethash 相同)
PROGPOW_CNT_CACHE每轮循环的缓存访问次数
PROGPOW_CNT_MATH每轮循环的数学运算次数

EIP-1057 文档给出了 0.9.2 与 0.9.3 两版参数演进对照表,此处完整保留:

参数0.9.20.9.3
PROGPOW_PERIOD5010
PROGPOW_LANES1616
PROGPOW_REGS3232
PROGPOW_DAG_LOADS44
PROGPOW_CACHE_BYTES16x102416x1024
PROGPOW_CNT_DAG6464
PROGPOW_CNT_CACHE1211
PROGPOW_CNT_MATH2018

以及 DAG 参数:

DAG 参数0.9.20.9.3
ETHASH_DATASET_PARENTS256256

关键解读:

  • PROGPOW_PERIOD从 50 降到 10:程序更换更频繁(约 2 分钟一次),进一步压缩手工优化随机序列的时间窗。原文档特别说明,若程序仅随 DAG epoch 更换(约 5 天一次),部分矿工将有时间针对随机序列开发手工优化版本,从而获得不当优势。
  • 数值约定:规范代码为 C++,所有数值均按无符号 32 位整数计算,溢出在下一次计算前截断。Python、JavaScript 等非定长数值语言,以及 Java 等仅用有符号整数的语言,实现时需特别注意位宽语义。这一设计与现代 GPU 的内部数据架构(32 位)保持一致。

3.1 两个基础随机/合并原语

FNV1a(32 位变体)——用于合并数据。Ethash 使用 FNV1,而 ProgPoW 改用分布性质更优的 FNV1a:

const uint32_t FNV_PRIME = 0x1000193; const uint32_t FNV_OFFSET_BASIS = 0x811c9dc5; uint32_t fnv1a(uint32_t h, uint32_t d) { return (h ^ d) * FNV_PRIME; }

KISS99——用于随机数生成。它是指令数最少、且能通过 TestU01 统计测试套件的随机生成器;之所以不用 Mersenne Twister 这类更复杂的生成器,是因为它能在专用 ASIC 上被高效实现,反而给 ASIC 留下效率优化的空间:

typedef struct { uint32_t z, w, jsr, jcong; } kiss99_t; uint32_t kiss99(kiss99_t &st) { st.z = 36969 * (st.z & 65535) + (st.z >> 16); st.w = 18000 * (st.w & 65535) + (st.w >> 16); uint32_t MWC = ((st.z << 16) + st.w); st.jsr ^= (st.jsr << 17); st.jsr ^= (st.jsr >> 13); st.jsr ^= (st.jsr << 5); st.jcong = 69069 * st.jcong + 1234567; return ((MWC^st.jcong) + st.jsr); }

3.2 mix 状态初始化:fill_mix

fill_mix用 FNV 将"每 warp 的种子"扩展为"每 lane 的种子",再用 KISS99 填充每个 lane 的mix数组(规模为PROGPOW_REGS):

void fill_mix( uint64_t seed, uint32_t lane_id, uint32_t mix[PROGPOW_REGS] ) { uint32_t fnv_hash = FNV_OFFSET_BASIS; kiss99_t st; st.z = fnv1a(fnv_hash, seed); st.w = fnv1a(fnv_hash, seed >> 32); st.jsr = fnv1a(fnv_hash, lane_id); st.jcong = fnv1a(fnv_hash, lane_id); for (int i = 0; i < PROGPOW_REGS; i++) mix[i] = kiss99(st); }

3.3 哈希原语:keccak_f800

与 Ethash 一样,Keccak 用于按 nonce 播种序列并产出最终结果。ProgPoW 采用keccak-f800变体,因为其 32 位字宽与现代 GPU 原生字宽匹配。其实现是 SHAKE 的变体:width=800、bitrate=576、capacity=224、output=256、无填充。Keccak 结果按 256 位大端数处理(结果第 0 字节为 MSB)。

由于输入输出固定且较小,只需单次 absorb 与 squeeze:

hash32_t keccak_f800_progpow(uint32_t* state) { // 单次 absorb 的 keccak_f800 调用 for (int r = 0; r < 22; r++) keccak_f800_round(st, r); // 固定 8 字输出的 squeeze 阶段 hash32_t ret; for (int i=0; i<8; i++) ret.uint32s[i] = st[i]; return ret; }

3.4 程序种子初始化:progPowInit

progPowInitprog_seed生成两个序列:mix_seq_dst(merge 的目标寄存器序列)与mix_seq_src(缓存读取的源寄存器序列)。它保证每个目标恰好被 merge 一次缓存读取无重复(重复读取可能被优化掉),并使用 Fisher-Yates 洗牌实现随机化:

kiss99_t progPowInit(uint64_t prog_seed, int mix_seq_dst[PROGPOW_REGS], int mix_seq_src[PROGPOW_REGS]) { kiss99_t prog_rnd; prog_rnd.z = fnv1a(FNV_OFFSET_BASIS, prog_seed); prog_rnd.w = fnv1a(prog_rnd.z, prog_seed >> 32); prog_rnd.jsr = fnv1a(prog_rnd.w, prog_seed); prog_rnd.jcong = fnv1a(prog_rnd.jsr, prog_seed >> 32); // 生成 merge() 的 mix 目标序列与缓存读取的 mix 源序列 for (int i = 0; i < PROGPOW_REGS; i++) { mix_seq_dst[i] = i; mix_seq_src[i] = i; } for (int i = PROGPOW_REGS - 1; i > 0; i--) { int j; j = kiss99(prog_rnd) % (i + 1); swap(mix_seq_dst[i], mix_seq_dst[j]); j = kiss99(prog_rnd) % (i + 1); swap(mix_seq_src[i], mix_seq_src[j]); } return prog_rnd; }

3.5 数据合并与随机数学:merge 与 math

merge将新数据 b 并入高熵值 a,刻意只保留熵的操作(不做a & b这类会降低熵的运算):

uint32_t merge(uint32_t a, uint32_t b, uint32_t r) { switch (r % 4) { case 0: return (a * 33) + b; case 1: return (a ^ b) * 33; // 避免旋转 0 位(那是 NOP) case 2: return ROTL32(a, ((r >> 16) % 31) + 1) ^ b; case 3: return ROTR32(a, ((r >> 16) % 31) + 1) ^ b; } }

math的 11 种运算全部选取自 CUDA/OpenCL 的原生指令——mul_himinclzpopcountrotate等均与 OpenCL 内建函数一一对应,ROTR32 等价于rotate(i, 32-v)。这正是"算法要求匹配 GPU 硬件"原则在指令层面的落地:

uint32_t math(uint32_t a, uint32_t b, uint32_t r) { switch (r % 11) { case 0: return a + b; case 1: return a * b; case 2: return mul_hi(a, b); case 3: return min(a, b); case 4: return ROTL32(a, b); case 5: return ROTR32(a, b); case 6: return a & b; case 7: return a | b; case 8: return a ^ b; case 9: return clz(a) + clz(b); case 10: return popcount(a) + popcount(b); } }

3.6 内层循环:progPowLoop

progPowLoop的流程(每轮迭代):

  1. 选择loop % LANES号 lane 作为本轮的leader
  2. 用 leader 的mix[0]值对 DAG 中 256 字节条目数取模,决定从完整 DAG 何处读取;
  3. 每个 lane 以(lane ^ loop) % LANES作为条目内起始偏移,顺序读取DAG_LOADS个字;
  4. 执行随机序列的数学运算与缓存访问;
  5. 在循环末尾合并本轮读到的 DAG 数据(延迟到最后合并,是为了充分隐藏内存延迟)。

dag参数指向以 32 位无符号整数(小端)分组的 Ethash DAG 字节;在小端架构上,它就是一个指向现有 DAG 的普通 int32 指针。DAG_BYTES为当前 DAG 的字节数,其生成方式与既有 Ethash 完全一致。原文档还解释了(l ^ loop)混洗的深层意图:防止多芯片 ASIC 各自只存储 DAG 的一部分。

void progPowLoop( const uint64_t prog_seed, const uint32_t loop, uint32_t mix[PROGPOW_LANES][PROGPOW_REGS], const uint32_t *dag) { // dag_entry 保存从 DAG 加载的 256 字节数据 uint32_t dag_entry[PROGPOW_LANES][PROGPOW_DAG_LOADS]; // 每轮旋转哪个 lane 作为 DAG 地址来源,保证上一轮 DAG 数据反馈到本轮地址 uint32_t dag_addr_base = mix[loop%PROGPOW_LANES][0] % (DAG_BYTES / (PROGPOW_LANES*PROGPOW_DAG_LOADS*sizeof(uint32_t))); for (int l = 0; l < PROGPOW_LANES; l++) { size_t dag_addr_lane = dag_addr_base * PROGPOW_LANES + (l ^ loop) % PROGPOW_LANES; for (int i = 0; i < PROGPOW_DAG_LOADS; i++) dag_entry[l][i] = dag[dag_addr_lane * PROGPOW_DAG_LOADS + i]; } int mix_seq_dst[PROGPOW_REGS]; int mix_seq_src[PROGPOW_REGS]; int mix_seq_dst_cnt = 0; int mix_seq_src_cnt = 0; kiss99_t prog_rnd = progPowInit(prog_seed, mix_seq_dst, mix_seq_src); int max_i = max(PROGPOW_CNT_CACHE, PROGPOW_CNT_MATH); for (int i = 0; i < max_i; i++) { if (i < PROGPOW_CNT_CACHE) { // 缓存内存访问:lane 访问 DAG 前部区域的随机 32 位位置 int src = mix_seq_src[(mix_seq_src_cnt++)%PROGPOW_REGS]; int dst = mix_seq_dst[(mix_seq_dst_cnt++)%PROGPOW_REGS]; int sel = kiss99(prog_rnd); for (int l = 0; l < PROGPOW_LANES; l++) { uint32_t offset = mix[l][src] % (PROGPOW_CACHE_BYTES/sizeof(uint32_t)); mix[l][dst] = merge(mix[l][dst], dag[offset], sel); } } if (i < PROGPOW_CNT_MATH) { // 随机数学:生成两个唯一源寄存器 int src_rnd = kiss99(prog_rnd) % (PROGPOW_REGS * (PROGPOW_REGS-1)); int src1 = src_rnd % PROGPOW_REGS; int src2 = src_rnd / PROGPOW_REGS; if (src2 >= src1) ++src2; // src2 现在是不等于 src1 的任意寄存器 int sel1 = kiss99(prog_rnd); int dst = mix_seq_dst[(mix_seq_dst_cnt++)%PROGPOW_REGS]; int sel2 = kiss99(prog_rnd); for (int l = 0; l < PROGPOW_LANES; l++) { uint32_t data = math(mix[l][src1], mix[l][src2], sel1); mix[l][dst] = merge(mix[l][dst], data, sel2); } } } // 在循环最末尾消费全局加载数据,以充分隐藏延迟 // 始终合并进 mix[0] 以喂养下一次偏移计算 for (int i = 0; i < PROGPOW_DAG_LOADS; i++) { int dst = (i==0) ? 0 : mix_seq_dst[(mix_seq_dst_cnt++)%PROGPOW_REGS]; int sel = kiss99(prog_rnd); for (int l = 0; l < PROGPOW_LANES; l++) mix[l][dst] = merge(mix[l][dst], dag_entry[l][i], sel); } }

3.7 整体流程:progPowHash

算法整体分为六个阶段,与 Ethash 保持"header+nonce 播种 → 循环混合 → 归约 → 最终哈希"的宏观结构:

  1. 对 header + nonce 做一次 keccak_f800 哈希,得到 256 位摘要(填充与 Ethash 的自定义填充一致);
  2. 用摘要前两个字作为种子,生成初始 mix 数据;
  3. 多次循环,将随机加载与随机数学反复混入 mix;
  4. 将所有 mix 数据归约为单个 256 位值;
  5. 用初始摘要 + 最终 mix 256 位值做第二次 keccak 哈希(填充一致);
  6. 挖矿时将最终哈希与hash32_t目标值比较。
hash32_t progPowHash( const uint64_t prog_seed, // 值为 (block_number/PROGPOW_PERIOD) const uint64_t nonce, const hash32_t header, const uint32_t *dag // 位于显存中的 GB 级 DAG——前部区域被缓存 ) { hash32_t hash_init; hash32_t hash_final; uint32_t mix[PROGPOW_LANES][PROGPOW_REGS]; // ======== 初始 keccak 通道的 absorb 阶段 ======== { uint32_t state[25] = {0x0}; // 1st: 填充 header 数据(8 字) for (int i = 0; i < 8; i++) state[i] = header.uint32s[i]; // 2nd: 填充 nonce(2 字) state[8] = nonce; state[9] = nonce >> 32; // 3rd: 应用填充 state[10] = 0x00000001; state[18] = 0x80008081; hash_init = keccak_f800_progpow(state); // 取种子以初始化 mix seed = ((uint64_t)hash_init.uint32s[1] << 32) | hash_init.uint32s[0]); } // 为所有 lane 初始化 mix for (int l = 0; l < PROGPOW_LANES; l++) fill_mix(seed, l, mix[l]); // 执行随机生成的内层循环 for (int i = 0; i < PROGPOW_CNT_DAG; i++) progPowLoop(prog_seed, i, mix, dag); // 将 mix 数据归约为每 lane 的 32 位摘要 uint32_t digest_lane[PROGPOW_LANES]; for (int l = 0; l < PROGPOW_LANES; l++) { digest_lane[l] = FNV_OFFSET_BASIS; for (int i = 0; i < PROGPOW_REGS; i++) digest_lane[l] = fnv1a(digest_lane[l], mix[l][i]); } // 将所有 lane 归约为单个 256 位摘要 for (int i = 0; i < 8; i++) digest.uint32s[i] = FNV_OFFSET_BASIS; for (int l = 0; l < PROGPOW_LANES; l++) digest.uint32s[l%8] = fnv1a(digest.uint32s[l%8], digest_lane[l]); // ======== 最终 keccak 通道的 absorb 阶段 ======== { uint32_t state[25] = {0x0}; // 1st: 填充 hash_init(8 字) for (int i = 0; i < 8; i++) state[i] = hash_init.uint32s[i]; // 2nd: 填充主循环摘要(8 字) for (int i = 8; i < 16; i++) state[i] = digest.uint32s[i - 8]; // 3rd: 应用填充 state[17] = 0x00000001; state[24] = 0x80008081; hash_final = keccak_f800_progpow(state); } // 将最终哈希与目标值比较 [...] }

四、测试向量:验证实现正确性的唯一标准

ProgPoW 的工程可验证性极强——仓库在 assets/eip-1057/ 目录下提供了三份测试资产:人类可读的 assets/eip-1057/test-vectors.md、机器可读的 assets/eip-1057/test-vectors-0.9.2.json 与 assets/eip-1057/test-vectors-0.9.3.json。任何语言/平台的实现,都可以用这些向量逐函数校准。

4.1 基础原语向量

  • fnv1a:如fnv1a(0X811C9DC5, 0XDDD0A47B) = 0XD37EE61A
  • kiss99:以z=362436069, w=521288629, jsr=123456789, jcong=380116160为初态,第 1 次调用得769445856,第 100,000 次调用得941074834
  • fill_mix:以hash_seed=0xEE304846DDD0A47Blane_id=0填充的 mix 数组,首值为0x10C02F0D, 0x99891C9E, ...,lane_id=13 时则有另一组完全不同的 32 个值;
  • keccak_f800_progpow:两个官方用例,给定 header 8 字与 seed 后产出固定 digest;
  • merge / math:每个分支路径都有对应向量,例如mergemul/addxor/mulrotl/xorrotr/xor四路径,math的 add、mul、mul_hi32、min、rotl32、rotr32、and、or、xor、clz、popcount 共 11 种运算。

4.2 progPowInit 与 progPowLoop 向量

对于 ProgPow period 600(即块 30,000),progPowInit应产出长度为 32 的 src 序列(0x1A, 0x1E, 0x01, ...)、dst 序列(0x00, 0x04, 0x1B, ...)以及 KISS99 状态(z=0x6535921C, w=0x29345B16, jsr=0xC0DD7F78, jcong=0x1165D7EB)。

progPowLoop向量则给出块 30,000、第 1 轮循环后mix[0]mix[15]全部 16 条 lane 的完整 mix 数组快照——任何一行数值不匹配都意味着实现存在偏差,这是逐 lane、逐寄存器级别的严格校验。

4.3 progPowHash 端到端向量

以块 30,000 为例,EIP-1057 文档给出了完整链路:

Header : 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff Nonce : 0x123456789abcdef0 Hash init : 0xee304846ddd0a47b98179e96b60ec5ceeae2727834367e593de780e3e6d1892f Mix seed : 0x7ba4d0dd464830ee Mix hash : 0x493c13e9807440571511b561132834bbd558dddaa3b70c09515080a6a1aff6d0 Hash final : 0x46b72b75f238bea3fcfd227e0027dc173dceaa1fb71744bd3d5e030ed2fed053

test-vectors.md 的progPowHash一节还补充了 0.9.2 与 0.9.3 两个版本的序列化向量(Block 0、49、50、99、29,950、29,999、30,000、30,049、30,050、30,099、59,950、59,999 直至 Block 100,000,000),覆盖prog_seed从 0 到 2,000,000 的跨度,并标注了prog_seed = block_number / PROGPOW_PERIOD的计算关系。其中 0.9.2 与 0.9.3 的差异正对应上文参数表中PROGPOW_CNT_CACHE(12→11)与PROGPOW_CNT_MATH(20→18)的调整,是理解参数如何影响输出结果的第一手资料。

五、安全考量:审计结论、已知漏洞与修复边界

EIP-1057 是极少数同时接受过软件与硬件双重审计的 PoW 提案,原文档记录了以下关键结论:

1. 软件审计(Least Authority):审计方建议修改 DAG 生成——将ETHASH_DATASET_PARENTS从 256 提升到 512,以缓解"Light Evaluation"(轻量求值)攻击。副作用是 ProgPoW 的 DAG 内存文件将不再与 Ethash 兼容(epoch 长度与体积增长比例不变)。但 EIP 作者明确建议暂不实施此修复:"Ethash 数年之内不会可被利用,也不清楚 ProgPoW 是否会被利用,部署经过审计的代码是更优选择。"

2. 硬件审计(Bob Rao):作为配套的 ASIC 实现可行性评估,结论同样支撑"效率增益有限"的核心论点。

3. Kik 发现的绕过内存硬性漏洞:该漏洞同样存在于 Ethash,但近不可利用;而在 ProgPoW 中实际不可利用——它假设攻击者能像比特币那样构造候选块 header 的变体,而以太坊做不到这一点。攻击者需要修改块 header、需要接受修改后 header 的定制节点、并以非常规方式使用 extraNonce/extraData 作为熵,且暴力搜索难以在一个出块时间内完成;即便定制节点支持,其产出的块也会因 header 哈希无效而被其他对等节点立即拒绝。

4. 类似漏洞与建议修复:作者后来发现了与 Kik 类似的另一漏洞,但其开销过大,反而对 ASIC 不友好。若要彻底防范此类利用,可将最后一次 keccak 通道的输入状态从

  • header(256 位)+ mix 种子(64 位)+ 主循环 mix(256 位)+ 无填充

改为

  • 初始 keccak 摘要(256 位)+ 主循环 mix(256 位)+ 填充

从而把 keccak 暴力搜索的约束宽度从 64 位扩大到 256 位。该修复以 PR 形式存在于参考实现中,但作者同样建议暂不实施

5. 影响边界:原文档特别强调,上述漏洞均无法用于拒绝服务、双花或破坏网络,最坏情况只是给部署者带来对普通矿工的效率优势。

六、实现参考与许可证说明

EIP-1057 的参考挖矿实现位于 @ifdefelse 的 ProgPOW 仓库(文档链接指向 GitHub 外部仓库,本文仅转述其事实):它是 ethminer 的衍生品,因此保留GPL 许可证——这对想基于参考实现二次开发的团队是一个重要的合规前提。

七、总结

EIP-1588 以一份极简的 meta-EIP 将 ProgPoW(EIP-1057)打包为"Ethereum ProgPoW"备选硬分叉,激活区块门槛为主网 7,280,000。剥开这层薄壳,其技术内核是一套为商品化 GPU"量身定制"的工作量证明:用 keccak_f800、FNV1a、KISS99 与 Fisher-Yates 洗牌构建随机程序,用PROGPOW_PERIOD(10 块)级的高频程序更换阻止手工优化,用覆盖全部 11 种 GPU 原生指令的math运算填满通用硬件资源,最终把定制 ASIC 的相对效率增益压到约 1.1~1.2 倍。该提案曾进入核心开发者会议候选流程(见 EIPS/eip-2378.md),最终状态为 Stagnant,但其完整的参数表、伪代码与测试向量体系,至今仍是研究"程序化抗 ASIC PoW"设计的范本。若要继续深挖,可直接研读 EIPS/eip-1057.md 全文、assets/eip-1057/test-vectors.md 的逐函数向量,以及两份 JSON 机器可读向量文件。

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 18:35:19

富文本编辑器开发实战:从技术选型到性能优化的完整指南

做内容管理后台&#xff0c;十个项目里九个逃不开“做个编辑器”这个需求。标题就两个词“editor”&#xff0c;看起来简单&#xff0c;但实际上这个需求背后藏着大量的技术决策和工程坑。作为一个折腾过好几代内容编辑器的前端老手&#xff0c;我把从需求梳理到最终落地的完整…

作者头像 李华
网站建设 2026/9/15 18:32:46

Fast-LIO2激光SLAM核心原理与工业级调优实战

1. 这不是“看懂代码”而是“吃透激光SLAM闭环逻辑”的实战拆解Fast-LIO2不是一段能靠CtrlC/V跑起来的示例程序&#xff0c;它是一套把激光雷达点云、IMU高频运动状态、紧耦合优化器、李代数微分更新全部拧成一股绳的精密系统。我第一次在古月居视频里看到它实时建图帧率稳定在…

作者头像 李华
网站建设 2026/9/15 18:29:37

Three.js 实现 3D-Gaussian-Splatting 的完整实践路径

简介&#xff1a;本资源是一套基于Three.js实现3D-Gaussian-Splatting算法的Web端三维重建实战项目&#xff0c;面向前端工程师、计算机视觉初学者及WebGL图形开发爱好者&#xff0c;解决在浏览器中轻量级部署高斯溅射三维重建的技术落地难题。压缩包共83个文件&#xff0c;含5…

作者头像 李华
网站建设 2026/9/15 18:29:24

Zettlr 图片渲染机制详解:从 Markdown 语法到所见即所得预览

Zettlr 图片渲染机制详解&#xff1a;从 Markdown 语法到所见即所得预览 【免费下载链接】Zettlr Your One-Stop Publication Workbench 项目地址: https://gitcode.com/GitHub_Trending/ze/Zettlr Zettlr 是一个以 Markdown 为核心的"一站式出版工作台"&…

作者头像 李华