Skip to content

Espresso 2: derivation pipeline#445

Draft
QuentinI wants to merge 33 commits into
celo-org:celo-rebase-17from
EspressoSystems:ag/derivation-pipeline
Draft

Espresso 2: derivation pipeline#445
QuentinI wants to merge 33 commits into
celo-org:celo-rebase-17from
EspressoSystems:ag/derivation-pipeline

Conversation

@QuentinI

Copy link
Copy Markdown

This PR introduces the op-node changes of Espresso integration: batch authentication and caff node. As I can't push branches to this PR I can't create a clean stacked PR; relevant changes over #443 are in commit 08a9056

Batch authentication: derivation pipeline now reads BatchAuthenticated events emitted by the BatchAuthenticator contract introduced by #443, gated by EspressoEnforcementTime hardfork timestamp.

Caff node: op-node gains Caff mode support. When a node is configured as a Caff node (CaffNodeConfig.Enabled) and the parent L2 block has reached the configured caffeination height (and the Espresso enforcement hardfork is active), derivation pulls batches directly from the Espresso sequencer via Espresso Streamer

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.

QuentinI and others added 5 commits May 8, 2026 02:50
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>
Comment thread op-service/sources/eth_client.go Outdated
Comment thread op-node/rollup/types.go Outdated
Comment thread espresso/cli.go Outdated
QuentinI and others added 5 commits May 20, 2026 21:14
- 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>
@piersy

piersy commented May 21, 2026

Copy link
Copy Markdown

Hi @QuentinI, wondering why there are these "CaffNode" changes:

Caff node: op-node gains Caff mode support

I thought this was being handled by the espresso-rollup-node-proxy?

@QuentinI

Copy link
Copy Markdown
Author

@piersy you are right, I was out of date on how the proxy works. I'll strip the Caff node out of this PR, we are not going to need it.

QuentinI and others added 5 commits May 25, 2026 14:11
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 ethereum-optimism#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>
@QuentinI QuentinI force-pushed the ag/derivation-pipeline branch from 08a9056 to 51888e5 Compare May 25, 2026 14:05
@QuentinI

Copy link
Copy Markdown
Author

Rebased and dropped caff-node changes.

QuentinI added a commit to EspressoSystems/optimism-espresso-integration that referenced this pull request May 25, 2026
Matches upstream Optimism hardfork naming convention (RegolithTime,
EcotoneTime, IsthmusTime, ...). All hardforks enforce a new set of
rules, so the "Enforcement" qualifier was redundant.

Renames:
  EspressoEnforcementTime    -> EspressoTime    (rollup.Config field)
  IsEspressoEnforcement      -> IsEspresso      (rollup.Config method)
  espressoEnforcementTime    -> espressoTime    (DataSourceConfig field)
  isEspressoEnforcement      -> isEspresso      (DataSourceConfig method)
  espresso_enforcement_time  -> espresso_time   (JSON tag, forEachFork log key)
  "Espresso Enforcement"     -> "Espresso"      (forEachFork display name)

Also rewords prose docstrings: "EspressoEnforcement" -> "Espresso",
"Pre/Post-EspressoEnforcement" -> "Pre/Post-Espresso".

Addresses PR feedback: celo-org#445 (comment)

Co-authored-by: OpenCode <noreply@opencode.ai>
@jcortejoso

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d55b56e12a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/contracts-bedrock/scripts/deploy/DeployEspresso.s.sol Outdated
Comment thread op-node/rollup/types.go Outdated
Comment thread op-node/rollup/types.go

// BatchAuthenticatorAddress is the L1 address of the BatchAuthenticator contract whose
// BatchInfoAuthenticated(bytes32) events the derivation pipeline scans post-Espresso.
BatchAuthenticatorAddress common.Address `json:"batch_authenticator_address,omitempty,omitzero"`

@palango palango May 27, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EspressoTime makes BatchInfoAuthenticated events mandatory post-fork, but the rollup config does not appear to require a non-zero BatchAuthenticatorAddress when the fork is configured.

There should be a check added that checks BatchAuthenticatorAddress is set when EspressoTime is non-nil.

Then test should be added:

  • A rollup config validation test that fails when EspressoTime != nil and BatchAuthenticatorAddress == common.Address{}.
  • A rollup config validation test that passes when both EspressoTime and a non-zero BatchAuthenticatorAddress are configured.

@QuentinI QuentinI Jun 2, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Implemented in 1c566cd

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide not to hardcode BatchAuthLookbackWindow then we probably would like to include that in the check as well.

@palango

palango commented May 27, 2026

Copy link
Copy Markdown

@QuentinI Will the derivation changes for kona also be submitted in this PR or in a stacked one?

@QuentinI

Copy link
Copy Markdown
Author

@palango kona counterpart available at #449

Comment thread op-node/rollup/derive/data_source.go Outdated
QuentinI and others added 4 commits May 29, 2026 16:45
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>
…trictions entries

