ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

kona-preimage 源码解读:Optimism Fault Proof 预镜像预言机的高层 Rust API 设计

kona-preimage 源码解读:Optimism Fault Proof 预镜像预言机的高层 Rust API 设计 kona-preimage 源码解读Optimism Fault Proof 预镜像预言机的高层 Rust API 设计【免费下载链接】optimismOptimism is Ethereum, scaled.项目地址: https://gitcode.com/GitHub_Trending/op/optimism本文以 OP Stack 官方 Rust 实现 Kona 仓库中的kona-preimagecrate位于 rust/kona/crates/proof/preimage为核心系统讲解 Fault Proof 系统中Preimage Oracle预镜像预言机的高层接口设计客户端程序如何通过 32 字节 Key 向主机请求数据、主机如何通过 Hint 通道预取数据并回传以及这套协议在no_std与异步两种运行环境下的具体实现。读完本文你将掌握PreimageKey的编码规范、OracleReader/OracleServer与HintWriter/HintReader的交互流程、Channel抽象与原生实现以及VerifyingPreimageFetcher的防篡改校验机制并能够基于这套 trait 体系自行接入新的数据源或移植到自定义证明客户端。一、定位为什么 Fault Proof 需要预镜像预言机在 Optimism 的 Fault Proof故障证明架构中链上运行的证明程序必须能够读取 L1 区块头、L2 输出根、Rollup 配置等大量链外数据来完成状态派生与执行验证。但链上存储空间与成本极其有限无法直接容纳这些大体积数据。解决思路是把数据交给链外的主机host进程持有客户端client证明程序只提交一个 32 字节的 Key由主机通过一个预言机通道把对应的数据喂回给客户端。这套机制在 OP Stack 规范中被称为 Preimage Oracle预镜像预言机。kona-preimagecrate 正是这套机制在 Rust 侧的高层封装。它的设计要点在 README 中概括为两点no_std兼容客户端程序运行在 FPVMFault Proof Virtual Machine等受限环境中因此 crate 默认不依赖标准库lib.rs中通过#![cfg_attr(not(feature std), no_std)]声明主机侧异步化主机程序需要通过网络 RPC、KV 存储等外部数据源取数因此主机侧的句柄全部是async的允许在等待外部数据时并发处理其他请求。lib.rs中#![doc include_str!(../README.md)]将 README 直接嵌入 crate 文档说明这份 README 同时也是该 crate 的 API 文档入口。二、核心抽象Channel、trait 体系与角色划分2.1 通信底座Channeltrait客户端与主机之间的一切交互都建立在通道channel之上。traits.rs定义了Channeltrait包含三个异步方法async fn read(self, buf: mut [u8]) - ChannelResultusize; async fn read_exact(self, buf: mut [u8]) - ChannelResultusize; async fn write(self, buf: [u8]) - ChannelResultusize;read_exact保证读满buf.len()字节这是所有定长协议帧如 32 字节 Key、4 字节长度前缀、8 字节长度前缀读取的基础。Channel的实现与运行环境解耦no_std环境下由 FPVM 提供系统调用通道std环境下则由 native_channel.rs 提供基于async_channel无界队列的内存实现NativeChannel与BidirectionalChannel。BidirectionalChannel::new()通过两条无界异步队列构成双向通信client 句柄的write连接 host 句柄的read反之亦然。测试代码正是用它在内存中模拟完整的 client–host 往返。2.2 六大 trait客户端面与主机面traits.rs将整条数据流拆解为职责清晰的 trait 体系Trait所属侧职责PreimageOracleClient客户端按PreimageKey获取预镜像get返回新分配的Vecu8get_exact写入调用方提供的缓冲区HintWriterClient客户端向主机写入一条 hint 字符串阻塞至收到主机确认CommsClient客户端PreimageOracleClient Clone HintWriterClient的组合 trait一个类型即可同时承担两种客户端职责PreimageOracleServer主机循环读取下一条预镜像请求取数后写回客户端管道HintReaderServer主机读取客户端发来的 hint交给HintRouter路由处理并回执确认PreimageFetcher主机根据PreimageKey从外部数据源获取预镜像HintRouter主机将 hint 路由到对应的处理函数如拉取某个 L1 区块头PreimageServerBackend主机PreimageFetcher HintRouter的组合 trait值得注意的组合 trait 实现方式CommsClient与PreimageServerBackend都是空 trait 自动 blanket impl即任何同时满足其子 trait 约束的类型都会自动获得该组合 trait无需手工实现。2.3 错误模型errors.rs定义了贯穿全 crate 的PreimageOracleError与ChannelError。PreimageOracleError覆盖了管道断裂IOError、非法 KeyInvalidPreimageKey、Key 不存在KeyNotFound、返回数据与 Key 哈希不符IncorrectData、不支持的 Key 类型UnsupportedKeyType、等待超时Timeout、缓冲区长度不匹配BufferLengthMismatch、hint 解析失败HintParseFailed以及兜底的Other。这种错误分类使主机在处理每个请求时都能精确区分管道问题与业务问题从而决定是终止服务还是仅记录错误后继续。三、PreimageKey32 字节的身份编码3.1 布局与类型字节key.rs定义了预镜像的寻址方式。一个PreimageKey始终是 32 字节布局为位段含义第 0 字节类型字节PreimageKeyType第 1~31 字节数据区31 字节PreimageKeyType是一个#[repr(u8)]枚举取值与语义如下与 OP 规范 Pre-image key types 对齐值类型语义1Local本地 Key与特定 Fault Proof 实例相关、依赖上下文通常映射引导bootstrap数据2Keccak256默认全局 Key由预镜像的 keccak256 摘要低 31 字节映射3GlobalGeneric保留未来使用4Sha256全局 Key由预镜像的 sha256 摘要低 31 字节映射5Blob全局 Key构造为keccak256(commitment z)后把最高字节替换为类型字节6Precompile全局 Key构造为keccak256(precompile_addr input)后把最高字节替换为类型字节TryFromu8将类型字节解析为枚举超出 1~6 的范围返回InvalidPreimageKeytest_preimage_key_from_u8测试验证了0与7均报错。3.2 构造器与编码转换PreimageKey提供多种构造方式覆盖典型使用场景new(key: [u8; 32], key_type)取 32 字节输入的低 31 字节作为数据区new_local(local_ident: u64)把 64 位本地标识写入数据区低 8 字节大端用于引导数据寻址new_keccak256(digest)keccak256 摘要的快捷构造new_precompile(addr, input)内部计算keccak256(addr input)并截断。与线格式互转的路径为PreimageKey - [u8; 32]From实现把类型字节放回首位、[u8; 32] - PreimageKeyTryFrom解析首位类型字节。Display实现输出完整的0x前缀 B256 字符串见test_preimage_key_display类型字节为01时输出0x01ffff...。3.3 本地 Key 常量Fault Proof ABI 的一部分local_keys.rs中导出了 8 个引导数据槽位的本地 Key 常量源码注释明确指出这些值属于 Fault Proof ABI 的一部分禁止变更常量值用途L1_HEAD_KEY1L1 头哈希含派生争议 L2 区块所需数据L2_OUTPUT_ROOT_KEY2双方认可的 L2 输出根派生起点Interop 下为约定的超级链前状态承诺L2_CLAIM_KEY3争议中的 L2 输出根声明Interop 下为声明的超级链后状态承诺L2_CLAIM_BLOCK_NUMBER_KEY4争议 L2 区块号Interop 下为声明的 L2 时间戳L2_CHAIN_ID_KEY5选择网络特定配置的链 IDL2_ROLLUP_CONFIG_KEY6Rollup 配置的 oracle 回退L1_CONFIG_KEY7L1 链配置的 oracle 回退DEPENDENCY_SET_KEY8Interop 依赖集的 oracle 回退local_key_values_match_fault_proof_abi测试断言这些常量与 Fault Proof ABI 完全一致防止无意改动。四、客户端侧OracleReader 与 HintWriter 的取数与提示协议4.1OracleReader.get一次完整的取数往返客户端通过OracleReaderoracle.rs发起请求流程分三步写 Keywrite_key将PreimageKey序列化为 32 字节写入通道主机据此准备数据读长度读取 8 字节大端u64长度前缀作为后续读取的数据量也为get_exact的缓冲区校验提供依据读数据按长度读取完整数据长度为 0 时直接返回空结果。get返回新分配的Vecu8适合数据大小未知的场景get_exact则要求调用方预先提供精确大小的缓冲区若buf.len() ! length返回BufferLengthMismatch避免不必要的数据拷贝。test_oracle_client_and_host与test_oracle_reader_get_exact两个测试在内存通道上完整验证了这两条路径主机侧循环处理请求直至收到IOError通道关闭退出。4.2HintWriter.writehint 线协议与阻塞确认hint 是客户端提前告知主机接下来可能需要哪些数据的轻量通知使主机可以并行预取从而降低取数延迟。hint.rs中的HintWriter实现了HintWriterClient将 hint 字符串按4 字节大端长度前缀 原始字节写入通道阻塞等待主机返回 1 字节确认read_exact读入hint_ack后才返回。这个阻塞至确认的语义在 trait 文档中有明确说明写入会覆盖管道中任何旧 hint且必须等到主机处理完成客户端才能继续。五、主机侧OracleServer 与 HintReader 的服务循环5.1OracleServer.next_preimage_request请求—取数—回写主机侧对应物是OracleServer其next_preimage_requestF: PreimageFetcher是一个单次服务循环读取 32 字节并解析为PreimageKey调用fetcher.get_preimage(key)从外部数据源取数异步依次写入 8 字节大端长度与数据本体。该方法是逐请求调用的主机通常在一个循环中不断调用它测试代码即如此直到通道关闭返回IOError退出。PreimageFetcher是注入点主机可自由实现为 RPC 客户端、KV 读取器或内存缓存与OracleServer完全解耦。5.2HintReader.next_hint读取、路由、回执HintReader实现HintReaderServer流程为读取 4 字节长度前缀再读取对应字节数的 hint 负载将字节解码为 UTF-8 字符串失败时仍回写 1 字节失败信号0x00防止客户端永久阻塞并返回HintParseFailed将 hint 交给HintRouter.route_hint处理如触发 L1 数据预取路由失败同样回写0x00解除客户端阻塞成功后回写0x00作为确认。hint.rs的测试test_unblock_on_bad_utf8、test_unblock_on_fetch_failure、test_hint_client_and_host专门验证了失败也必须解除客户端阻塞这一关键健壮性设计无论 UTF-8 解码失败还是路由失败客户端都不会卡死。六、防篡改校验VerifyingPreimageFetcher6.1 为什么要二次校验预镜像数据可能经过不可信的 RPC 响应、hint 处理 bug 或被篡改的 KV 存储。若错误数据进入证明程序可能导致错误的证明结果。verifyfeature 下的 verifier.rs 提供一个装饰器VerifyingPreimageFetcherF包装内部PreimageFetcher在取数时就重新哈希校验把坏数据挡在传播之前。6.2verify_preimage的分类型策略verify_preimage(key, data)按 Key 类型区分处理Keccak256对返回数据计算keccak256比较摘要低 31 字节与 Key 数据区是否一致Sha256同理计算sha256并比较Local / Blob / Precompile直接放行——Local 是本地不透明标识、Blob 的单个字段元素需要 KZG 证明才能验证、Precompile 结果需要输入才能验证仅凭(key, data)无法自证GlobalGeneric作为保留且当前无任何主机路径产生它的类型直接返回UnsupportedKeyType让未来可能的误用响亮地失败而非静默通过。校验失败统一返回携带原 Key 的IncorrectData方便定位是哪一次请求的数据被污染。VerifyingPreimageFetcher同时实现了HintRouter透传给内部因此可以直接作为PreimageServerBackend使用。verify模块的 9 个测试覆盖了全部关键路径合法 keccak/sha 数据通过、损坏数据与空数据被拒绝、Local/Blob/Precompile 放行、GlobalGeneric 拒绝、错误携带 Key、内部错误透传。七、Feature 配置与在 Kona 中的实际使用7.1 Cargo featurescrates/proof/preimage/Cargo.toml 将依赖按 feature 严格隔离default []默认不启用任何 feature保证no_std纯净std启用async-channel及alloy-primitives/std等提供NativeChannel/BidirectionalChannel等原生内存通道verify启用sha2提供VerifyingPreimageFetcher与verify_preimagerkyv/serde为PreimageKey与PreimageKeyType派生零拷贝序列化 / serde 序列化便于在 SP1 等场景跨边界传递。7.2 在仓库中的消费方从各Cargo.toml的依赖声明可以看出该 crate 的覆盖范围bin/clientkona-preimage.workspace true默认无 std feature用于 FPVM 内运行的no_std客户端程序bin/hostfeatures [std, verify]主机侧既需要异步原生通道也需要防篡改校验crates/proof/proof 与 proof-interop、std-fpvm在证明与 Interop 证明逻辑中复用其 trait 与类型sp1/crates 等 SP1 相关 crate分别按需启用serde、rkyv、std说明该 crate 已适配 zkVM 证明路径。这种客户端零依赖 主机全功能的 feature 拆分正是它能在 FPVM、SP1 与传统异步主机三种环境中无缝复用的关键。八、小结一套协议两个角色多种载体kona-preimage用约十个文件、一组精心设计的 trait完整实现了 Fault Proof 预镜像预言机的高层协议一条数据通道Channel承载 32 字节 Key、8 字节长度、数据本体的定长帧交互与运行环境解耦两条交互线取数线PreimageOracleClient/PreimageOracleServer/PreimageFetcher与提示线HintWriterClient/HintReaderServer/HintRouter客户端提示预取、主机回执确认二者并行不悖一种寻址规范PreimageKey类型字节 31 字节数据的 32 字节编码覆盖 Local/Keccak256/Sha256/Blob/Precompile 五类语义一层纵深防御VerifyingPreimageFetcher在取数入口对自描述哈希类 Key 做重哈希校验。无论你是要理解 OP Stack Fault Proof 的数据交互机制还是计划在自定义证明客户端或主机中接入新的数据源都可以从这套 trait 出发客户端侧实现Channel 复用OracleReader/HintWriter主机侧实现PreimageFetcher/HintRouter 复用OracleServer/HintReader即可获得与 Kona 官方实现一致的协议行为。继续深入可阅读的仓库源码客户端取数实现 oracle.rs、hint 线协议 hint.rs、Key 编码 key.rs、防篡改校验 verifier.rs、原生内存通道 native_channel.rs以及各 trait 的完整定义 traits.rs。【免费下载链接】optimismOptimism is Ethereum, scaled.项目地址: https://gitcode.com/GitHub_Trending/op/optimism创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表