libssz:一个极速的、zkVM 友好的 SSZ Rust 库

本文介绍了一个名为 libssz 的全新 Rust 库,旨在为以太坊共识层和执行层提供快速且支持 no_std 环境的 SSZ(Simple Serialize)序列化和 Merkle 化功能。它通过优化编码、解码及 Merkle 化过程,显著提升了性能,并解决了现有库在 no_std 兼容性方面的不足,支持 EIP-8025 等新的以太坊提案。

SSZ (Simple Serialize) 是以太坊共识层的序列化和 Merkleization 格式。共识客户端以 SSZ 格式编码信标状态和区块,并使用 SSZ 哈希树根对数据计算 Merkle 证明。直到最近,SSZ 只是共识层关注的问题,因为执行客户端不需要它。

当前的 SSZ 格局

目前存在的一些 Rust SSZ 库有:

Lighthouse (ethereum_ssz) ssz_rs
性能 快速,经过生产环境实战检验 在复合类型上慢 100-300 倍
no_std 否。依赖 std,通过 ethereum_hashing 引入 ring (C 库) 是。no_std + alloc
有界类型 typenum typenum

Lighthouse 是 Rust SSZ 的参考实现,也是大多数项目使用的。ssz_rs 支持 no_std,但在复合类型上慢 100-300 倍,这意味着你无法在生产环境中使用它。直到最近,没有人同时需要快速支持 no_std 的 SSZ,所以没有人构建它。

两项提案将 SSZ 引入执行层

两项提案将 SSZ 引入执行层。

一项将二进制 SSZ 添加到 Engine API 的提案 用原始 SSZ 字节而非 JSON 通过 HTTP 传输。CL 已经有来自信标区块的 SSZ 格式的执行Payload;二进制传输使其能够将原始字节无需转换地转发到 EL。随着 Blob 数量的增长,JSON 成为瓶颈(十六进制编码使每个 128 KB 的 Blob 大小加倍),切换到 SSZ 允许客户端扩展 MAX_BLOB_COMMITMENTS_PER_BLOCK

EIP-8025 (可选执行证明) 扩展了 Engine API,增加了用于生成、提交和验证 zkVM 执行证明的端点,允许验证者通过加密证明支持执行有效性进行证明。这些证明的公共输入是 tree_hash_root(NewPayloadRequest),一个 SSZ Merkle 根。执行客户端需要对 ExecutionPayloadNewPayloadRequest 及其子容器等结构化共识类型计算哈希树根。

RISC-V 目标标准 要求符合标准的 zkVM 在没有标准库的情况下编译 guest 代码。目前许多 zkVM 通过分叉编译器来解决这个问题,但 ZKsync 的 Airbender 强制执行 no_std。随着团队采纳该标准,未来会有更多项目效仿。

如果你正在构建二进制 SSZ 传输,你需要一个快速的 SSZ 库。如果你正在 zkVM 内部证明 EIP-8025 执行,你需要一个既快速又支持 no_std 的库。

为什么不适配现有方案?

jsign 在为 ere-guests 实现 EIP-8025 guest 程序时,曾尝试使 Lighthouse 的 SSZ 在 no_std 中工作 ( ere-guests#8):

我深入研究了一下,进展很大但至少需要这些补丁:

ethereum_ssz = { git = "https://github.com/han0110/ethereum_ssz", branch = "feature/no-std" }
ethereum_ssz_derive = { git = "https://github.com/han0110/ethereum_ssz", branch = "feature/no-std" }
ethereum_serde_utils = { git = "https://github.com/han0110/ethereum_serde_utils", branch = "feature/no-std" }
tree_hash = { git = "https://github.com/jsign/tree_hash", rev = "2263d50" }
tree_hash_derive = { git = "https://github.com/jsign/tree_hash", rev = "2263d50" }
ssz_types = { git = "https://github.com/jsign/ssz_types", rev = "679b7ba" }

六个分叉的 crate 涉及三位维护者,但他仍然没有完成。在 PR 中,他实现了 EIP-8025 guest 程序设计,jsign 写道:

我花了一段时间修补 crate,但我已经需要修补 6 个 crate,这让我感到非常不舒服。

Lighthouse 团队从未为 no_std 构建他们的 SSZ 生态系统。ethereum_hashing 在没有功能门控的情况下引入 ring (一个 C 库),ssz_types 依赖于带有 std 功能的 typenum,而且依赖图足够深,你需要协调多个仓库的补丁才能使其完全 no_std 兼容。

我们如何构建 libssz

SSZ 编码有两条路径:固定大小类型(整数、字节数组、只有固定字段的结构体)和可变大小类型(列表、至少包含一个可变字段的容器)。固定路径是直接内存复制 (memcpy)。可变路径需要支付偏移量簿记和中间缓冲区的开销。

Lighthouse 为可变数据分配一个单独的缓冲区,然后将其复制到最终输出中。每个可变字段都会经过两次写入。对于一个包含 21 个字段的 BeaconState,这会累积起来。

我们编写了一个 ContainerEncoder,它直接将可变数据写入输出缓冲区。固定字段被原地修补到一个预分配的区域。每个字段一次写入,零中间分配。对于全固定容器(BeaconBlockHeader、Fork、Checkpoint),derive 宏完全跳过编码器,生成直接的字段逐个追加。无堆内存,无偏移量跟踪。

另一个瓶颈是逐元素迭代。Lighthouse 通过对每个元素调用 ssz_append 来编码一个 Vec<u64>。在小端平台,Vec<u64> 在内存中的布局已符合 SSZ 预期。我们对整个切片使用单次 memcpy[u8; N] 数组和解码也是如此。

Merkleization 也有类似的问题:Lighthouse 对零哈希表使用 lazy_static(首次访问时计算 64 个 SHA-256 哈希)。我们通过 build.rs 在构建时计算它们。无运行时初始化,无同步。

这些决定塑造了 crate 结构:

libssz-derive ···→ libssz-merkle ──→ libssz ←── libssz-types
  (过程宏)           (Merkleization)    (核心)     (有界集合)
  • libssz: SszEncode / SszDecode traitContainerEncoder / ContainerDecoder,原始实现
  • libssz-types: 使用 const 泛型而非 typenum 的有界集合(SszVectorSszListSszBitvectorSszBitlist
  • libssz-merkle: HashTreeRootmerkleize,构建时零哈希
  • libssz-derive: #[derive(SszEncode, SszDecode, HashTreeRoot)] 带有全固定检测

该结构源于这些决定:Merkleization 是独立的,因为你可能不需要它(例如,仅用于传输的用例)。derive 宏在编译时检测所有固定容器,并生成不同的代码路径。有界类型使用 const 泛型(SszVector<T, 1024>)而非 typenum(Vector<T, U1024>),因为 SSZ 规范的边界始终是字面常量。

一切都为 no_std + alloc 构建。CI 验证每次提交在 thumbv7m-none-eabiriscv64imac-unknown-none-elf 上的编译。

正确性

我们根据官方的 以太坊共识规范测试向量 (v1.6.1) 验证 libssz:涵盖所有 9 个分叉 (Phase0, Altair, Bellatrix, Capella, Deneb, Electra, Fulu, Gloas, EIP-7805) 的 62,489 个测试用例

这包括:

  • ssz_generic: 所有 SSZ 原始类型、向量、列表、位字段、容器和联合体,包括有效和无效的情况
  • ssz_static mainnet: 所有以太坊共识类型(BeaconStateBeaconBlockAttestation 等)在主网参数下
  • ssz_static minimal: 相同类型在最小预设参数下

每个测试用例都验证解码、重新编码的往返正确性以及哈希树根的正确性。

我们对 Lighthouse 和 ssz_rs 运行 19 个差分模糊测试目标,涵盖往返测试、对抗性偏移、深度嵌套和宽联合体。这些测试在 CI 中每晚运行。

性能

我们对照 Lighthouse (ethereum_ssz + tree_hash) 和 ssz_rs v0.9 进行了基准测试,使用 --release 和 thin LTO 编译,在 AMD Ryzen 9 9950X3D (x86_64) 上运行。

编码

类型 libssz Lighthouse ssz_rs 对比 Lighthouse 对比 ssz_rs
BeaconBlockHeader 10.1 ns 84.2 ns 1.71 µs 8.4x 170x
Vec<u64> (100K) 9.20 µs 35.8 µs 2.36 ms 3.9x 257x
BeaconState (100K val) 450 µs 773 µs 201 ms 1.7x 446x
BeaconState (1M val) 10.1 ms 20.3 ms 1.58 s 2.0x 156x

解码

类型 libssz Lighthouse ssz_rs 对比 Lighthouse 对比 ssz_rs
BeaconBlockHeader 8.96 ns 7.08 ns 196 ns 0.8x 22x
Vec<u64> (100K) 9.24 µs 59.7 µs 31.8 µs 6.5x 3.4x
BeaconState (100K val) 313 µs 908 µs 20.5 ms 2.9x 66x
BeaconState (1M val) 9.27 ms 13.9 ms 172 ms 1.5x 19x

在所有测试的验证者数量下,libssz 在 BeaconState 编码和解码方面均快于 Lighthouse。在原始类型上,libssz 在 u64 编码上达到 3.2 倍,在 [u8; 32] 编码上达到 3.2 倍。

包括 ARM (Apple M3 Max) 基准测试和哈希树根比较的完整结果请参见 README。所有基准测试均可通过 cargo bench --bench differential 重现。

采纳情况

ethlambda

ethlambda 是 LambdaClass 用 Rust 编写的一个极简主义的 Lean 共识客户端。PR #242ethereum_ssz 迁移到 libssz:跨 34 个文件增加了 550 行/删除了 460 行。API 接口与 Lighthouse 的 SSZ 足够接近,团队可以无缝替换。

ethrex

ethrex 正在 PR #6361 中实现 EIP-8025。该团队使用 libssz 处理 SSZ 容器(ExecutionPayloadNewPayloadRequestNewPayloadRequestHeader)并计算作为执行证明公共输入的 hash_tree_root

该实现支持多种 zkVM 后端:SP1、RISC Zero、OpenVM 和 ZisK。guest 程序将 (NewPayloadRequest [SSZ], ExecutionWitness [rkyv]) 作为输入,输出 33 字节(一个 32 字节的 SSZ 根加上一个 1 字节的有效性布尔值)。libssz 在 no_std 中工作,因此 ethrex 不需要维护一堆分叉的依赖。

ere-guests

ere-guestsPR #26 中从 ethereum_ssz 切换到 libssz。此次迁移使 stateless-validator-common 完全支持 no_std,从而解除了 Airbender 作为证明后端的阻碍。用于 CI 集成测试的 15 个主网区块仍然符合其固定预期。jsign 测量到 ziskemu 内部的 hash_tree_root 部分有 2 倍的加速,这转换为整体区块证明时间 1.05 倍的加速。

开始使用

libssz v0.1.0 已发布在 crates.io 上。

cargo add libssz libssz-derive libssz-merkle libssz-types

对于 no_std 目标(zkVM、WASM、嵌入式):

cargo add libssz --no-default-features --features alloc
cargo add libssz-types --no-default-features --features alloc
cargo add libssz-merkle --no-default-features --features alloc
cargo add libssz-derive

完整文档、架构指南和示例:github.com/lambdaclass/libssz。采用双重 Apache-2.0 / MIT 许可证。

  • 原文链接: blog.lambdaclass.com/lib...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
lambdaclass
lambdaclass
LambdaClass是一家风险投资工作室,致力于解决与分布式系统、机器学习、编译器和密码学相关的难题。