Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
883ab3c
op-service: add ChainSigner interface and eth_sign helper
QuentinI May 26, 2026
a19af3b
op-node: add EspressoBatch type and converters
QuentinI May 26, 2026
81524ef
espresso: add TEE batcher CLI flags, streamer interface, and L1 adapter
QuentinI May 27, 2026
e4bf6a0
op-batcher: add Nitro NSM attestation helper
QuentinI May 26, 2026
737e1d1
op-batcher: integrate TEE batcher and Espresso submission loop
QuentinI May 27, 2026
bc6b2a6
op-batcher: route TEE auth through ordered tx queue
QuentinI Jun 18, 2026
d4def8f
op-node: add EspressoBatch roundtrip test, fix derive_test vet failure
QuentinI Jun 18, 2026
78c1626
verify batches while converting to a block
lukeiannucci Jul 6, 2026
37ae33a
start throttling loop after espresso/non espresso loops
lukeiannucci Jul 6, 2026
0c60c52
make use of unsafeBytesUpdated
lukeiannucci Jul 6, 2026
24f048a
initChainSigner fallback for non espresso mode
lukeiannucci Jul 6, 2026
3a59d9f
lint and make use of isBatcherActive
lukeiannucci Jul 7, 2026
35ba27c
request to clear state from BlockLoader
lukeiannucci Jul 7, 2026
1044084
safety check for namespace and nil l1 deposit
lukeiannucci Jul 16, 2026
ce51162
remove duplicate files between espresso streamer and upstream
lukeiannucci Jul 16, 2026
65c7066
address comments
lukeiannucci Jul 16, 2026
18cfce3
remove uneeded comments
lukeiannucci Jul 16, 2026
0d0a1d0
update unsafe bytes every 100 blocks. matching non espresso path
lukeiannucci Jul 16, 2026
94464e8
always set the prev sync status
lukeiannucci Jul 17, 2026
f0dfb44
log at warn level if batcher isnt active
lukeiannucci Jul 20, 2026
6ad7114
check the batcher address before publishing to avoid reverts
lukeiannucci Jul 21, 2026
e041f56
copy espresso.go over from our fork
lukeiannucci Jul 28, 2026
404fb29
proper version of streamer
lukeiannucci Jul 28, 2026
1d6a913
address comment, remove uneeded methods and compile time assertion
lukeiannucci Jul 28, 2026
4ad160f
use sync status interface for streamer
lukeiannucci Jul 31, 2026
4037710
empty commit to trigger CI
philippecamacho Aug 5, 2026
8c1568a
point streamer to v2 (espresso-streamers PR 36) and adapt the batcher
philippecamacho Aug 6, 2026
3f3fb18
Check resolveTEEVerifierAddress first so that the streamer is not lef…
philippecamacho Aug 8, 2026
bdf99e8
Use LocalSafeL2 instead of SafeL2 so that Channel Manager and Streame…
philippecamacho Aug 8, 2026
2740245
Roll back running state on Espresso setup failure so that a failed st…
philippecamacho Aug 8, 2026
b911f25
Anchor the streamer at the local-safe head during construction so tha…
philippecamacho Aug 8, 2026
89194a2
Floor every streamer anchor and sync comparison at the caffeination p…
philippecamacho Aug 8, 2026
eb9ae89
Bound every raw RPC read on the Espresso startup path with the networ…
philippecamacho Aug 8, 2026
b32e0b9
Re-anchor the streamer instead of queueing batches at or below the lo…
philippecamacho Aug 8, 2026
daa789c
Gate startup on the caffeination point being local-safe instead of an…
philippecamacho Aug 8, 2026
b459e07
Retry on zeroed sync-status fields in nextBlockRange so a transient z…
philippecamacho Aug 8, 2026
3281b62
Skip the drain tick when the sync status is out of sync so a zeroed L…
philippecamacho Aug 8, 2026
ee6cc80
Fetch the streamer re-anchor target before clearing state so a transi…
philippecamacho Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,640 changes: 2,640 additions & 0 deletions espresso/bindings/system_config.go

Large diffs are not rendered by default.

258 changes: 258 additions & 0 deletions espresso/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
package espresso

import (
"crypto/ecdsa"
"fmt"
"strings"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"

"github.com/urfave/cli/v2"
)

// espressoFlags returns the flag names for espresso
func espressoFlags(v string) string {
return "espresso." + v
}

func espressoEnvs(envprefix, v string) []string {
return []string{envprefix + "_ESPRESSO_" + v}
}

// Default values for batch submission receipt verification tuning.
// Defined here so that both the CLI flag defaults and the batcher logic
// can reference a single source of truth.
//
// Note: DefaultBatchAuthLookbackWindow lives in constants.go (mips64-clean
// build target shared with the derivation pipeline).
const (
DefaultVerifyReceiptMaxBlocks uint64 = 5
DefaultVerifyReceiptSafetyTimeout time.Duration = 5 * time.Minute
DefaultVerifyReceiptRetryDelay time.Duration = 100 * time.Millisecond
DefaultMaxInFlightRequestsToEspresso = 128
)

var (
EnabledFlagName = espressoFlags("enabled")
PollIntervalFlagName = espressoFlags("poll-interval")
QueryServiceUrlsFlagName = espressoFlags("urls")
LightClientAddrFlagName = espressoFlags("light-client-addr")
L1UrlFlagName = espressoFlags("l1-url")
TestingBatcherPrivateKeyFlagName = espressoFlags("testing-batcher-private-key")
CaffeinationHeightEspresso = espressoFlags("origin-height-espresso")
CaffeinationHeightL2 = espressoFlags("origin-height-l2")
NamespaceFlagName = espressoFlags("namespace")
RollupL1UrlFlagName = espressoFlags("rollup-l1-url")
AttestationServiceFlagName = espressoFlags("espresso-attestation-service")
BatchAuthenticatorAddrFlagName = espressoFlags("batch-authenticator-addr")
VerifyReceiptMaxBlocksFlagName = espressoFlags("verify-receipt-max-blocks")
VerifyReceiptSafetyTimeoutFlagName = espressoFlags("verify-receipt-safety-timeout")
VerifyReceiptRetryDelayFlagName = espressoFlags("verify-receipt-retry-delay")
)

func CLIFlags(envPrefix string, category string) []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: EnabledFlagName,
Usage: "Enable Espresso mode",
Value: false,
EnvVars: espressoEnvs(envPrefix, "ENABLED"),
Category: category,
},
&cli.DurationFlag{
Name: PollIntervalFlagName,
Usage: "Polling interval for Espresso queries",
Value: 250 * time.Millisecond,
EnvVars: espressoEnvs(envPrefix, "POLL_INTERVAL"),
Category: category,
},
&cli.StringSliceFlag{
Name: QueryServiceUrlsFlagName,
Usage: "Comma-separated list of Espresso query service URLs",
EnvVars: espressoEnvs(envPrefix, "URLS"),
Category: category,
},
&cli.StringFlag{
Name: LightClientAddrFlagName,
Usage: "Address of the Espresso light client",
EnvVars: espressoEnvs(envPrefix, "LIGHT_CLIENT_ADDR"),
Category: category,
},
&cli.StringFlag{
Name: L1UrlFlagName,
Usage: "L1 RPC URL Espresso contracts are deployed on",
EnvVars: espressoEnvs(envPrefix, "L1_URL"),
Category: category,
},
&cli.StringFlag{
Name: TestingBatcherPrivateKeyFlagName,
Usage: "Pre-approved batcher ephemeral key (testing only)",
EnvVars: espressoEnvs(envPrefix, "TESTING_BATCHER_PRIVATE_KEY"),
Category: category,
},
&cli.Uint64Flag{
Name: CaffeinationHeightEspresso,
Usage: "Espresso transactions below this height will not be considered",
EnvVars: espressoEnvs(envPrefix, "ORIGIN_HEIGHT_ESPRESSO"),
Category: category,
},
&cli.Uint64Flag{
Name: CaffeinationHeightL2,
Usage: "L2 batch position at which the Espresso streamer starts emitting batches. " +
"Operational parameter for restarting batchers mid-chain. " +
"When zero, the streamer falls back to its internal default. " +
"Independent of the EspressoTime hardfork, which gates derivation semantics.",
Value: 0,
EnvVars: espressoEnvs(envPrefix, "ORIGIN_HEIGHT_L2"),
Category: category,
},
&cli.Uint64Flag{
Name: NamespaceFlagName,
Usage: "Namespace of Espresso transactions",
EnvVars: espressoEnvs(envPrefix, "NAMESPACE"),
Category: category,
},
&cli.StringFlag{
Name: RollupL1UrlFlagName,
Usage: "RPC URL of L1 backing the Rollup we're streaming for",
EnvVars: espressoEnvs(envPrefix, "ROLLUP_L1_URL"),
Category: category,
},
&cli.StringFlag{
Name: AttestationServiceFlagName,
Usage: "URL of the Espresso attestation service",
EnvVars: espressoEnvs(envPrefix, "ESPRESSO_ATTESTATION_SERVICE"),
Category: category,
},
&cli.StringFlag{
Name: BatchAuthenticatorAddrFlagName,
Usage: "Address of the Batch Authenticator contract",
EnvVars: espressoEnvs(envPrefix, "BATCH_AUTHENTICATOR_ADDR"),
Category: category,
},
&cli.Uint64Flag{
Name: VerifyReceiptMaxBlocksFlagName,
Usage: "Number of HotShot blocks to wait for a submitted transaction to become queryable before re-submitting",
Value: DefaultVerifyReceiptMaxBlocks,
EnvVars: espressoEnvs(envPrefix, "VERIFY_RECEIPT_MAX_BLOCKS"),
Category: category,
},
&cli.DurationFlag{
Name: VerifyReceiptSafetyTimeoutFlagName,
Usage: "Wall-clock backstop for receipt verification; re-submits the transaction if this duration is exceeded",
Value: DefaultVerifyReceiptSafetyTimeout,
EnvVars: espressoEnvs(envPrefix, "VERIFY_RECEIPT_SAFETY_TIMEOUT"),
Category: category,
},
&cli.DurationFlag{
Name: VerifyReceiptRetryDelayFlagName,
Usage: "Delay between receipt verification retries",
Value: DefaultVerifyReceiptRetryDelay,
EnvVars: espressoEnvs(envPrefix, "VERIFY_RECEIPT_RETRY_DELAY"),
Category: category,
},
// Note: --espresso.fallback-auth-lead-time is registered by the
// fallback batcher in op-batcher/flags/flags.go; it is read by both
// the fallback and the TEE batcher paths.
}
}

type CLIConfig struct {
Enabled bool
PollInterval time.Duration
QueryServiceURLs []string
LightClientAddr common.Address
BatchAuthenticatorAddr common.Address
L1URL string
RollupL1URL string
TestingBatcherPrivateKey *ecdsa.PrivateKey
Namespace uint64
CaffeinationHeightEspresso uint64
CaffeinationHeightL2 uint64
EspressoAttestationService string

// Batch submission receipt verification tuning
VerifyReceiptMaxBlocks uint64
VerifyReceiptSafetyTimeout time.Duration
VerifyReceiptRetryDelay time.Duration

// Non directly configurable option
allowEmptyAttestationService bool `json:"-"`
}

// AllowEmptyAttestationService allows the attestation service URL to be
// empty. This is set explicitly from a public method, and isn't derivable
// from serialization or any other form other than this method. This allows
// this setting to be configured via the code, but not externally.
func (c *CLIConfig) AllowEmptyAttestationService() {
c.allowEmptyAttestationService = true
}

func (c CLIConfig) Check() error {
if c.Enabled {
Comment thread
piersy marked this conversation as resolved.
// Check required fields when Espresso is enabled
if len(c.QueryServiceURLs) == 0 {
return fmt.Errorf("query service URLs are required when Espresso is enabled")
}
if c.LightClientAddr == (common.Address{}) {
return fmt.Errorf("light client address is required when Espresso is enabled")
}
if c.L1URL == "" {
return fmt.Errorf("L1 URL is required when Espresso is enabled")
}
if c.RollupL1URL == "" {
return fmt.Errorf("rollup L1 URL is required when Espresso is enabled")
}
if c.Namespace == 0 {
return fmt.Errorf("namespace is required when Espresso is enabled")
}
if !c.allowEmptyAttestationService && c.EspressoAttestationService == "" {
return fmt.Errorf("attestation service URL is required when Espresso is enabled")
}
if c.VerifyReceiptMaxBlocks == 0 {
return fmt.Errorf("verify-receipt-max-blocks must be > 0")
}
if c.VerifyReceiptSafetyTimeout <= 0 {
return fmt.Errorf("verify-receipt-safety-timeout must be > 0")
}
if c.VerifyReceiptRetryDelay <= 0 {
return fmt.Errorf("verify-receipt-retry-delay must be > 0")
}
}
return nil
}

func ReadCLIConfig(c *cli.Context) CLIConfig {
config := CLIConfig{
Enabled: c.Bool(EnabledFlagName),
PollInterval: c.Duration(PollIntervalFlagName),
L1URL: c.String(L1UrlFlagName),
RollupL1URL: c.String(RollupL1UrlFlagName),
Namespace: c.Uint64(NamespaceFlagName),
CaffeinationHeightEspresso: c.Uint64(CaffeinationHeightEspresso),
CaffeinationHeightL2: c.Uint64(CaffeinationHeightL2),
EspressoAttestationService: c.String(AttestationServiceFlagName),
VerifyReceiptMaxBlocks: c.Uint64(VerifyReceiptMaxBlocksFlagName),
VerifyReceiptSafetyTimeout: c.Duration(VerifyReceiptSafetyTimeoutFlagName),
VerifyReceiptRetryDelay: c.Duration(VerifyReceiptRetryDelayFlagName),
}

config.QueryServiceURLs = c.StringSlice(QueryServiceUrlsFlagName)

addrStr := c.String(LightClientAddrFlagName)
config.LightClientAddr = common.HexToAddress(addrStr)

batchAuthenticatorAddrStr := c.String(BatchAuthenticatorAddrFlagName)
config.BatchAuthenticatorAddr = common.HexToAddress(batchAuthenticatorAddrStr)

pkStr := c.String(TestingBatcherPrivateKeyFlagName)
pkStr = strings.TrimPrefix(pkStr, "0x")
pk, err := crypto.HexToECDSA(pkStr)
if err == nil {
config.TestingBatcherPrivateKey = pk
}

return config
}
60 changes: 60 additions & 0 deletions espresso/ethclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package espresso

