Skip to content

Espresso 3b: TEE batcher#447

Closed
QuentinI wants to merge 49 commits into
celo-org:celo-rebase-18from
EspressoSystems:ag/batcher
Closed

Espresso 3b: TEE batcher#447
QuentinI wants to merge 49 commits into
celo-org:celo-rebase-18from
EspressoSystems:ag/batcher

Conversation

@QuentinI

@QuentinI QuentinI commented May 26, 2026

Copy link
Copy Markdown
Collaborator

Based on #448

Pulls in the Espresso/TEE batcher .

  • In op-node: adds EspressoBatch type and marshaling logic for it. This is the datastructure that ends up posted to Espresso.
  • In espresso package: adds the CLI flags and interfaces for the streamer.
  • In the batcher: adds NSM helper in op-batcher/enclave/attestation.go and modifies the driver to add an Espresso path. Bulk of the changes is in espresso_-prefixed files.

Relevant commits are
fb72ec5
07ab780
72657fa
f366d5c
1e1ccb1

@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: 8c835d2640

ℹ️ 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".

Comment on lines +379 to +380
if err := bs.initChainSigner(); err != nil {
return err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Badge Guard ChainSigner initialization behind Espresso mode

initTxManager now always calls initChainSigner, but bs.TxManager is created as *txmgr.SimpleTxManager, which does not implement opcrypto.ChainSigner; this makes service startup fail with "tx manager does not implement ChainSigner" even when --espresso.enabled=false. Because this path runs during normal batcher boot, it becomes a universal startup regression rather than an Espresso-only failure.

Useful? React with 👍 / 👎.

Comment thread espresso/cli.go
}

func (c CLIConfig) Check() error {
if c.Enabled {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate fallback-auth lead time outside Espresso-enabled block

FallbackAuthLeadTime is validated only when c.Enabled is true, but fallback mode (--espresso.enabled=false) still uses this field in isFallbackAuthRequired; a negative duration therefore bypasses validation and is later cast to uint64, producing a huge lead that can force premature/always-on fallback auth gating. This creates hard-to-diagnose behavior for fallback batchers with a mis-set duration flag.

Useful? React with 👍 / 👎.

Comment on lines +51 to +52
isActive := (activeIsEspresso && l.Config.Espresso.Enabled) ||
(!activeIsEspresso && !l.Config.Espresso.Enabled)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check configured signer address in active-batcher decision

isBatcherActive determines activity solely from activeIsEspresso vs EspressoEnabled, without verifying that this node’s Txmgr.From() matches the currently authorized batcher address for that mode. In fallback mode, a node with the wrong sender key will still consider itself active and keep attempting publishes that revert at BatchAuthenticator, causing avoidable failure loops and no progress if this is the only running batcher.

Useful? React with 👍 / 👎.

@QuentinI QuentinI marked this pull request as draft May 26, 2026 16:49
@QuentinI QuentinI changed the title Espresso 3: Batcher Espresso 3b: TEE batcher May 27, 2026
// sendTxWithEspresso uses the txmgr queue to send the given transaction candidate after setting
// its gaslimit. It will block if the txmgr queue has reached its MaxPendingTransactions limit.
func (l *BatchSubmitter) sendTxWithEspresso(txdata txData, isCancel bool, candidate *txmgr.TxCandidate, queue TxSender[txRef], receiptsCh chan txmgr.TxReceipt[txRef]) {
transactionReference := txRef{id: txdata.ID(), isCancel: isCancel, isBlob: txdata.daType == DaTypeBlob}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would be possible to include here daType and tx size?:

type txRef struct {
    id       txID
    isCancel bool
    isBlob   bool
    daType   DaType
    size     int
}

QuentinI and others added 23 commits June 12, 2026 11:38
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 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>
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>
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>
piersy and others added 12 commits June 12, 2026 11:40
The Go script host's getArtifact translated a fully-qualified Foundry
name like "src/universal/Proxy.sol:Proxy" into the artifact path
"src/universal/Proxy.sol/Proxy.json", which does not exist because the
artifacts FS is keyed by the source-file basename. Reduce any
directory-qualified path to its basename before ReadArtifact so that
both "File.sol:Contract" and "path/to/File.sol:Contract" resolve to the
same artifact.

This unblocks the deploy scripts that use
getCode("src/universal/Proxy.sol:Proxy") to disambiguate from the
OpenZeppelin v5 proxy/Proxy.sol artifact, fixing TestNewDeployAltDAScript,
TestNewDeployImplementationsScript, TestNewDeploySuperchainScript and the
op-e2e proofs actions that hit the same DeploySuperchain code path.

Co-authored-by: OpenCode <noreply@opencode.ai>
Resolve the 16 findings flagged by the contracts-bedrock-checks-fast
semgrep job:

- sol-safety-use-deployutils-getcode: replace vm.getCode(...) with
  DeployUtils.getCode(...) in DeployBatchAuthenticator, DeployEspresso,
  DeployPeriphery, BatchAuthenticator.t, and FeesDepositor.t (add the
  DeployUtils import where missing).
- sol-style-use-abi-encodecall: add a justified nosemgrep on the
  EspressoTEEVerifier initialize encoding in DeployEspresso; encodeCall
  would pull the EspressoTEEVerifier impl closure into OP's compile group,
  which deploying from the submodule artifact is meant to avoid.
- sol-style-input-arg-fmt / sol-style-return-arg-fmt: rename interface and
  contract args (index -> _index, l1Block -> _l1Block) and name returns
  (batcher_, fromBlock_) on BatchAuthenticator and IBatchAuthenticator.

forge build, the semgrep scan (0 blocking findings), and the
BatchAuthenticator/FeesDepositor test suites all pass.

Co-authored-by: OpenCode <noreply@opencode.ai>
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>
…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 changed the base branch from celo-rebase-17 to celo-rebase-18 June 12, 2026 11:04
piersy and others added 12 commits June 16, 2026 15:11
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>
Regenerated against PR ethereum-optimism#443's BatchAuthenticator.sol via forge build +
abigen. Includes the new history-based API (espressoBatcherAt,
espressoBatcherAtBlock, espressoBatcherHistoryLength, setEspressoBatcher)
and the EspressoBatcherUpdated(address,address,uint64) event with the
fromBlock parameter; drops the removed paused() function.

Consumed by the fallback batcher (next commit) to read activeIsEspresso
and pack authenticateBatchInfo calldata. The TEE batcher in a follow-up
PR will use the same binding.

Co-authored-by: OpenCode <noreply@opencode.ai>
Add the fallback (non-TEE) batcher's BatchAuthenticator integration:

- op-batcher/batcher/fallback_auth.go: sendTxWithFallbackAuth path that
  posts authenticateBatchInfo before the batch tx, with a deadline check
  against the batch's L1 inclusion window. Computes the batch commitment
  hash from either calldata or concatenated blob versioned hashes.
- op-batcher/batcher/espresso_active.go: hasBatchAuthenticator (does
  this rollup use BatchAuthenticator at all?) and isFallbackAuthRequired
  (gates fallback authentication on Config.IsEspresso(tip.Time + lead)).
  The Espresso hardfork predicate is consulted with the configured
  FallbackAuthLeadTime added to the L1 tip, so the batcher starts
  authenticating slightly before the verifier requires it. This absorbs
  worst-case L1 inclusion delay between the batcher's decision time
  (L1 tip) and the verifier's evaluation time (containing L1 block).
- op-batcher/batcher/espresso_driver.go: the authGroup bookkeeping
  (initAuthGroup, waitForAuthGroup, fallbackAuthGroupLimit) and the
  dispatchAuthenticatedSendTx fan-out used by driver.go sendTx.

Small wiring edits to upstream files:

- op-batcher/flags/flags.go: register --espresso.fallback-auth-lead-time
  (default 5m).
- op-batcher/batcher/config.go: thread the FallbackAuthLeadTime through
  CLIConfig.
- op-batcher/batcher/service.go: BatcherConfig.FallbackAuthLeadTime
  field, propagated from CLIConfig in initFromCLIConfig.
- op-batcher/batcher/driver.go: extend L1Client to embed
  bind.ContractBackend (required by the BatchAuthenticator binding), add
  authGroup field to BatchSubmitter, call initAuthGroup in
  NewBatchSubmitter, call dispatchAuthenticatedSendTx in sendTx, call
  waitForAuthGroup in publishingLoop's shutdown drain.
- op-batcher/batcher/driver_test.go: embed bind.ContractBackend in
  fakeL1Client so the AltDA tests still satisfy L1Client.

The fallback batcher does nothing when the rollup config has no
BatchAuthenticator address, and it falls through to the upstream
queue.Send path pre-EspressoTime. Cancel transactions always take the
upstream path. No new external dependencies are added; the only third-
party Go modules needed are already in PR ethereum-optimism#445.

The TEE batcher is a separate PR stacked on top.

Co-authored-by: OpenCode <noreply@opencode.ai>
Bring in op-service/crypto/espresso.go (ChainSigner interface unifying
SignTransaction and arbitrary-data signing) and op-service/signer/espresso.go
(SignerClient.Sign wrapper around eth_sign).

Required by the Espresso batcher to sign batch-authentication payloads with
either a remote signer or a local private key, in addition to the existing
transaction-signing path.

Co-authored-by: OpenCode <noreply@opencode.ai>
Introduce op-node/rollup/derive/espresso_batch.go defining EspressoBatch
(a SingularBatch with block number, L1 info deposit transaction, and
signer address attached), along with BlockToEspressoBatch and the
unmarshaler used by the streamer.

Also pulls in the github.com/EspressoSystems/espresso-network/sdks/go
dependency, which provides the Espresso transaction and namespace types.

Consumed by the Espresso batcher (next commits) to convert L2 blocks into
batches submitted to Espresso, and to round-trip those batches back through
the streamer.

Co-authored-by: OpenCode <noreply@opencode.ai>
Bring in the parts of the espresso/ shared package that are TEE-only:

- espresso/cli.go: full CLI flag set for --espresso.enabled, query
  service URLs, light-client/L1 endpoints, batch-authenticator address,
  receipt-verification tuning, namespace/origin-height parameters used
  to construct the espresso-streamer. (The --espresso.fallback-auth-lead-time
  flag lives in op-batcher/flags/flags.go and was added by the fallback PR.)
- espresso/interface.go: EspressoStreamer[B] interface that wraps
  github.com/EspressoSystems/espresso-streamers/op.BatchStreamer.
- espresso/ethclient.go: AdaptL1BlockRefClient adapter (used by cli.go to
  construct the streamer) and FetchEspressoBatcherAddress helper.

Also adds the EspressoSystems/espresso-network/sdks/go and
EspressoSystems/espresso-streamers Go module dependencies.

The regenerated BatchAuthenticator bindings already live in the
fallback PR's espresso/bindings/.

Co-authored-by: OpenCode <noreply@opencode.ai>
Bring in op-batcher/enclave/attestation.go: a thin wrapper around the
hf/nsm library that obtains an AWS Nitro NSM attestation document over
a given public key. Used by the Espresso batcher (next commit) to attach
a TEE attestation to its registration with the BatchAuthenticator.

Adds the github.com/hf/nsm dependency. Builds on all platforms; NSM
device access is only attempted at runtime when invoked from inside
a Nitro enclave.

Co-authored-by: OpenCode <noreply@opencode.ai>
Add the Espresso TEE batcher write-path on top of the fallback batcher:

- op-batcher/batcher/espresso.go: Espresso submission loop (peeks the
  channel manager, converts each L2 block to an EspressoBatch, submits
  it to Espresso, waits for inclusion, and then posts the batch txs to
  L1 with TEE-attested BatchAuthenticator.authenticateBatchInfo calls).
- op-batcher/batcher/espresso_service.go: EspressoBatcherConfig,
  initEspresso (Espresso client / light-client construction, optional
  TEE attestation gathering), and the initChainSigner hook that wraps
  the txmgr into a opcrypto.ChainSigner.
- op-batcher/batcher/espresso_helpers_test.go and
  espresso_transaction_submitter_test.go: unit tests for the helpers and
  the TEE transaction submitter.

Extends the existing fallback wiring:

- op-batcher/batcher/espresso_driver.go: adds EspressoDriverSetup
  fields (Client/LightClient/ChainSigner/SequencerAddress/Attestation),
  batcherL1Adapter, setupEspressoStreamer, startEspressoLoops,
  resetEspressoStreamer; extends dispatchAuthenticatedSendTx with the
  TEE branch (always authenticates when Espresso.Enabled).
- op-batcher/batcher/espresso_active.go: adds isBatcherActive
  (queries BatchAuthenticator.activeIsEspresso to gate publishing
  against this batcher's role).
- op-batcher/batcher/driver.go: extends DriverSetup with the Espresso
  EspressoDriverSetup field; adds espressoSubmitter / espressoStreamer /
  teeVerifierAddress / degradedLog fields on BatchSubmitter; calls
  setupEspressoStreamer in NewBatchSubmitter; branches
  StartBatchSubmitting on Espresso.Enabled to call startEspressoLoops;
  calls resetEspressoStreamer in clearState.
- op-batcher/batcher/service.go: BatcherConfig.Espresso field;
  EspressoClient / EspressoLightClient / ChainSigner / Attestation
  runtime fields; initEspresso / initChainSigner /
  applyEspressoDriverSetup call-outs.
- op-batcher/batcher/config.go: thread Espresso espresso.CLIConfig
  through CLIConfig.
- op-batcher/flags/flags.go: register espresso.CLIFlags (TEE-only
  flags; the --espresso.fallback-auth-lead-time flag added by the
  fallback PR continues to live in op-batcher/flags/flags.go).

Also adds op-service/log/repeat_state.go (RepeatStateLogger) and its
test, used by the Espresso submission loop's tick-driven warnings.
A safeTestRecorder helper is inlined into the test to avoid pulling
in the unrelated debouncer.

Adds the github.com/hf/nitrite dependency (transitively required by
hf/nsm for attestation document parsing).

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

Copy link
Copy Markdown
Collaborator Author

Superseded by #459, re-hosted on an in-repo branch (now properly stacked). Closing in favor of that.

@QuentinI QuentinI closed this Jun 17, 2026
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.

5 participants