src/L1/StandardValidator.sol was renamed to OPContractsManagerStandardValidator.sol
upstream in ethereum-optimism#16237; the old path was a rebase artifact with no corresponding file.

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>
Comment thread espresso/constants.go Outdated
Comment thread op-node/rollup/derive/batch_authenticator.go Outdated
Comment on lines +3 to +13
// IsEspresso returns true if the Espresso upgrade is active at or past the
// given timestamp. EspressoTime is conceptually an L2-timestamp fork activation
// time, but the derivation pipeline calls this with the L1 origin time of the
// enclosing L1 block (mirroring upstream's ecotoneTime treatment), so the fork
// is effectively gated per L2 epoch. When active, the derivation pipeline runs
// all Espresso-specific semantics (event-based batch authentication via the
// BatchAuthenticator contract). When inactive, the pipeline behaves exactly as
// upstream Optimism.
func (c *Config) IsEspresso(timestamp uint64) bool {
return c.EspressoTime != nil && timestamp >= *c.EspressoTime
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary, I can't see this being used anywhere, I can just see the datasource config's isEspresso being used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually I think I'm in favour of just holding the rollup config in the DataSourceConfig, since we are already copying three espresso fields out of it, if we did that then we could use this method and drop the implementation on the datasource.

@piersy piersy Jun 3, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made these changes in this commit 487ec10

I think its a slight improvement since we don't need to duplicate the description of those config fields and it makes the diff in the DataSourceConfig type 1 line instead of multiple. You can cherry pick if you agree.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes total sense! Will cherry-pick it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

QuentinI added a commit to EspressoSystems/celo-kona that referenced this pull request Jun 4, 2026
Add the `celo-derive` crate implementing the Rust (kona) companion to the
op-node Espresso batch-authentication changes (celo-org/optimism#445),
relocated from the vendored-kona PR celo-org/optimism#449 to follow
celo-kona's wrap/duplicate-don't-patch model.

Post-fork, batches are authorized by scanning L1 receipts for
`BatchInfoAuthenticated` events from the configured `BatchAuthenticator`
contract within a lookback window, instead of by transaction sender.
Pre-fork (or when the fork is not yet active) derivation is byte-identical
to upstream OP Stack.

- `CeloEthereumDataSource` / `CeloCalldataSource` / `CeloBlobSource`:
  duplicates of kona's data sources with the auth branch folded in
  (kona's `CalldataSource`/`BlobSource`/`BlobData` internals are
  `pub(crate)` and cannot be wrapped).
- `BatchAuthConfig` bundles the authenticator address, fork time and
  lookback window so the "fork scheduled but no authenticator" state is
  unrepresentable; `CeloRollupConfig::batch_auth_params` validates it and
  hard-errors on misconfiguration (otherwise derivation would silently
  stall at the fork boundary).
- `CeloRollupConfig` gains the Espresso settings (parsed from the same
  rollup.json) and `CeloBootInfo` carries them through boot.
- Point the hokulea git dep at celo-org/hokulea fccc98c (celo-org/hokulea#2),
  which generalizes `EigenDADataSource` over `DataAvailabilityProvider` so
  the eigenda path can wrap `CeloEthereumDataSource`.

Co-authored-by: OpenCode <noreply@opencode.ai>
QuentinI and others added 2 commits June 4, 2026 19:47
Deploy the BatchAuthenticator and TEEVerifier proxies behind the existing
OP Stack ProxyAdmin instead of dedicated ones (celo-org#443).
Both proxies use the deployer as a transient admin to initialize directly,
then changeAdmin to the shared ProxyAdmin (DeployAltDA/DeployFeesDepositor
pattern). Reorder TEE deploy so the Nitro verifier is wired via initialize,
removing the post-init onlyOwner call and ownership-transfer dance. Rename
inputs to espressoOwner/sharedProxyAdmin and drop the teeVerifierProxyAdmin
output.

Co-authored-by: OpenCode <noreply@opencode.ai>
Co-authored-by: OpenCode <noreply@opencode.ai>
QuentinI added a commit to EspressoSystems/optimism-espresso-integration that referenced this pull request Jun 8, 2026
Matches upstream Optimism hardfork naming convention (RegolithTime,
EcotoneTime, IsthmusTime, ...). All hardforks enforce a new set of
rules, so the "Enforcement" qualifier was redundant.

Renames:
  EspressoEnforcementTime    -> EspressoTime    (rollup.Config field)
  IsEspressoEnforcement      -> IsEspresso      (rollup.Config method)
  espressoEnforcementTime    -> espressoTime    (DataSourceConfig field)
  isEspressoEnforcement      -> isEspresso      (DataSourceConfig method)
  espresso_enforcement_time  -> espresso_time   (JSON tag, forEachFork log key)
  "Espresso Enforcement"     -> "Espresso"      (forEachFork display name)

Also rewords prose docstrings: "EspressoEnforcement" -> "Espresso",
"Pre/Post-EspressoEnforcement" -> "Pre/Post-Espresso".

Addresses PR feedback: celo-org#445 (comment)

Co-authored-by: OpenCode <noreply@opencode.ai>
@QuentinI QuentinI force-pushed the ag/derivation-pipeline branch from cfc8950 to 6b21ccb Compare June 8, 2026 13:39
QuentinI and others added 5 commits June 8, 2026 15:41
Adds derivation-pipeline support for the BatchAuthenticator contract
introduced in the previous PR. Stacks on the contracts PR.

Introduces an L2-timestamp hardfork (EspressoEnforcementTime) gating all
post-fork derivation semantics. Pre-fork, derivation behaves exactly as
upstream Optimism: batches are accepted based on the L1 transaction
sender matching the SystemConfig batcher address. Post-fork, batches are
authenticated via BatchInfoAuthenticated(bytes32) events emitted by the
BatchAuthenticator contract, and sender-based authorization is rejected.

Adds CollectAuthenticatedBatches which scans L1 receipts over a
configurable lookback window (default 100 blocks) to build the set of
authenticated batch commitment hashes for each L1 block being derived.
Results are cached in two reorg-safe (block-hash-keyed) LRU caches: one
for receipt-derived event sets, one for L1BlockRef resolution. For
consecutive L1 blocks the lookback windows overlap by ~99 blocks, so
only one new block's receipts need to be fetched on each call.

Adds rollup.Config fields: EspressoEnforcementTime *uint64,
BatchAuthenticatorAddress, BatchAuthLookbackWindow.

Adds unit tests for batch authentication across calldata, blob, and
altda data sources.

Co-authored-by: OpenCode <noreply@opencode.ai>
Matches upstream Optimism hardfork naming convention (RegolithTime,
EcotoneTime, IsthmusTime, ...). All hardforks enforce a new set of
rules, so the "Enforcement" qualifier was redundant.

Renames:
  EspressoEnforcementTime    -> EspressoTime    (rollup.Config field)
  IsEspressoEnforcement      -> IsEspresso      (rollup.Config method)
  espressoEnforcementTime    -> espressoTime    (DataSourceConfig field)
  isEspressoEnforcement      -> isEspresso      (DataSourceConfig method)
  espresso_enforcement_time  -> espresso_time   (JSON tag, forEachFork log key)
  "Espresso Enforcement"     -> "Espresso"      (forEachFork display name)

Also rewords prose docstrings: "EspressoEnforcement" -> "Espresso",
"Pre/Post-EspressoEnforcement" -> "Pre/Post-Espresso".

Addresses PR feedback: celo-org#445 (comment)

Co-authored-by: OpenCode <noreply@opencode.ai>
Co-authored-by: OpenCode <noreply@opencode.ai>
EspressoTime is a conceptually L2-timestamp fork activation time, but the
derivation pipeline gates on it by comparing against the L1 origin time of
the enclosing L1 block. Update the doc comments to reflect this, consistent
with the existing blob_data_source.go/calldata_source.go comments.

Co-authored-by: OpenCode <noreply@opencode.ai>
Adapts the derivation pipeline to PR ethereum-optimism#443's updated BatchAuthenticator
event and enforces that a batch is submitted by the same address that
authenticated it.

The contract event changed from BatchInfoAuthenticated(bytes32 indexed
commitment) to BatchInfoAuthenticated(bytes32 commitment, address indexed
caller): the signature hash (Topics[0]) changes, the commitment moves into
the unindexed log data, and the caller becomes the indexed Topics[1]. The
event scanner is updated accordingly (new ABI string, commitment read from
Data[:32], with a length guard).

CollectAuthenticatedBatches and collectAuthEventsFromReceipts now return
map[commitment]caller instead of a commitment set. Post-fork,
isBatchTxAuthorized recovers the batch transaction's L1 sender and accepts
the batch only if it equals the caller that emitted the auth event. This
binds each batch to the address that authenticated it, so a batch
authenticated by one batcher cannot be submitted by another. When the same
commitment is authenticated in more than one block within the lookback
window, the newest event's caller is retained.

Removes FindBatchAuthEvent and its test: it had no production caller (the
pipeline uses CollectAuthenticatedBatches) and, lacking a caller check,
diverged from the enforced sender-equals-caller semantics.

Updates the data-source tests to set the auth event caller to the batch
tx sender, and adds cases covering acceptance for a non-batcher sender
matching its caller, rejection when the sender differs from the caller,
and newest-caller-wins on duplicate authentication.

Co-authored-by: OpenCode <noreply@opencode.ai>
@QuentinI QuentinI force-pushed the ag/derivation-pipeline branch from 6b21ccb to 288a1ec Compare June 8, 2026 13:42
piersy and others added 4 commits June 8, 2026 16:05
…stances

Remove package-level singleton LRU caches and sync.Once from
batch_authenticator.go. Instead, BatchAuthCaches are constructed in
NewDataSourceFactory and threaded through DataSourceConfig to
CollectAuthenticatedBatches. This eliminates the global mutable state,
the cache-size-locked-by-first-caller problem, and the data race in
resetBatchAuthCaches. Tests now construct their own caches and are safe
for t.Parallel().
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>
@QuentinI QuentinI force-pushed the ag/derivation-pipeline branch from d6ae34f to 47d6742 Compare June 8, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants