Skip to content

Espresso 3b: TEE batcher (re-hosted)#459

Draft
QuentinI wants to merge 7 commits into
espresso/batcher-fallbackfrom
espresso/batcher
Draft

Espresso 3b: TEE batcher (re-hosted)#459
QuentinI wants to merge 7 commits into
espresso/batcher-fallbackfrom
espresso/batcher

Conversation

@QuentinI

@QuentinI QuentinI commented Jun 17, 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.

This is #447, re-hosted from an in-repo branch (now properly stacked).

return err
}
bs.TxManager = txManager
if err := bs.initChainSigner(); err != nil {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Mirrored from #447 (unresolved review thread) to keep tracking it here after the re-host. Original location: op-batcher/batcher/service.go:390.

Original transcript:


@chatgpt-codex-connector — 2026-05-26:

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 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment thread espresso/cli.go
}

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Mirrored from #447 (unresolved review thread) to keep tracking it here after the re-host. Original location: espresso/cli.go:200.

Original transcript:


@chatgpt-codex-connector — 2026-05-26:

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 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

batcherAddr := l.Txmgr.From()

isActive := (activeIsEspresso && l.Config.Espresso.Enabled) ||
(!activeIsEspresso && !l.Config.Espresso.Enabled)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Mirrored from #447 (unresolved review thread) to keep tracking it here after the re-host. Original location: op-batcher/batcher/espresso_active.go:52.

Original transcript:


@chatgpt-codex-connector — 2026-05-26:

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 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment thread op-batcher/batcher/espresso.go Outdated
// 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
Collaborator Author

Choose a reason for hiding this comment

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

Mirrored from #447 (unresolved review thread) to keep tracking it here after the re-host. Original location: op-batcher/batcher/espresso.go:1344.

Original transcript:


@jcortejoso — 2026-05-29:

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

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

@QuentinI QuentinI force-pushed the espresso/batcher-fallback branch from f840eee to 12b46a6 Compare June 17, 2026 16:22
@QuentinI QuentinI force-pushed the espresso/batcher-fallback branch from 12b46a6 to f8480f8 Compare June 17, 2026 16:27
@QuentinI QuentinI force-pushed the espresso/batcher-fallback branch 3 times, most recently from 9330de1 to 1616378 Compare June 18, 2026 14:25
QuentinI and others added 7 commits June 18, 2026 18:38
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>
The TEE batcher's Espresso submission path called Txmgr.Send directly for both
the authenticateBatchInfo tx and the batch inbox tx, and ran inside authGroup,
so it bypassed MaxPendingTransactions, assigned nonces nondeterministically
(violating Holocene's in-order L1 inclusion requirement), and never checked
whether the auth tx reverted — a reverted authenticateBatchInfo emits no event,
so the verifier would silently drop the batch.

Submit both txs through the ordered queue.Send path on the publishing-loop
goroutine (auth first, batch second) so the auth tx takes the lower nonce and is
mined first, and both stay under MaxPendingTransactions. A watcher goroutine
(tracked by authGroup) collects both receipts, fails the pair if the auth tx
reverted, runs the lookback-window check, and emits a single synthetic receipt.

This is the same fix already applied to the fallback path; extract the shared
submission + receipt-watching flow into submitAuthenticatedBatch /
watchAuthReceipts so both paths reuse it and differ only in how the
authenticateBatchInfo calldata is built (TEE-attested signature vs empty
signature). Rename fallback_auth.go to espresso_auth.go to reflect the shared
scope, and restructure the tests: one suite drives the shared flow directly,
plus per-path tests asserting the distinguishing auth calldata (empty sig vs a
recoverable EIP-712 signature).

Co-authored-by: OpenCode <noreply@opencode.ai>
Port TestBatchRoundtrip from the integration branch and fold it into
espresso_batch_test.go. It is the only test covering ToEspressoTransaction
and the batcher->derivation serialization path; it asserts the decoded batch
matches the original and that the recovered signer is the batcher.

Also drop the decodedBlock.ExecutionWitness() comparison in
TestEspressoBatchConversion: that method does not exist on the op-geth
types.Block pinned in the rebase-18 base, so go vet of the derive_test
package failed to build. EspressoBatch/ToBlock carries no execution witness.

Co-authored-by: OpenCode <noreply@opencode.ai>
@QuentinI QuentinI force-pushed the espresso/batcher-fallback branch from 1616378 to 25a6c63 Compare June 18, 2026 16:40
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.

1 participant