import (
"context"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"

"github.com/ethereum-optimism/optimism/espresso/bindings"
)

// AdaptL1BlockRefClient is a wrapper around eth.L1BlockRef that implements the espresso.L1Client interface
type AdaptL1BlockRefClient struct {
L1Client *ethclient.Client
}

// NewAdaptL1BlockRefClient creates a new L1BlockRefClient
func NewAdaptL1BlockRefClient(L1Client *ethclient.Client) *AdaptL1BlockRefClient {
return &AdaptL1BlockRefClient{
L1Client: L1Client,
}
}

// HeaderHashByNumber implements the espresso.L1Client interface
func (c *AdaptL1BlockRefClient) HeaderHashByNumber(ctx context.Context, number *big.Int) (common.Hash, error) {
expectedL1BlockRef, err := c.L1Client.HeaderByNumber(ctx, number)
if err != nil {
return common.Hash{}, err
}

return expectedL1BlockRef.Hash(), nil
}

func (c *AdaptL1BlockRefClient) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
return c.L1Client.CodeAt(ctx, contract, blockNumber)
}

func (c *AdaptL1BlockRefClient) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
return c.L1Client.CallContract(ctx, call, blockNumber)
}

// FetchEspressoBatcherAddress reads the Espresso batcher address from the BatchAuthenticator
// contract on L1. This is used by the caff node to determine which address signed
// Espresso batches, since the Espresso batcher may use a different key than the
// SystemConfig batcher (fallback batcher).
func FetchEspressoBatcherAddress(ctx context.Context, l1Client *ethclient.Client, batchAuthenticatorAddr common.Address) (common.Address, error) {
caller, err := bindings.NewBatchAuthenticatorCaller(batchAuthenticatorAddr, l1Client)
if err != nil {
return common.Address{}, fmt.Errorf("failed to bind BatchAuthenticator at %s: %w", batchAuthenticatorAddr, err)
}
addr, err := caller.EspressoBatcher(&bind.CallOpts{Context: ctx})
if err != nil {
return common.Address{}, fmt.Errorf("failed to call BatchAuthenticator.espressoBatcher(): %w", err)
}
return addr, nil
}
15 changes: 12 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ toolchain go1.26.4

require (
github.com/BurntSushi/toml v1.5.0
github.com/EspressoSystems/espresso-network/sdks/go v0.3.5-0.20260410134522-1a819609a513
github.com/EspressoSystems/espresso-streamers v1.3.1-0.20260731024110-22c396aa0934
github.com/Masterminds/semver/v3 v3.3.1
github.com/andybalholm/brotli v1.1.0
github.com/base/go-bip39 v1.1.0
Expand All @@ -21,7 +23,7 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.4-0.20251001155152-4eb15ccedf7e
github.com/ethereum-optimism/superchain-registry/validation v0.0.0-20260115192958-fb86a23cd30e
github.com/ethereum/go-ethereum v1.17.0
github.com/ethereum/go-ethereum v1.17.1
github.com/fsnotify/fsnotify v1.9.0
github.com/golang/snappy v1.0.0
github.com/google/go-cmp v0.7.0
Expand All @@ -32,6 +34,8 @@ require (
github.com/hashicorp/raft v1.7.3
github.com/hashicorp/raft-boltdb/v2 v2.3.1
github.com/hashicorp/raft-wal v0.4.2
github.com/hf/nitrite v0.0.0-20241225144000-c2d5d3c4f303
github.com/hf/nsm v0.0.0-20220930140112-cd181bd646b9
github.com/holiman/uint256 v1.3.2
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-leveldb v0.5.0
Expand Down Expand Up @@ -69,18 +73,23 @@ require (
gopkg.in/yaml.v3 v3.0.1
)

require github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
require (
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f // indirect
)

require (
github.com/benbjohnson/immutable v0.4.0 // indirect
github.com/coreos/etcd v3.3.27+incompatible // indirect
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fxamacker/cbor/v2 v2.2.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/grafana/pyroscope-go v1.2.7 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 // indirect
)
Expand Down Expand Up @@ -113,7 +122,7 @@ require (
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/dchest/siphash v1.2.3 // indirect
Expand Down
Loading