Espresso 2: derivation pipeline (re-hosted)#457
Merged
Conversation
QuentinI
commented
Jun 17, 2026
c6b82ca to
5b28541
Compare
34c9d0d to
60f92ee
Compare
a6a2298 to
d5ff5d1
Compare
8a45116 to
266fe6f
Compare
Adds the Espresso-introduced contracts and the minimum supporting changes
required for them to compile, test, and pass the contract checks.
New contracts and scripts:
- src/L1/BatchAuthenticator.sol and interfaces/L1/IBatchAuthenticator.sol
(upgradeable contract that authenticates batch transactions, with switching
between Espresso and fallback batchers)
- scripts/deploy/DeployBatchAuthenticator.s.sol and
scripts/deploy/DeployEspresso.s.sol
- test/L1/BatchAuthenticator.t.sol and test/mocks/MockEspressoTEEVerifiers.sol
- snapshots/{abi,storageLayout}/BatchAuthenticator.json
- snapshots/semver-lock.json entry for BatchAuthenticator
New submodules:
- lib/espresso-tee-contracts (interfaces required by BatchAuthenticator)
- lib/openzeppelin-contracts-upgradeable-v5 (OZ v5 used by BatchAuthenticator
via OwnableUpgradeable)
Supporting changes (Espresso-driven):
- foundry.toml: remappings for OZ v5 and espresso-tee-contracts; ignored
warning codes for vendored libs; OOM-safe jobs settings; via-ir profile.
- justfile: fix-proxy-artifact recipe to handle OZ v5 shadowing Proxy/ProxyAdmin
artifacts; build/coverage hooks.
- src/universal/Proxy.sol, src/universal/ProxyAdmin.sol: pin pragma to exact
0.8.15 so they stay in their own compilation group and never emit PUSH0.
- src/universal/ReinitializableBase.sol: loosen pragma to ^0.8.15 so
BatchAuthenticator (compiled with OZ v5) can import it.
- scripts/* and test/*: disambiguate Proxy artifact lookups to
src/universal/Proxy.sol:Proxy (avoids OZ v5 proxy/Proxy.sol shadow).
- scripts/checks: bypass interface checks for artifacts originating from lib/;
add Espresso-related contract names to exclude lists; pragma exclusions for
Proxy/ProxyAdmin/BatchAuthenticator.
- test/vendor/Initializable.t.sol: exclude BatchAuthenticator (deployed by a
separate Espresso script).
Co-authored-by: OpenCode <noreply@opencode.ai>
Co-authored-by: piersy <pierspowlesland@gmail.com>
- strict-pragma: remove unneeded exclusions for src/universal/Proxy.sol and src/universal/ProxyAdmin.sol — both already use strict 'pragma solidity 0.8.15;', so the entries (and their misleading comment claiming '^') were dead. - interfaces: move the Espresso excludeContracts block out of the upstream-shared area and down next to the Celo block, with one entry per line to match the surrounding style. Localizes future rebase deltas. Co-authored-by: OpenCode <noreply@opencode.ai>
Inline the EspressoTEEVerifier deployment in DeployEspresso.s.sol so it
no longer imports lib/espresso-tee-contracts/scripts/DeployTEEVerifier.s.sol
or DeployNitroTEEVerifier.s.sol. The upstream scripts pulled OZ v5's
TransparentUpgradeableProxy (and its auto-deployed ProxyAdmin) into the
OP artifact tree, shadowing src/universal/ProxyAdmin.sol and forcing a
~90-line fix-proxy-artifact justfile recipe.
The TEEVerifier is now deployed behind src/universal/Proxy.sol +
src/universal/ProxyAdmin.sol, matching how BatchAuthenticator is
deployed in the same script. ERC-1967 slots are unchanged, so external
callers see no difference.
The raw vm.getCode("ProxyAdmin") lookups in the deploy scripts and
BatchAuthenticator tests are switched to the explicit artifact path
vm.getCode("forge-artifacts/ProxyAdmin.sol/ProxyAdmin.json") to
deterministically resolve the default compilation profile's bytecode
(the dispute profile transitively compiles ProxyAdmin at optimizer_runs=5000,
creating a second artifact that broke unqualified lookups).
The fix-proxy-artifact recipe and its 5 callsites are removed.
Cherry-picked from piersy's commit 5d0a803 on PR #443. Walks the dual-batcher state machine: Espresso path → switchBatcher → fallback path → switchBatcher → Espresso path. Asserts every transition emits the expected event, that signer registration survives the round-trip, and that re-issuing the same call after a mode flip changes the outcome (the previously-valid Espresso signature is no longer consulted on the fallback path). Co-authored-by: Piers Powlesland <pierspowlesland@gmail.com> Co-authored-by: OpenCode <noreply@opencode.ai>
Replace the hand-rolled `EspressoBatcherEntry[]` history + binary search with OpenZeppelin's `Checkpoints.Trace160` (`(uint96 key, uint160 value)`). `uint160` is exactly an address with no waste, and `uint96` easily covers L1 block numbers. `upperLookupRecent` replaces the custom binary search and the same-block-overwrite branch is now handled inside `_insert`. Co-authored-by: OpenCode <noreply@opencode.ai>
…sed warning codes Drop the two impl imports (EspressoTEEVerifier, EspressoNitroTEEVerifier) from DeployEspresso.s.sol and replace direct instantiation with vm.getCode + assembly create, reading bytecode from the submodule's own out/ directory. This removes the impl closure (TEEHelper, JournalValidation, and the aws-nitro-enclave-attestation chain) from OP's solc invocations. The impls are still parsed/ABI-checked by forge via libs=['lib'], but they no longer require bytecode emission or the optimizer backend. Since OP's build no longer compiles the submodule's impl files, the three error codes those files triggered (6321 unnamed return, 5667 unused param, 1878 missing SPDX) can be removed from ignored_error_codes. OP's own code does not trigger any of them. The lint_on_build=false workaround is also removed for the same reason — with the impl closure gone, forge lint reports 283 warnings (all from OP's own code), none of which cause a build failure. Adds fs_permissions read access for lib/espresso-tee-contracts/out/ so vm.getCode can locate the pre-built artifacts. The submodule must be built (forge build --root lib/espresso-tee-contracts) before OP's main build. Co-authored-by: OpenCode <noreply@opencode.ai>
Two follow-ups required after cherry-picking the batch-auth cache dependency-injection and rollup.Config-in-DataSourceConfig changes: - batch_authenticator_test.go: thread the BatchAuthCaches argument through the duplicate-authentication caller test, which post-dates the cache-DI change and so was not updated by it. - IsEspresso: guard against a nil Config receiver. Holding the rollup.Config in DataSourceConfig means IsEspresso is now reached via DataSourceConfig.rollupCfg, which is nil for non-Espresso data-source tests; the previous DataSourceConfig.isEspresso method was nil-safe. Co-authored-by: OpenCode <noreply@opencode.ai>
Adds derivation tests that close two gaps in the Espresso batch-auth
coverage. Both data sources gate event-based authentication on
IsEspresso(ref.Time), and each implements that gate separately, but no
test exercised the gate flipping across activation or verified that
multiple batches are matched to their own commitments.
Fork-boundary tests (TestDataFromEVMTransactionsForkBoundary for the
calldata source, TestDataAndHashesFromTxsForkBoundary for the blob
source) reuse a single DataSourceConfig with EspressoTime set and vary
only ref.Time across the activation boundary:
- pre-fork (ref.Time < EspressoTime): the batcher tx is accepted via
upstream sender-based auth, and an empty L1 mock asserts that zero
receipt scanning occurs;
- non-batcher senders remain rejected pre-fork;
- activation block (ref.Time == EspressoTime): the same batcher tx is
rejected without a BatchInfoAuthenticated event and accepted with one.
This pins the ">=" gate in both directions: a regression to ">" makes the
activation block either accept an unauthenticated batch or skip the event
scan, failing the test. The blob-source copy drives a type-2 calldata tx,
the shape an Ecotone-active, calldata-batching chain (e.g. Celo) submits
through the blob source.
TestDataFromEVMTransactionsEventAuth gains a "multiple authenticated txs
each accepted for their own commitment" case: two distinct batches, each
authenticated by its own commitment, must both be accepted in order and
mapped to their own data, verifying each tx is matched against its own
commitment rather than to "some" authenticated entry.
Test-only change; no production code is modified.
Moves the Espresso batch-auth tests out of the upstream calldata/blob data-source test files into new espresso_calldata_source_test.go and espresso_blob_data_source_test.go, and renames batch_authenticator_test.go to espresso_batch_authenticator_test.go. Pure test relocation: no production code and no test logic, names, comments, or assertions change. The goal is to keep all Espresso-specific tests in espresso_-prefixed files so upstream changes to the shared data-source test files cannot conflict with them on rebase. Moved into espresso_calldata_source_test.go (from calldata_source_test.go): the mockAuthEvents helper, TestDataFromEVMTransactionsEventAuth (including the "multiple authenticated txs each accepted for their own commitment" subtest), and TestDataFromEVMTransactionsForkBoundary. Moved into espresso_blob_data_source_test.go (from blob_data_source_test.go): TestDataAndHashesFromTxsEventAuth and TestDataAndHashesFromTxsForkBoundary. The upstream tests (TestDataFromEVMTransactions, TestDataAndHashesFromTxs, TestFillBlobPointers, TestBlobDataSourceL1FetcherErrors) stay in their original files. The only import adjustment is dropping io, common/hexutil, and op-service/txmgr from the new blob file (used only by the kept TestBlobDataSourceL1FetcherErrors); both original import blocks are unchanged.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
d5ff5d1 to
27f75a7
Compare
266fe6f to
2782976
Compare
…nd (#462) CollectAuthenticatedBatches walks the L1 parent chain over the BatchAuthLookbackWindow, calling FetchReceipts and L1BlockRefByHash on each block. Both error sites wrapped every error as a TemporaryError, including ethereum.NotFound. A TemporaryError makes the pipeline retry the same step at the same L1 origin with capped exponential backoff and no give-up, so a persistently unavailable in-window ancestor (e.g. an L1 reorg orphaned it) causes a silent derivation stall. The independent reorg detection in L1Traversal only validates the single forward parent-hash link to the next origin, not the [ref-window, ref-1] range the lookback walks, so it does not rescue this case. The sibling data sources already classify a NotFound ref block as a ResetError (calldata_source.go, blob_data_source.go); a reset rewinds to a canonical origin and re-derives, which is the correct response to a missing L1 block. Match that here: NotFound -> ResetError, other errors stay TemporaryError. Adds TestCollectAuthenticatedBatchesResetOnNotFound covering NotFound on FetchReceipts and on L1BlockRefByHash (both -> ResetError) and a non-NotFound error (stays TemporaryError).
Collaborator
Author
|
@piersy that made total sense, merged, thanks! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b7785e75b0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
piersy
approved these changes
Jun 22, 2026
QuentinI
added a commit
to celo-org/celo-kona
that referenced
this pull request
Jun 30, 2026
…lldataSource The pre-ecotone calldata data source is unreachable on Celo: every Celo chain sets ecotone_time = 0 (active from the L2/Cel2 transition), so CeloEthereumDataSource always routes to the blob source. Espresso (espresso_time) activates strictly after ecotone, so the Espresso-specific batch-authentication branch folded into CeloCalldataSource could never fire. Remove CeloCalldataSource entirely and wire ethereum.rs to upstream kona's CalldataSource for the (still byte-identical, still unreachable) pre-ecotone path. The blob source retains the Espresso event-based authentication and also handles plain calldata-carrying batcher txs post-ecotone, so no behaviour is lost. Addresses piersy's review thread on calldata.rs. The matching Go-side removal in celo-org/optimism#457 is out of scope for this change. Co-authored-by: OpenCode <noreply@opencode.ai>
piersy
added a commit
that referenced
this pull request
Jul 1, 2026
The calldata data source is only reached pre-ecotone: OpenData routes to the blob source whenever ecotone is active (ref.Time >= ecotone_time). Every Celo chain sets ecotone_time = 0, and Espresso (espresso_time) activates strictly later, so the Espresso event-based batch-authentication branch that #457 folded into DataFromEVMTransactions could never fire on a real chain. Revert DataFromEVMTransactions / CalldataSource to the upstream sender-based path (no lookback-window event scan, no L1Fetcher): the calldata source now uses isValidBatchTx + isAuthorizedBatchSender only. The blob source keeps Espresso event-based authentication and also handles plain calldata-carrying batcher txs post-ecotone, so no behaviour is lost. Delete the now-dead Espresso calldata test and move its shared mockAuthEvents helper into the blob data source test, its only user. Mirrors the celo-kona removal (drop CeloCalldataSource, use upstream CalldataSource).
piersy
added a commit
that referenced
this pull request
Jul 1, 2026
The calldata data source is only reached pre-ecotone: OpenData routes to the blob source whenever ecotone is active (ref.Time >= ecotone_time). Every Celo chain sets ecotone_time = 0, and Espresso (espresso_time) activates strictly later, so the Espresso event-based batch-authentication branch that #457 folded into DataFromEVMTransactions could never fire on a real chain. Restore the calldata data source to its pre-Espresso form, byte-identical to before #457. #457 split isValidBatchTx into a type/To-only check plus isAuthorizedBatchSender to serve the blob source's event-based path, which rippled a needless change into the calldata source. Reinstate the original 5-arg isValidBatchTx (composed from isBatchTxToInbox + isAuthorizedBatchSender) so the calldata source is untouched, and point the blob source at isBatchTxToInbox. The blob source keeps Espresso event-based authentication and also handles plain calldata-carrying batcher txs post-ecotone, so no behaviour is lost. Delete the now-dead Espresso calldata test and move its shared mockAuthEvents helper into the blob data source test, its only user. Mirrors the celo-kona removal (drop CeloCalldataSource, use upstream CalldataSource).
piersy
added a commit
that referenced
this pull request
Jul 2, 2026
The calldata data source is only reached pre-ecotone: OpenData routes to the blob source whenever ecotone is active (ref.Time >= ecotone_time). Every Celo chain sets ecotone_time = 0, and Espresso (espresso_time) activates strictly later, so the Espresso event-based batch-authentication branch that #457 folded into DataFromEVMTransactions could never fire on a real chain. Restore the calldata data source to its pre-Espresso form. calldata_source.go is byte-identical to before #457; #457 had split isValidBatchTx into a type/To-only check plus isAuthorizedBatchSender to serve the blob source's event-based path, which rippled a needless change into the calldata source. Reinstate the original 5-arg isValidBatchTx (composed from isBatchTxToInbox + isAuthorizedBatchSender) and point the blob source at isBatchTxToInbox. calldata_source_test.go is also restored except for one line: DataSourceConfig gained fields (rollupCfg, batchAuthCaches), so its literal must use named fields instead of positional. The blob source keeps Espresso event-based authentication and also handles plain calldata-carrying batcher txs post-ecotone, so no behaviour is lost. Delete the now-dead Espresso calldata test and move its shared mockAuthEvents helper into the blob data source test, its only user. Mirrors the celo-kona removal (drop CeloCalldataSource, use upstream CalldataSource).
piersy
pushed a commit
to celo-org/celo-kona
that referenced
this pull request
Jul 2, 2026
Rust counterpart of celo-org/optimism#457. Adds an Espresso-aware L1 derivation path in which batches are authorized by on-chain `BatchInfoAuthenticated` events rather than by batcher-transaction sender. Pre-Espresso behaviour is byte-for-byte identical to upstream OP Stack; the event path activates only once Espresso is active for the L1 origin time. New crate `celo-derive` (crates/kona/derive): - batch_auth.rs: the only genuinely new logic. Walks a fixed lookback window of L1 blocks, collects `BatchInfoAuthenticated` events into a commitment -> caller map, and gates each batch by its blob/calldata commitment hash. Receipts and headers are cached in reorg-safe, block-hash-keyed LRUs so consecutive origins refetch only one block. - blobs.rs / blob_data.rs / ethereum.rs: CeloBlobSource, CeloBlobData and CeloEthereumDataSource are duplicated from kona because the upstream BlobData fields and its decode/fill helpers are pub(crate) and celo-kona wraps kona rather than patching it. The batch-auth branch is folded into the duplicated blob source; the calldata path delegates to upstream. Config plumbing: - genesis (rollup.rs): add Espresso settings to CeloRollupConfig with flattened serialization; reject an espresso_time scheduled before ecotone_time. - proof (boot.rs) / bin/host / bin/client: thread the Espresso config through the rollup-config preimage and boot so host and client agree, and bake activation for the Celo registry chains (Chaos). Provider-polymorphic error handling is preserved: fetch failures collapse via each provider's Into<PipelineErrorKind>, so the lookback walk resets under the online provider and stays critical under the fault-proof oracle, matching the existing block/blob paths.
palango
added a commit
that referenced
this pull request
Jul 3, 2026
…ime >= ecotone_time (#466) * refactor(derive): drop dead Espresso calldata authentication path The calldata data source is only reached pre-ecotone: OpenData routes to the blob source whenever ecotone is active (ref.Time >= ecotone_time). Every Celo chain sets ecotone_time = 0, and Espresso (espresso_time) activates strictly later, so the Espresso event-based batch-authentication branch that #457 folded into DataFromEVMTransactions could never fire on a real chain. Restore the calldata data source to its pre-Espresso form. calldata_source.go is byte-identical to before #457; #457 had split isValidBatchTx into a type/To-only check plus isAuthorizedBatchSender to serve the blob source's event-based path, which rippled a needless change into the calldata source. Reinstate the original 5-arg isValidBatchTx (composed from isBatchTxToInbox + isAuthorizedBatchSender) and point the blob source at isBatchTxToInbox. calldata_source_test.go is also restored except for one line: DataSourceConfig gained fields (rollupCfg, batchAuthCaches), so its literal must use named fields instead of positional. The blob source keeps Espresso event-based authentication and also handles plain calldata-carrying batcher txs post-ecotone, so no behaviour is lost. Delete the now-dead Espresso calldata test and move its shared mockAuthEvents helper into the blob data source test, its only user. Mirrors the celo-kona removal (drop CeloCalldataSource, use upstream CalldataSource). * feat(rollup): reject espresso_time scheduled before ecotone_time Dropping the Espresso-aware calldata source made "espresso_time >= ecotone_time" a silent correctness invariant: Espresso event-based authentication now lives only on the post-ecotone blob path, so a block in the [espresso_time, ecotone_time) window would route to the pre-ecotone calldata source and silently fall back to sender-based authorization. Guard the invariant in Config.Check(): error with ErrEspressoBeforeEcotone when espresso_time is set but ecotone_time is unset or scheduled later than espresso_time. Every real Celo chain sets ecotone_time = 0, so this only fires on a misconfiguration -- but now it fails fast instead of corrupting derivation. Mirrors the celo-kona validate_espresso guard (EspressoBeforeEcotone). * test(derive): restore multi-tx event-auth coverage; review cleanups - add mixed-block and multi-commitment subtests to TestDataAndHashesFromTxsEventAuth, restoring per-tx commitment matching coverage lost with espresso_calldata_source_test.go - drop stale comment reference to the deleted TestDataFromEVMTransactionsForkBoundary - reuse u64ptr instead of a local ptr closure in TestConfig_Check_EspressoBeforeEcotone --------- Co-authored-by: Paul Lange <palango@gmx.de>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces the op-node changes of Espresso integration: batch authentication and caff node.
Batch authentication: derivation pipeline now reads
BatchAuthenticatedevents emitted by theBatchAuthenticatorcontract introduced by #443, gated byEspressoTimehardfork timestamp.The PR includes unit tests for the batch-authenticator event scanner, Espresso batch RLP round-trip, and the new event-based authorization path across the calldata, blob, and altDA data sources. e2e testing involving actual Espresso instance is not part of this PR because it requires batcher changes as well.
This is #445, re-hosted from an in-repo branch (now properly stacked on #455).