forked from ethereum-optimism/optimism
-
Notifications
You must be signed in to change notification settings - Fork 13
Espresso 3b: TEE batcher (re-hosted) #459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
QuentinI
wants to merge
19
commits into
espresso/batcher-fallback
Choose a base branch
from
espresso/batcher
base: espresso/batcher-fallback
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
eab01ee
op-service: add ChainSigner interface and eth_sign helper
QuentinI ea2d4fc
op-node: add EspressoBatch type and converters
QuentinI b3829ac
espresso: add TEE batcher CLI flags, streamer interface, and L1 adapter
QuentinI 28c5440
op-batcher: add Nitro NSM attestation helper
QuentinI 1398890
op-batcher: integrate TEE batcher and Espresso submission loop
QuentinI 2819520
op-batcher: route TEE auth through ordered tx queue
QuentinI df2e7ac
op-node: add EspressoBatch roundtrip test, fix derive_test vet failure
QuentinI 7dceae9
verify batches while converting to a block
lukeiannucci 0cbed58
start throttling loop after espresso/non espresso loops
lukeiannucci 4bfb1f2
make use of unsafeBytesUpdated
lukeiannucci 51884bc
initChainSigner fallback for non espresso mode
lukeiannucci 7c59f66
lint and make use of isBatcherActive
lukeiannucci 5ffeca3
request to clear state from BlockLoader
lukeiannucci deb54f0
safety check for namespace and nil l1 deposit
lukeiannucci c48c8a8
remove duplicate files between espresso streamer and upstream
lukeiannucci 83fd1d6
address comments
lukeiannucci 83905d7
remove uneeded comments
lukeiannucci 3c0864f
update unsafe bytes every 100 blocks. matching non espresso path
lukeiannucci c828d61
always set the prev sync status
lukeiannucci File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,306 @@ | ||
| package espresso | ||
|
|
||
| import ( | ||
| "crypto/ecdsa" | ||
| "fmt" | ||
| "strings" | ||
| "time" | ||
|
|
||
| espressoStreamers "github.com/EspressoSystems/espresso-streamers/op" | ||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/crypto" | ||
| "github.com/ethereum/go-ethereum/ethclient" | ||
| "github.com/ethereum/go-ethereum/log" | ||
|
|
||
| "github.com/urfave/cli/v2" | ||
|
|
||
| espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" | ||
| espressoLightClient "github.com/EspressoSystems/espresso-network/sdks/go/light-client" | ||
| ) | ||
|
|
||
| // 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 { | ||
| // 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 | ||
| } | ||
|
|
||
| func BatchStreamerFromCLIConfig[B espressoStreamers.Batch]( | ||
| cfg CLIConfig, | ||
| log log.Logger, | ||
| unmarshalBatch func([]byte) (*B, error), | ||
| ) (*espressoStreamers.BatchStreamer[B], error) { | ||
| if !cfg.Enabled { | ||
| return nil, fmt.Errorf("espresso is not enabled") | ||
| } | ||
|
|
||
| l1Client, err := ethclient.Dial(cfg.L1URL) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to dial L1 RPC at %s: %w", cfg.L1URL, err) | ||
| } | ||
|
|
||
| RollupL1Client, err := ethclient.Dial(cfg.RollupL1URL) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to dial Rollup L1 RPC at %s: %w", cfg.RollupL1URL, err) | ||
| } | ||
|
|
||
| urlZero := cfg.QueryServiceURLs[0] | ||
| espressoClient := espressoClient.NewClient(urlZero) | ||
|
|
||
| espressoLightClient, err := espressoLightClient.NewLightclientCaller(cfg.LightClientAddr, l1Client) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create Espresso light client") | ||
| } | ||
|
|
||
| return espressoStreamers.NewEspressoStreamer( | ||
| cfg.Namespace, | ||
| NewAdaptL1BlockRefClient(l1Client), | ||
| NewAdaptL1BlockRefClient(RollupL1Client), | ||
| espressoClient, | ||
| espressoLightClient, | ||
| log, | ||
| unmarshalBatch, | ||
| cfg.CaffeinationHeightEspresso, | ||
| cfg.CaffeinationHeightL2, | ||
| cfg.BatchAuthenticatorAddr, | ||
| false, | ||
| ) | ||
| } | ||
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
| 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 | ||
| } |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.