feat(kona): execute NUT bundles at Karst fork activation#20157
feat(kona): execute NUT bundles at Karst fork activation#20157
Conversation
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #20157 +/- ##
===========================================
- Coverage 76.6% 0.5% -76.2%
===========================================
Files 691 488 -203
Lines 76081 61762 -14319
===========================================
- Hits 58298 320 -57978
- Misses 17639 61442 +43803
+ Partials 144 0 -144
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
c38d430 to
0a41e7d
Compare
Add build.rs that reads karst_nut_bundle.json at compile time and generates Rust code to construct the NutBundle. The derive pipeline now adds upgrade gas to the block gas limit at fork activation, matching op-node's gas accounting behavior.
Verifies that every fork with an embedded NUT bundle produces an activation block containing exactly the bundle's deposit transactions. Discovery uses forks.All + derive.UpgradeTransactions, so future forks with NUT bundles are covered automatically without test changes.
0a41e7d to
df9aabf
Compare
| // the copy is byte-identical to op-core/nuts/bundles/<fork>_nut_bundle.json. | ||
| let manifest_dir = | ||
| PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set")); | ||
| let karst_bundle = manifest_dir.join("bundles/karst_nut_bundle.json"); |
There was a problem hiding this comment.
We had discussed whether it would be possible to reach out to the file in op-core, or else just use a symlink to it, but that did not work with the kona build, as the docker context would need to be expanded to include the monorepo root.
If we're ok with that, then it might be better to go that way.
There was a problem hiding this comment.
I see... wondering if we could add a dir shared/nuts to the repo root and then only add shared/ to the docker context (in addition to rust/) and also reach out to this dir from Go for embedding the bundle JSONs?
There was a problem hiding this comment.
go:embed doesn't allow reaching out to relative paths like that.
So what I ended up doing is just caf98bb, which expands the context to include op-core/nuts/bundles.
It may be a bit weird to have rust reach into a go package, but it is better than the duplication, and it allowed me to remove (e3dc8f0) a lot of the code that was required to copy the bundle into the crate dir.
| // the copy is byte-identical to op-core/nuts/bundles/<fork>_nut_bundle.json. | ||
| let manifest_dir = | ||
| PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set")); | ||
| let karst_bundle = manifest_dir.join("bundles/karst_nut_bundle.json"); |
There was a problem hiding this comment.
I see... wondering if we could add a dir shared/nuts to the repo root and then only add shared/ to the docker context (in addition to rust/) and also reach out to this dir from Go for embedding the bundle JSONs?
941e15a to
caf98bb
Compare
The Docker build context for kona images is scoped to rust/, so build.rs cannot reach op-core/nuts/bundles/ via ancestor walk and the kona-client/ host/node images fail to build. Keep a byte-identical copy inside the crate, written and verified automatically by the existing snapshot/lock tooling so the two sources cannot silently drift.
This reverts commit 941e15a.
The kona-hardforks build.rs walks ancestors of CARGO_MANIFEST_DIR to find
op-core/nuts/bundles/ during compilation. The kona-{client,host,node}
images scope their Docker context to rust/, so the ancestor walk can't
reach op-core/ and the images fail to build.
Pass op-core/nuts/bundles as an additional named BuildKit context
(nuts-bundles) and copy it into /workspace/op-core/nuts/bundles so the
ancestor walk succeeds. Keeps the primary rust/ context small and avoids
mirroring the bundle JSON into the crate tree.
caf98bb to
815bf97
Compare
Iterate forks.From(forks.Karst) with a NoError assertion on derive.UpgradeTransactions rather than silently skipping forks whose bundle fails to load, so a broken bundle surfaces as a test failure. Also assert every tx in the activation block has a successful receipt so a reverted upgrade tx can't hide behind the byte-equality check.
The Hardfork impl for each NUT-bundle-backed fork is identical boilerplate (decode, EIP-2718 encode, sum gas). Move that pattern into a single internal macro so adding future forks is a one-liner next to the build-script-generated constructor.
Split parsing + codegen into build_helpers.rs, included via #[path] from both the build script and a new integration test. Use anyhow to carry error context up to a single panic in main. The integration test runs the generator on a fixture JSON and asserts the exact Rust source output, so codegen changes surface as a test failure rather than a downstream compile error.
Follows the per-fork test convention from ecotone/fjord/isthmus/holocene. Asserts the EIP-1967 implementation slot of representative predeploy proxies (L1Block, GasPriceOracle) changes across Karst activation — a Karst-specific smoke test that the bundle's proxy upgrades took effect. Not generalized into the NUT bundle activation test because it does not hold for future forks that may not upgrade proxies.
Summary
Wires Karst NUT bundle execution into kona-node's payload derivation, matching op-node's behavior.
kona-hardforksgets abuild.rsthat readsop-core/nuts/bundles/karst_nut_bundle.jsonat compile time and emits Rust code constructing theNutBundle— keeps the crateno_stdand serde-free at runtime.Hardforktrait growsupgrade_gas(); the stateful attributes builder adds that gas to the block gas limit when Karst activates, so upgrade transactions have headroom.forks.rsupdated fromlen() == 0tolen() == 32to reflect the populated bundle.TestActivationBlockNUTBundleinop-e2e/actions/proofs/verifies activation block contents generically: discovery runs throughforks.All+derive.UpgradeTransactions, so any future fork that ships a NUT bundle is covered without test changes.Test plan
cargo nextest runinrust/kona— 119 passedtest_karst_upgrade_txsasserts 32 transactionstest_karst_upgrade_gasasserts 51_600_000 gasTestActivationBlockNUTBundle/karstcompiles + vets clean locally (full run needs forge artifacts — CI)