ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Optimism NUT Bundles 深度解析:L2 硬分叉激活交易的锁定、嵌入与来源验证机制

2026/9/17 1:59:08 拓冰建站 浏览量
Optimism NUT Bundles 深度解析:L2 硬分叉激活交易的锁定、嵌入与来源验证机制 Optimism NUT Bundles 深度解析L2 硬分叉激活交易的锁定、嵌入与来源验证机制【免费下载链接】optimismOptimism is Ethereum, scaled.项目地址: https://gitcode.com/GitHub_Trending/op/optimismOptimism 的每次 L2 硬分叉如 karst、lagoon背后都有一组有序的存款交易——即 NUTNetwork Upgrade Transactionbundle。本文基于op-core/nuts模块的官方文档与配套源码完整讲解 NUT bundle 的文件构成、两 PR 更新流程、来源验证命令与 CI 校验逻辑并结合 lock.go、bundles.go 与 check-nut-locks 的实现帮助读者掌握“如何安全地为一次硬分叉冻结部署交易并证明其字节级可复现”这一完整工程机制。NUT Bundle 是什么按 README 的定义NUT bundle 是定义 L2 存款交易的集合这些交易负责激活一次硬分叉。每个 bundle 是一个 JSON 文件内部包含有序交易合约实现部署、代理升级等由 rollup 节点op-node 与 kona-node在编译期嵌入并在分叉激活块fork activation block处执行。这个模块由两类文件构成文件作用fork_lock.toml锁文件将 fork 名称映射到 bundle 路径、sha256 哈希与来源提交bundles/fork_nut_bundle.json嵌入 bundle在分叉激活时被 op-node 和 kona-node 消费当前仓库中锁文件实际锁定了 karst 与 lagoon 两个分叉例如 karst 条目记录了 bundle 相对路径、sha256:08f5df36...哈希与完整提交 SHAf2e5bfe4...。锁文件末尾还带有一条面向评审者的显式警示对 fork_lock.toml 的任何修改都会影响嵌入 op-node 与 kona-node 的硬分叉激活 bundle必须仔细审查。bundle 如何进入节点二进制go:embed 嵌入从源码结构看bundle 并非运行时下载而是直接编译进 Go 二进制。bundles.go 中每个分叉对应一个//go:embed声明// KarstNUTBundleJSON is the embedded Karst NUT bundle. // //go:embed bundles/karst_nut_bundle.json var KarstNUTBundleJSON []byte // LagoonNUTBundleJSON is the embedded Lagoon NUT bundle. // //go:embed bundles/lagoon_nut_bundle.json var LagoonNUTBundleJSON []byte这意味着 bundle 的“冻结”发生在构建时刻op-node/kona-node 构建出来的二进制里分叉激活时执行的交易内容与仓库中bundles/下的 JSON 字节完全一致任何部署前的篡改都会与fork_lock.toml中的哈希失配从而被 CI 拦截。锁文件的解析与写回lock.goop-core/nuts/lock.go 实现了锁文件的读写与规范化ForkLockEntry结构体严格对应 toml 中的三个字段bundle/hash/commitForkLock则是以 fork 名称为 key 的 mapLockFilePath借助opservice.FindMonorepoRoot定位仓库根保证无论从哪个子目录执行工具都能找到op-core/nuts/fork_lock.tomlWriteLockFile在写回时按 forks.All 定义的时间顺序bedrock → regolith → canyon → … → jovian → karst → lagoon输出条目避免新增分叉时打乱旧条目顺序未知 fork 名称则以字母序兜底写在最后。forks.go 同时提供Next/Prev等前驱后继关系这正是后续 pre-fork state 链见下文依赖的骨架新增主线分叉时必须同步在All列表中添加名称锁文件排序与状态链推导都建立在这一单一事实来源之上。更新 bundle 的两 PR 工作流README 明确更新某个分叉的 bundle 是一个两 PR 流程two-PR flow其设计目的是让锁文件中记录的提交成为develop分支的近期祖先从而在 squash-merge 后仍然保留在分支历史中。PR 1 — 合约变更修改 Solidity 源码后在合约子项目内重新生成仓内快照cd packages/contracts-bedrock just generate-nut-bundle该命令更新两个文件packages/contracts-bedrock/snapshots/semver-lock.json如果任何 predeploy 字节码发生变化packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json候选 bundle。两者需与合约变更一同提交。关键约束这个 PR 必须先合入develop才能进入 PR 2。PR 2 — 为分叉做 bundle 快照基于更新后的develop拉出分支执行just nut-snapshot-for fork从 justfile 可见该任务实际调用go run ./ops/scripts/nut-snapshot-for fork把current-upgrade-bundle.json复制到op-core/nuts/bundles/fork_nut_bundle.json并用sha256 哈希与和origin/develop的 merge-base 提交更新fork_lock.toml。为什么记录 merge-base 而不是 HEADREADME 对此有专门解释记录的提交是相对于develop的 merge-base。通过确保所记录的提交是develop的近期祖先该引用就能在 squash-merge 之后依然存活于develop分支的历史中——这正是 PR 1 必须先合并的原因。随后为该分叉本身生成 pre-fork 状态并与锁一起提交这样下一个分叉的激活测试才有可用的启动状态just nut-prefork-state-for fork该命令写出op-core/nuts/state/fork_state.json等于“上一个分叉的状态 应用本分叉 bundle 后的状态”check-nut-locks会强制要求它存在。从 justfile 的实现可以看到nut-prefork-state-for先依赖build-contracts build-superchain-go再通过环境变量门控一个测试来完成生成OP_E2E_GEN_PREFORK_STATE{{fork}} go test -count1 -run TestGenerateForkState ./rust/kona/tests/proofs/也就是说生成逻辑复用了验证测试的activateFork使“生成”与“验证”保持同步in lockstep这也是它住在 proofs 测试套件里而非独立二进制中的原因。pre-fork state 的状态链jovian 为种子逐分叉累积op-core/nuts/state/README.md 补充了锁文件未展开的重要细节。fork_state.json冻结的是该分叉激活后、下一分叉激活前L2predeploy 范围的完整状态代理及其实现含全部存储。状态文件以它所代表的分叉命名而非消费它的 bundle文件状态时点被谁消费jovian_state.jsonjoviankarst bundle 测试karst_state.jsonkarstlagoon bundle 测试lagoon_state.jsonlagoon下一个分叉状态是逐层合成的karst_state jovian_state (karst bundle 应用后)依此类推。分叉 F 的 NUT bundle 激活测试rust/kona/tests/proofs/nut_bundle_activation_test.go直接从forks.Prev(F)_state.json启动而不是用当前源码重新构建 genesis——因此它验证的是不可变的已锁 bundle在其实际设计要升级的 predeploy 版本上的表现。两个值得注意的边界种子状态 jovian 必须用其时代工具链生成ops/scripts/gen-seed-state.sh因为当前 op-deployer 无法消费 jovian 时代的合约而后续状态只重放已冻结 bundle不需要分叉时代合约构建现代工具即可必须逐分叉生成并提交loader 在编译期嵌入状态文件所以生成fork_state.json之前prev_state.json必须已在磁盘上确定性合成状态karst_state 及以后在给定已提交种子的前提下是字节级可复现的——生成器会清零L1Block 的 L1 属性槽整数槽 0..8时间戳、L1 哈希等见canonicalizeL1Block因为这些值由逐块 L1-info 存款写入会随测试墙钟变化种子状态本身则因 op-deployer 运行随机化 CREATE2 salt 而不是字节可复现仓库中提交的是其中一次具体实例。验证 bundlenut-provenance-verify对某个分叉的来源provenance验证命令为just nut-provenance-verify fork对应 justfile 的go run ./ops/scripts/nut-provenance-verify fork。它检查两点bundle 文件存在且其 sha256 与锁文件记录一致在锁文件记录的提交处创建一个临时 worktree重新生成 bundle并与已提交 bundle 做逐字节比对。第 2 步真正的来源证明需要forge环境。该机制把“bundle 由哪个提交生成”从口头承诺变成了可机器重放的证据。CI 校验两道闸门README 列出的两个 CI 检查其实现细节在 ops/scripts/check-nut-locks/main.go 与 justfile 中可以得到印证check-nut-locks— 每个 PR 都会运行由go run ./ops/scripts/check-nut-locks驱动。从源码看它对锁文件中每个条目执行四项校验读取 bundle 内容计算 sha256 并与hash字段比对失配即报错并提示更新锁文件检查commit字段非空通过git merge-base --is-ancestor commit origin/develop验证记录的提交确实是origin/develop的祖先脚本注释还说明若要从非 develop 分支生成 bundle需在此函数加特例并 cherry-pick 回 develop见packages/contracts-bedrock/book/src/policies/release-process.md的 L2 合约发布章节检查op-core/nuts/state/fork_state.json存在保证状态链完整使下一个分叉的激活测试有启动基础。此外还有一项反向检查glob 扫描op-core/nuts/bundles/*_nut_bundle.json任何没有对应锁条目的 bundle 文件都会导致失败——防止“忘记上锁”的 bundle 悄悄混入并影响节点行为。check-nut-prefork-states— 重新生成所有已提交的非种子状态文件若任何已提交的状态快照发生漂移则失败。justfile 中的 bash 脚本实现了这一逻辑遍历op-core/nuts/state/*_state.json跳过jovian种子不可重放对其余每个 fork 调用_nut-prefork-state-for最后用git diff --exit-code判定零漂移。该任务依赖build-contracts build-superchain-go先产出构建工件。fork_lock.toml 模式schemaREADME 给出的锁文件模式如下三个字段均为必填语义[fork-name] bundle op-core/nuts/bundles/fork_nut_bundle.json # 仓库相对路径 hash sha256:hex # bundle 内容的 sha256 commit full-sha # 生成该 bundle 的提交这与 fork_lock.toml 中 karst/lagoon 的实际条目一一对应也与 lock.go 中ForkLockEntry的 toml tag 完全一致——schema、解析器与 CI 校验器三者共同保证了对锁文件的单一解释。小结一条“合约源码 → 冻结交易 → 节点行为”的信任链NUT bundle 机制把 L2 硬分叉激活从“运维手工发交易”变成了可验证的工程流程合约变更PR 1产生候选 bundlejust nut-snapshot-for forkPR 2将其冻结进 op-core/nuts/bundles/ 并写入哈希与提交引用bundles.go 的go:embed让 op-node/kona-node 在分叉激活块处执行的就是这份被字节锁定的交易序列just nut-provenance-verify fork可在记录提交上重放生成过程做逐字节证明而check-nut-locks与check-nut-prefork-states两道 CI 闸门连同 pre-fork 状态链保证分叉激活测试始终在“上一分叉的真实状态”上验证“下一个分叉的不可变 bundle”。理解这条链路也就理解了 Optimism 在多分叉并行演进时如何保证激活行为的可复现与可审计。【免费下载链接】optimismOptimism is Ethereum, scaled.项目地址: https://gitcode.com/GitHub_Trending/op/optimism创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考