diff --git a/espresso/cli.go b/espresso/cli.go new file mode 100644 index 00000000000..89066fa728d --- /dev/null +++ b/espresso/cli.go @@ -0,0 +1,130 @@ +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} +} + +var ( + EnabledFlagName = espressoFlags("enabled") + PollIntervalFlagName = espressoFlags("poll-interval") + UseFetchApiFlagName = espressoFlags("fetch-api") + QueryServiceUrlsFlagName = espressoFlags("urls") + LightClientAddrFlagName = espressoFlags("light-client-addr") + L1UrlFlagName = espressoFlags("l1-url") + TestingBatcherPrivateKeyFlagName = espressoFlags("testing-batcher-private-key") +) + +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.BoolFlag{ + Name: UseFetchApiFlagName, + Usage: "Use fetch API for Espresso queries", + Value: false, + EnvVars: espressoEnvs(envPrefix, "FETCH_API"), + 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, + }, + } +} + +type CLIConfig struct { + Enabled bool + PollInterval time.Duration + UseFetchAPI bool + QueryServiceURLs []string + LightClientAddr common.Address + L1URL string + TestingBatcherPrivateKey *ecdsa.PrivateKey +} + +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") + } + } + return nil +} + +func ReadCLIConfig(c *cli.Context) CLIConfig { + config := CLIConfig{ + Enabled: c.Bool(EnabledFlagName), + PollInterval: c.Duration(PollIntervalFlagName), + UseFetchAPI: c.Bool(UseFetchApiFlagName), + L1URL: c.String(L1UrlFlagName), + } + + config.QueryServiceURLs = c.StringSlice(QueryServiceUrlsFlagName) + + addrStr := c.String(LightClientAddrFlagName) + config.LightClientAddr = common.HexToAddress(addrStr) + + pkStr := c.String(TestingBatcherPrivateKeyFlagName) + pkStr = strings.TrimPrefix(pkStr, "0x") + pk, err := crypto.HexToECDSA(pkStr) + if err == nil { + config.TestingBatcherPrivateKey = pk + } + + return config +} diff --git a/espresso/docker-compose.yml b/espresso/docker-compose.yml index 3d60fe437b7..99ab55e1092 100644 --- a/espresso/docker-compose.yml +++ b/espresso/docker-compose.yml @@ -96,7 +96,7 @@ services: l1-genesis: condition: service_completed_successfully healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:${L1_HTTP_PORT}" ] + test: ["CMD", "curl", "-f", "http://localhost:${L1_HTTP_PORT}"] interval: 3s timeout: 2s retries: 40 @@ -191,7 +191,7 @@ services: dockerfile: espresso/docker/op-stack/Dockerfile target: op-node-target healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:${ROLLUP_PORT}" ] + test: ["CMD", "curl", "-f", "http://localhost:${ROLLUP_PORT}"] interval: 3s timeout: 2s retries: 40 @@ -233,7 +233,7 @@ services: dockerfile: espresso/docker/op-stack/Dockerfile target: op-node-target healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:${VERIFIER_PORT}" ] + test: ["CMD", "curl", "-f", "http://localhost:${VERIFIER_PORT}"] interval: 3s timeout: 2s retries: 40 @@ -271,7 +271,7 @@ services: dockerfile: espresso/docker/op-stack/Dockerfile target: op-node-target healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:${CAFF_PORT}" ] + test: ["CMD", "curl", "-f", "http://localhost:${CAFF_PORT}"] interval: 3s timeout: 2s retries: 40 @@ -287,9 +287,9 @@ services: OP_NODE_L1_ETH_RPC: http://l1-geth:${L1_HTTP_PORT} OP_NODE_L1_BEACON: http://l1-beacon:${L1_BEACON_PORT} OP_NODE_L2_ENGINE_RPC: http://op-geth-caff-node:${OP_ENGINE_PORT} - OP_NODE_CAFF_L1_ETH_RPC: http://l1-geth:${L1_HTTP_PORT} - OP_NODE_CAFF_ESPRESSO_LIGHT_CLIENT_ADDR: "0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797" - OP_NODE_CAFF_HOTSHOT_URLS: http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT},http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT} + OP_NODE_ESPRESSO_L1_URL: http://l1-geth:${L1_HTTP_PORT} + OP_NODE_ESPRESSO_LIGHT_CLIENT_ADDR: "0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797" + OP_NODE_ESPRESSO_URLS: http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT},http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT} OP_NODE_RPC_PORT: ${CAFF_PORT} ports: - "${CAFF_PORT}:${CAFF_PORT}" @@ -301,14 +301,13 @@ services: - --rollup.config=/config/rollup.json - --rpc.addr=0.0.0.0 - --sequencer.enabled=false - - --caff.node=true + - --espresso.enabled=true - --verifier.l1-confs=0 - --rollup.load-protocol-versions=false - --rollup.halt=none - --l1.trustrpc=true - --rpc.enable-admin=true - - --caff.next-hotshot-block-num=1 - - --caff.polling-hotshot-polling-interval=500ms + - --espresso.poll-interval=500ms - --log.level=debug - --p2p.disable=true - --l1.http-poll-interval=1s @@ -316,7 +315,7 @@ services: restart: "no" op-batcher: - profiles: [ "default" ] + profiles: ["default"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile @@ -339,30 +338,35 @@ services: OP_BATCHER_L1_ETH_RPC: http://l1-geth:${L1_HTTP_PORT} OP_BATCHER_L2_ETH_RPC: http://op-geth-sequencer:${OP_HTTP_PORT} OP_BATCHER_ROLLUP_RPC: http://op-node-sequencer:${ROLLUP_PORT} - OP_BATCHER_ESPRESSO_URL: http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT},http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT} + OP_BATCHER_ESPRESSO_URLS: http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT},http://espresso-dev-node:${ESPRESSO_SEQUENCER_API_PORT} volumes: - ../packages/contracts-bedrock/lib/superchain-registry/ops/testdata/monorepo:/config command: - op-batcher - - --espresso-light-client-addr=0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797 - - --testing-espresso-batcher-private-key=${OP_TESTING_BATCHER_PRIVATE_KEY:-$OPERATOR_PRIVATE_KEY} + - --espresso.enabled=true + - --espresso.poll-interval=1s + - --espresso.light-client-addr=0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797 + - --espresso.testing-batcher-private-key=${OP_TESTING_BATCHER_PRIVATE_KEY:-$OPERATOR_PRIVATE_KEY} - --private-key=${OP_BATCHER_PRIVATE_KEY:-$OPERATOR_PRIVATE_KEY} - --throttle-threshold=0 - --max-channel-duration=2 - --target-num-frames=1 - --max-pending-tx=32 - --altda.max-concurrent-da-requests=32 - - --espresso-poll-interval=1s op-batcher-tee: - profiles: [ "tee" ] + profiles: ["tee"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile target: op-batcher-enclave-target image: op-batcher-tee:espresso healthcheck: - test: [ "CMD-SHELL", "test -f /tmp/enclave-tools.pid && kill -0 $(cat /tmp/enclave-tools.pid) 2>/dev/null || exit 1" ] + test: + [ + "CMD-SHELL", + "test -f /tmp/enclave-tools.pid && kill -0 $(cat /tmp/enclave-tools.pid) 2>/dev/null || exit 1", + ] interval: 30s timeout: 10s retries: 3 @@ -408,7 +412,7 @@ services: /source/espresso/docker/op-batcher-tee/run-enclave.sh op-proposer: - profiles: [ "default" ] + profiles: ["default"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile @@ -432,10 +436,10 @@ services: OP_PROPOSER_PROPOSAL_INTERVAL: 6s OP_PROPOSER_MNEMONIC: "test test test test test test test test test test test junk" OP_PROPOSER_HD_PATH: "m/44'/60'/0'/0/1" - OP_PROPOSER_GAME_TYPE: '1' + OP_PROPOSER_GAME_TYPE: "1" op-proposer-tee: - profiles: [ "tee" ] + profiles: ["tee"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile @@ -459,11 +463,10 @@ services: OP_PROPOSER_PROPOSAL_INTERVAL: 6s OP_PROPOSER_MNEMONIC: "test test test test test test test test test test test junk" OP_PROPOSER_HD_PATH: "m/44'/60'/0'/0/1" - OP_PROPOSER_GAME_TYPE: '1' - + OP_PROPOSER_GAME_TYPE: "1" op-challenger: - profiles: [ "default" ] + profiles: ["default"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile diff --git a/espresso/docker/op-batcher-tee/run-enclave.sh b/espresso/docker/op-batcher-tee/run-enclave.sh index f89e0efd041..c1682e8c6f9 100755 --- a/espresso/docker/op-batcher-tee/run-enclave.sh +++ b/espresso/docker/op-batcher-tee/run-enclave.sh @@ -38,15 +38,15 @@ echo "=====================================" BATCHER_ARGS="--l1-eth-rpc=$L1_RPC_URL" BATCHER_ARGS="$BATCHER_ARGS,--l2-eth-rpc=$L2_RPC_URL" BATCHER_ARGS="$BATCHER_ARGS,--rollup-rpc=$ROLLUP_RPC_URL" -BATCHER_ARGS="$BATCHER_ARGS,--espresso-url=$ESPRESSO_URL1" -BATCHER_ARGS="$BATCHER_ARGS,--espresso-url=$ESPRESSO_URL2" -BATCHER_ARGS="$BATCHER_ARGS,--testing-espresso-batcher-private-key=$OPERATOR_PRIVATE_KEY" +BATCHER_ARGS="$BATCHER_ARGS,--espresso.enabled=true" +BATCHER_ARGS="$BATCHER_ARGS,--espresso.urls=$ESPRESSO_URL1" +BATCHER_ARGS="$BATCHER_ARGS,--espresso.urls=$ESPRESSO_URL2" BATCHER_ARGS="$BATCHER_ARGS,--mnemonic=test test test test test test test test test test test junk" BATCHER_ARGS="$BATCHER_ARGS,--hd-path=m/44'/60'/0'/0/0" BATCHER_ARGS="$BATCHER_ARGS,--throttle-threshold=0" BATCHER_ARGS="$BATCHER_ARGS,--max-channel-duration=1" BATCHER_ARGS="$BATCHER_ARGS,--target-num-frames=1" -BATCHER_ARGS="$BATCHER_ARGS,--espresso-light-client-addr=0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797" +BATCHER_ARGS="$BATCHER_ARGS,--espresso.light-client-addr=0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797" # Add debug arguments if enabled if [ "$ENCLAVE_DEBUG" = "true" ]; then diff --git a/espresso/environment/enclave_helpers.go b/espresso/environment/enclave_helpers.go index f6d494e5a3e..a903594eb63 100644 --- a/espresso/environment/enclave_helpers.go +++ b/espresso/environment/enclave_helpers.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/espresso" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-batcher/batcher" "github.com/ethereum-optimism/optimism/op-batcher/bindings" @@ -92,6 +93,7 @@ func LaunchBatcherInEnclave() E2eDevnetLauncherOption { l1Rpc := sys.L1.UserRPC().(endpoint.HttpRPC).HttpRPC() appendArg(&args, flags.L1EthRpcFlag.Name, l1Rpc) appendArg(&args, txmgr.L1RPCFlagName, l1Rpc) + appendArg(&args, espresso.L1UrlFlagName, l1Rpc) l2EthRpc := sys.EthInstances[e2esys.RoleSeq].UserRPC().(endpoint.HttpRPC).HttpRPC() appendArg(&args, flags.L2EthRpcFlag.Name, l2EthRpc) rollupRpc := sys.RollupNodes[e2esys.RoleSeq].UserRPC().(endpoint.HttpRPC).HttpRPC() @@ -105,8 +107,6 @@ func LaunchBatcherInEnclave() E2eDevnetLauncherOption { appendArg(&args, flags.CompressionAlgoFlag.Name, c.CompressionAlgo.String()) appendArg(&args, flags.CompressorFlag.Name, c.Compressor) appendArg(&args, flags.DataAvailabilityTypeFlag.Name, c.DataAvailabilityType.String()) - appendArg(&args, flags.EspressoLCAddrFlag.Name, c.EspressoLightClientAddr) - appendArg(&args, flags.EspressoPollIntervalFlag.Name, c.EspressoPollInterval) appendArg(&args, flags.MaxBlocksPerSpanBatch.Name, c.MaxBlocksPerSpanBatch) appendArg(&args, flags.MaxChannelDurationFlag.Name, c.MaxChannelDuration) appendArg(&args, flags.MaxL1TxSizeBytesFlag.Name, c.MaxL1TxSize) @@ -120,11 +120,6 @@ func LaunchBatcherInEnclave() E2eDevnetLauncherOption { appendArg(&args, flags.ThrottleThresholdFlag.Name, c.ThrottleThreshold) appendArg(&args, flags.ThrottleTxSizeFlag.Name, c.ThrottleTxSize) appendArg(&args, flags.WaitNodeSyncFlag.Name, c.WaitNodeSync) - for _, url := range c.EspressoUrls { - appendArg(&args, flags.EspressoUrlsFlag.Name, url) - } - appendArg(&args, flags.EspressoLCAddrFlag.Name, c.EspressoLightClientAddr) - appendArg(&args, flags.TestingEspressoBatcherPrivateKeyFlag.Name, c.TestingEspressoBatcherPrivateKey) // TxMgr flags appendArg(&args, txmgr.MnemonicFlagName, c.TxMgrConfig.Mnemonic) @@ -175,6 +170,16 @@ func LaunchBatcherInEnclave() E2eDevnetLauncherOption { appendArg(&args, altda.GetTimeoutFlagName, c.AltDA.GetTimeout) appendArg(&args, altda.MaxConcurrentRequestsFlagName, c.AltDA.MaxConcurrentRequests) + // Espresso flags + appendArg(&args, espresso.EnabledFlagName, c.Espresso.Enabled) + appendArg(&args, espresso.PollIntervalFlagName, c.Espresso.PollInterval) + appendArg(&args, espresso.LightClientAddrFlagName, c.Espresso.LightClientAddr) + appendArg(&args, espresso.TestingBatcherPrivateKeyFlagName, hexutil.Encode(crypto.FromECDSA(c.Espresso.TestingBatcherPrivateKey))) + appendArg(&args, espresso.UseFetchApiFlagName, c.Espresso.UseFetchAPI) + for _, url := range c.Espresso.QueryServiceURLs { + appendArg(&args, espresso.QueryServiceUrlsFlagName, url) + } + err := SetupEnclaver(ct.Ctx, sys, args...) if err != nil { panic(fmt.Sprintf("failed to setup enclaver: %v", err)) diff --git a/espresso/environment/espresso_caff_node.go b/espresso/environment/espresso_caff_node.go index 8241fba0d0d..8ec03f2847c 100644 --- a/espresso/environment/espresso_caff_node.go +++ b/espresso/environment/espresso_caff_node.go @@ -10,12 +10,13 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/espresso" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/opnode" "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" "github.com/ethereum-optimism/optimism/op-node/chaincfg" - "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" ) const ( @@ -111,13 +112,13 @@ func LaunchCaffNode(t *testing.T, system *e2esys.System, espressoDevNode Espress caffNodeConfig := *system.Cfg.Nodes[e2esys.RoleVerif] caffNodeConfig.Rollup = *system.RollupConfig - caffNodeConfig.Rollup.CaffNodeConfig = rollup.CaffNodeConfig{ - IsCaffNode: true, - PollingHotShotPollingInterval: 30 * time.Millisecond, + caffNodeConfig.Rollup.CaffNodeConfig = espresso.CLIConfig{ + Enabled: true, + PollInterval: 30 * time.Millisecond, // To create a valid multiple nodes client, we need to provide at least 2 URLs. - HotShotUrls: []string{u.String(), u.String()}, - L1EthRpc: system.L1.UserRPC().RPC(), - EspressoLightClientAddr: ESPRESSO_LIGHT_CLIENT_ADDRESS, + QueryServiceURLs: []string{u.String(), u.String()}, + L1URL: system.L1.UserRPC().RPC(), + LightClientAddr: common.HexToAddress(ESPRESSO_LIGHT_CLIENT_ADDRESS), } // Configure diff --git a/espresso/environment/optitmism_espresso_test_helpers.go b/espresso/environment/optitmism_espresso_test_helpers.go index c139917f5bf..aba11f3efcb 100644 --- a/espresso/environment/optitmism_espresso_test_helpers.go +++ b/espresso/environment/optitmism_espresso_test_helpers.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" gethNode "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" @@ -555,7 +554,7 @@ func SetBatcherKey(privateKey ecdsa.PrivateKey) E2eDevnetLauncherOption { { Role: "set-batcher-key", BatcherMod: func(c *batcher.CLIConfig, sys *e2esys.System) { - c.TestingEspressoBatcherPrivateKey = hexutil.Encode(crypto.FromECDSA(&privateKey)) + c.Espresso.TestingBatcherPrivateKey = &privateKey }, }, }, @@ -574,7 +573,7 @@ func SetEspressoUrls(numGood int, numBad int, badServerUrl string) E2eDevnetLaun { BatcherMod: func(c *batcher.CLIConfig, sys *e2esys.System) { - goodUrl := c.EspressoUrls[0] + goodUrl := c.Espresso.QueryServiceURLs[0] var urls []string for i := 0; i < numGood; i++ { @@ -584,7 +583,7 @@ func SetEspressoUrls(numGood int, numBad int, badServerUrl string) E2eDevnetLaun for i := 0; i < numBad; i++ { urls = append(urls, badServerUrl) } - c.EspressoUrls = urls + c.Espresso.QueryServiceURLs = urls }, }, }, @@ -864,10 +863,10 @@ func launchEspressoDevNodeStartOption(ct *E2eDevnetLauncherContext) e2esys.Start } ct.EspressoDevNode = espressoDevNode - c.EspressoUrls = espressoDevNode.espressoUrls + c.Espresso.Enabled = true + c.Espresso.QueryServiceURLs = espressoDevNode.espressoUrls c.LogConfig.Level = slog.LevelDebug - c.TestingEspressoBatcherPrivateKey = "0x" + config.ESPRESSO_PRE_APPROVED_BATCHER_PRIVATE_KEY - c.EspressoLightClientAddr = ESPRESSO_LIGHT_CLIENT_ADDRESS + c.Espresso.LightClientAddr = common.HexToAddress(ESPRESSO_LIGHT_CLIENT_ADDRESS) }, } diff --git a/espresso/environment/query_service_intercept.go b/espresso/environment/query_service_intercept.go index 14e907ae963..6bc406a82f9 100644 --- a/espresso/environment/query_service_intercept.go +++ b/espresso/environment/query_service_intercept.go @@ -430,14 +430,14 @@ func createEspressoProxyOption(ctx *E2eDevnetLauncherContext, proxy *EspressoDev return } - if len(cfg.EspressoUrls) == 0 { + if len(cfg.Espresso.QueryServiceURLs) == 0 { // This should be being called after the Espresso // Dev Node is Already Live. // Without an Espresso URL, we cannot proceed. return } - u, err := url.Parse(cfg.EspressoUrls[0]) + u, err := url.Parse(cfg.Espresso.QueryServiceURLs[0]) if err != nil || u == nil { // We encountered an error ctx.Error = err @@ -448,7 +448,7 @@ func createEspressoProxyOption(ctx *E2eDevnetLauncherContext, proxy *EspressoDev proxy.u = *u // Replace the Espresso URL with the proxy URL // We need to provide at least 2 URLs to create a valid multiple nodes client - cfg.EspressoUrls = []string{server.URL, server.URL} + cfg.Espresso.QueryServiceURLs = []string{server.URL, server.URL} } } diff --git a/go.mod b/go.mod index 448bc6cca84..3bdc7b6230c 100644 --- a/go.mod +++ b/go.mod @@ -90,7 +90,7 @@ require ( github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/coder/websocket v1.8.13 // indirect + github.com/coder/websocket v1.8.13 github.com/consensys/bavard v0.1.27 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect diff --git a/op-batcher/batcher/config.go b/op-batcher/batcher/config.go index 9dc12216ab3..9de9de6e4f9 100644 --- a/op-batcher/batcher/config.go +++ b/op-batcher/batcher/config.go @@ -6,9 +6,11 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" + "github.com/ethereum-optimism/optimism/espresso" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-batcher/compressor" "github.com/ethereum-optimism/optimism/op-batcher/flags" @@ -124,10 +126,7 @@ type CLIConfig struct { RPC oprpc.CLIConfig AltDA altda.CLIConfig - EspressoPollInterval time.Duration - EspressoUrls []string - EspressoLightClientAddr string - TestingEspressoBatcherPrivateKey string + Espresso espresso.CLIConfig } func (c *CLIConfig) Check() error { @@ -185,6 +184,15 @@ func (c *CLIConfig) Check() error { if err := c.RPC.Check(); err != nil { return err } + + if c.Espresso.L1URL == "" { + log.Warn("Espresso L1 URL not provided, using L1EthRpc") + c.Espresso.L1URL = c.L1EthRpc + } + if err := c.Espresso.Check(); err != nil { + return err + } + return nil } @@ -225,9 +233,6 @@ func NewConfig(ctx *cli.Context) *CLIConfig { ThrottleAlwaysBlockSize: ctx.Uint64(flags.ThrottleAlwaysBlockSizeFlag.Name), PreferLocalSafeL2: ctx.Bool(flags.PreferLocalSafeL2Flag.Name), - EspressoUrls: ctx.StringSlice(flags.EspressoUrlsFlag.Name), - EspressoLightClientAddr: ctx.String(flags.EspressoLCAddrFlag.Name), - TestingEspressoBatcherPrivateKey: ctx.String(flags.TestingEspressoBatcherPrivateKeyFlag.Name), - EspressoPollInterval: ctx.Duration(flags.EspressoPollIntervalFlag.Name), + Espresso: espresso.ReadCLIConfig(ctx), } } diff --git a/op-batcher/batcher/service.go b/op-batcher/batcher/service.go index 05b519a6189..d3ae735c653 100644 --- a/op-batcher/batcher/service.go +++ b/op-batcher/batcher/service.go @@ -13,8 +13,6 @@ import ( espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" espressoLightClient "github.com/EspressoSystems/espresso-network/sdks/go/light-client" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" - "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/hf/nitrite" @@ -138,15 +136,14 @@ func (bs *BatcherService) initFromCLIConfig(ctx context.Context, version string, } opts = append(optsFromRPC, opts...) - // MultipleNodesClient requires at least 2 URLs. - if len(cfg.EspressoUrls) > 1 { - bs.EspressoPollInterval = cfg.EspressoPollInterval - client, err := espressoClient.NewMultipleNodesClient(cfg.EspressoUrls) + if cfg.Espresso.Enabled { + bs.EspressoPollInterval = cfg.Espresso.PollInterval + client, err := espressoClient.NewMultipleNodesClient(cfg.Espresso.QueryServiceURLs) if err != nil { return fmt.Errorf("failed to create Espresso client: %w", err) } bs.Espresso = client - espressoLightClient, err := espressoLightClient.NewLightclientCaller(common.HexToAddress(cfg.EspressoLightClientAddr), bs.L1Client) + espressoLightClient, err := espressoLightClient.NewLightclientCaller(cfg.Espresso.LightClientAddr, bs.L1Client) if err != nil { return fmt.Errorf("failed to create Espresso light client") } @@ -162,9 +159,9 @@ func (bs *BatcherService) initFromCLIConfig(ctx context.Context, version string, bs.Log.Info("Not running in enclave, skipping attestation", "info", err) // Replace ephemeral keys with configured keys, as in devnet they'll be pre-approved for batching - privateKey, err := crypto.HexToECDSA(strings.TrimPrefix(cfg.TestingEspressoBatcherPrivateKey, "0x")) - if err != nil { - return fmt.Errorf("failed to parse batcher's testing private key (%v): %w", cfg.TestingEspressoBatcherPrivateKey, err) + privateKey := cfg.Espresso.TestingBatcherPrivateKey + if privateKey == nil { + return fmt.Errorf("when not running in enclave, testing batcher private key should be set") } publicKey := privateKey.Public() diff --git a/op-batcher/enclave-entrypoint.bash b/op-batcher/enclave-entrypoint.bash index 50523dfa3c2..d6a640e626e 100644 --- a/op-batcher/enclave-entrypoint.bash +++ b/op-batcher/enclave-entrypoint.bash @@ -6,7 +6,7 @@ # to directly pass commandline arguments when starting EIF images) # We will need to start a proxy for each of those urls -URL_ARG_RE='^(--altda\.da-server|--espresso-url|--l1-eth-rpc|--l2-eth-rpc|--rollup-rpc|--signer\.endpoint)(=|$)' +URL_ARG_RE='^(--altda\.da-server|--espresso\.urls|--espresso.\l1-url|--l1-eth-rpc|--l2-eth-rpc|--rollup-rpc|--signer\.endpoint)(=|$)' # Re-populate the arguments passed through the environment if [ -n "$ENCLAVE_BATCHER_ARGS" ]; then diff --git a/op-batcher/flags/flags.go b/op-batcher/flags/flags.go index 486261c8ff9..8c13fd9faf7 100644 --- a/op-batcher/flags/flags.go +++ b/op-batcher/flags/flags.go @@ -8,6 +8,7 @@ import ( "github.com/urfave/cli/v2" + "github.com/ethereum-optimism/optimism/espresso" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-batcher/compressor" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" @@ -58,12 +59,6 @@ var ( Value: 100 * time.Millisecond, EnvVars: prefixEnvVars("POLL_INTERVAL"), } - EspressoPollIntervalFlag = &cli.DurationFlag{ - Name: "espresso-poll-interval", - Usage: "How frequently to poll Espresso for new batches", - Value: 6 * time.Second, - EnvVars: prefixEnvVars("ESPRESSO_POLL_INTERVAL"), - } MaxPendingTransactionsFlag = &cli.Uint64Flag{ Name: "max-pending-tx", Usage: "The maximum number of pending transactions. 0 for no limit.", @@ -194,24 +189,6 @@ var ( Value: false, EnvVars: prefixEnvVars("PREFER_LOCAL_SAFE_L2"), } - EspressoUrlsFlag = &cli.StringSliceFlag{ - Name: "espresso-url", - Usage: "URL of Espresso query service", - EnvVars: prefixEnvVars("ESPRESSO_URL"), - } - - EspressoLCAddrFlag = &cli.StringFlag{ - Name: "espresso-light-client-addr", - Usage: "Address of Espresso Light Client contract proxy", - Value: "0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797", - EnvVars: prefixEnvVars("ESPRESSO_LIGHT_CLIENT_ADDR"), - } - TestingEspressoBatcherPrivateKeyFlag = &cli.StringFlag{ - Name: "testing-espresso-batcher-private-key", - Usage: "Private key of batcher in Espresso mode: ONLY FOR TESTING", - Value: "", - EnvVars: prefixEnvVars("TESTING_ESPRESSO_BATCHER_PRIVATE_KEY"), - } // Legacy Flags SequencerHDPathFlag = txmgr.SequencerHDPathFlag ) @@ -245,10 +222,6 @@ var optionalFlags = []cli.Flag{ ThrottleBlockSizeFlag, ThrottleAlwaysBlockSizeFlag, PreferLocalSafeL2Flag, - EspressoUrlsFlag, - EspressoLCAddrFlag, - EspressoPollIntervalFlag, - TestingEspressoBatcherPrivateKeyFlag, } func init() { @@ -258,6 +231,7 @@ func init() { optionalFlags = append(optionalFlags, oppprof.CLIFlags(EnvVarPrefix)...) optionalFlags = append(optionalFlags, txmgr.CLIFlags(EnvVarPrefix)...) optionalFlags = append(optionalFlags, altda.CLIFlags(EnvVarPrefix, "")...) + optionalFlags = append(optionalFlags, espresso.CLIFlags(EnvVarPrefix, "")...) Flags = append(requiredFlags, optionalFlags...) } diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index 95d61e21ac1..b64b9994136 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -17,6 +17,7 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/espresso" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" @@ -38,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" @@ -1009,6 +1011,17 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, batcherTargetNumFrames = 1 } + testingBatcherPk, err := crypto.HexToECDSA(config.ESPRESSO_PRE_APPROVED_BATCHER_PRIVATE_KEY) + if err != nil { + return nil, fmt.Errorf("failed to parse pre-approved batcher private key: %w", err) + } + espressoCfg := espresso.CLIConfig{ + Enabled: (cfg.AllocType == config.AllocTypeEspressoWithEnclave) || (cfg.AllocType == config.AllocTypeEspressoWithoutEnclave), + PollInterval: 250 * time.Millisecond, + L1URL: sys.EthInstances[RoleL1].UserRPC().RPC(), + TestingBatcherPrivateKey: testingBatcherPk, + } + batcherCLIConfig := &bss.CLIConfig{ L1EthRpc: sys.EthInstances[RoleL1].UserRPC().RPC(), L2EthRpc: sys.EthInstances[RoleSeq].UserRPC().RPC(), @@ -1021,7 +1034,6 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, ApproxComprRatio: 0.4, SubSafetyMargin: 4, PollInterval: 50 * time.Millisecond, - EspressoPollInterval: 250 * time.Millisecond, TxMgrConfig: setuputils.NewTxMgrConfig(sys.EthInstances[RoleL1].UserRPC(), cfg.Secrets.Batcher), LogConfig: oplog.CLIConfig{ Level: log.LevelInfo, @@ -1033,6 +1045,8 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, DataAvailabilityType: sys.Cfg.DataAvailabilityType, CompressionAlgo: derive.Zlib, AltDA: altDACLIConfig, + + Espresso: espressoCfg, } // Apply batcher cli modifications diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index 7191a752b47..a19da2dc66e 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -6,6 +6,7 @@ import ( "github.com/urfave/cli/v2" + "github.com/ethereum-optimism/optimism/espresso" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-node/rollup/engine" "github.com/ethereum-optimism/optimism/op-node/rollup/sync" @@ -29,6 +30,7 @@ const ( AltDACategory = "6. ALT-DA (EXPERIMENTAL)" MiscCategory = "7. MISC" InteropCategory = "8. INTEROP (SUPER EXPERIMENTAL)" + CaffCategory = "9. ESPRESSO CAFFEINATED NODE (EXPERIMENTAL)" ) func init() { @@ -44,6 +46,7 @@ func init() { optionalFlags = append(optionalFlags, DeprecatedFlags...) optionalFlags = append(optionalFlags, opflags.CLIFlags(EnvVarPrefix, RollupCategory)...) optionalFlags = append(optionalFlags, altda.CLIFlags(EnvVarPrefix, AltDACategory)...) + optionalFlags = append(optionalFlags, espresso.CLIFlags(EnvVarPrefix, CaffCategory)...) Flags = append(requiredFlags, optionalFlags...) } @@ -464,48 +467,6 @@ var ( Category: RollupCategory, Hidden: true, } - CaffNodeFlag = &cli.BoolFlag{ - Name: "caff.node", - Usage: "Enable the caffeinated node", - EnvVars: prefixEnvVars("CAFF_NODE"), - Value: false, - Category: OperationsCategory, - } - CaffNodeNextHotShotBlockNum = &cli.Uint64Flag{ - Name: "caff.next-hotshot-block-num", - Usage: "Next hotshot block number for the caffeinated node", - EnvVars: prefixEnvVars("CAFF_NEXT_HOTSHOT_BLOCK_NUM"), - Value: 1, - Category: OperationsCategory, - } - CaffNodePollingHotShotPollingInterval = &cli.DurationFlag{ - Name: "caff.polling-hotshot-polling-interval", - Usage: "Polling interval for the hotshot block", - EnvVars: prefixEnvVars("CAFF_POLLING_HOTSHOT_POLLING_INTERVAL"), - Value: 500 * time.Millisecond, - Category: OperationsCategory, - } - CaffNodeHotShotUrls = &cli.StringSliceFlag{ - Name: "caff.hotshot-urls", - Usage: "HotShot urls for the caffeinated node", - EnvVars: prefixEnvVars("CAFF_HOTSHOT_URLS"), - Value: cli.NewStringSlice("http://op-espresso-devnode:24000", "http://op-espresso-devnode:24000", "http://op-espresso-devnode:24000", "http://op-espresso-devnode:24000"), - Category: OperationsCategory, - } - CaffNodeL1EthRpc = &cli.StringFlag{ - Name: "caff.l1-eth-rpc", - Usage: "L1 Ethereum RPC endpoint for the caffeinated node", - EnvVars: prefixEnvVars("CAFF_L1_ETH_RPC"), - Value: "http://localhost:8545", - Category: OperationsCategory, - } - CaffNodeEspressoLightClientAddr = &cli.StringFlag{ - Name: "caff.espresso-light-client-addr", - Usage: "Espresso light client address for the caffeinated node", - EnvVars: prefixEnvVars("CAFF_ESPRESSO_LIGHT_CLIENT_ADDR"), - Value: "0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797", - Category: OperationsCategory, - } ) var requiredFlags = []cli.Flag{ @@ -562,12 +523,6 @@ var optionalFlags = []cli.Flag{ InteropRPCPort, InteropJWTSecret, IgnoreMissingPectraBlobSchedule, - CaffNodeFlag, - CaffNodeNextHotShotBlockNum, - CaffNodePollingHotShotPollingInterval, - CaffNodeHotShotUrls, - CaffNodeEspressoLightClientAddr, - CaffNodeL1EthRpc, } var DeprecatedFlags = []cli.Flag{ diff --git a/op-node/rollup/derive/attributes_queue.go b/op-node/rollup/derive/attributes_queue.go index 2cf6196d432..d88d2ea5c67 100644 --- a/op-node/rollup/derive/attributes_queue.go +++ b/op-node/rollup/derive/attributes_queue.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum-optimism/optimism/espresso" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" @@ -77,7 +76,7 @@ type SingularBatchProvider interface { func initEspressoStreamer(log log.Logger, cfg *rollup.Config, l1Fetcher L1Fetcher) *espresso.BatchStreamer[EspressoBatch] { - if !cfg.CaffNodeConfig.IsCaffNode { + if !cfg.CaffNodeConfig.Enabled { log.Info("Espresso streamer not initialized: Caff node is not enabled") return nil } @@ -85,19 +84,19 @@ func initEspressoStreamer(log log.Logger, cfg *rollup.Config, l1Fetcher L1Fetche // Create an adapter that implements espresso.L1Client l1BlockRefClient := NewL1BlockRefClient(l1Fetcher) - l1Client, err := ethclient.Dial(cfg.CaffNodeConfig.L1EthRpc) + l1Client, err := ethclient.Dial(cfg.CaffNodeConfig.L1URL) if err != nil { log.Error("Espresso streamer not initialized: Failed to connect to L1", "err", err) return nil } - lightClient, err := espressoLightClient.NewLightclientCaller(common.HexToAddress(cfg.CaffNodeConfig.EspressoLightClientAddr), l1Client) + lightClient, err := espressoLightClient.NewLightclientCaller(cfg.CaffNodeConfig.LightClientAddr, l1Client) if err != nil { log.Error("Espresso streamer not initialized: Failed to connect to light client", "err", err) return nil } - client, err := espressoClient.NewMultipleNodesClient(cfg.CaffNodeConfig.HotShotUrls) + client, err := espressoClient.NewMultipleNodesClient(cfg.CaffNodeConfig.QueryServiceURLs) if err != nil { log.Error("Espresso streamer not initialized: Failed to connect to hotshot client", "err", err) return nil @@ -111,12 +110,12 @@ func initEspressoStreamer(log log.Logger, cfg *rollup.Config, l1Fetcher L1Fetche func(data []byte) (*EspressoBatch, error) { return UnmarshalEspressoTransaction(data, cfg.Genesis.SystemConfig.BatcherAddr) }, - cfg.CaffNodeConfig.PollingHotShotPollingInterval, + cfg.CaffNodeConfig.PollInterval, ) log.Debug("Espresso Streamer namespace:", streamer.Namespace) - log.Info("Espresso streamer initialized", "namespace", cfg.L2ChainID.Uint64(), "next hotshot block num", cfg.CaffNodeConfig.NextHotShotBlockNum, "polling hotshot polling interval", cfg.CaffNodeConfig.PollingHotShotPollingInterval, "hotshot urls", cfg.CaffNodeConfig.HotShotUrls) + log.Info("Espresso streamer initialized", "namespace", cfg.L2ChainID.Uint64(), "polling hotshot polling interval", cfg.CaffNodeConfig.PollInterval, "hotshot urls", cfg.CaffNodeConfig.QueryServiceURLs) return streamer } @@ -126,7 +125,7 @@ func NewAttributesQueue(log log.Logger, cfg *rollup.Config, builder AttributesBu config: cfg, builder: builder, prev: prev, - isCaffNode: cfg.CaffNodeConfig.IsCaffNode, + isCaffNode: cfg.CaffNodeConfig.Enabled, espressoStreamer: initEspressoStreamer(log, cfg, l1Fetcher), } } diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 617b07ea4f2..f5ae4f0d458 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -9,6 +9,7 @@ import ( "math/big" "time" + "github.com/ethereum-optimism/optimism/espresso" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" @@ -165,21 +166,11 @@ type Config struct { PectraBlobScheduleTime *uint64 `json:"pectra_blob_schedule_time,omitempty"` // Caff Node config - CaffNodeConfig CaffNodeConfig `json:"caff_node_config,omitempty"` + CaffNodeConfig espresso.CLIConfig `json:"caff_node_config,omitempty"` BatchAuthenticatorAddress common.Address `json:"batch_authenticator_address,omitempty,omitzero"` } -// CaffNodeConfig is the config for the Caff Node -type CaffNodeConfig struct { - IsCaffNode bool - NextHotShotBlockNum uint64 - PollingHotShotPollingInterval time.Duration - HotShotUrls []string - L1EthRpc string - EspressoLightClientAddr string -} - // ValidateL1Config checks L1 config variables for errors. func (cfg *Config) ValidateL1Config(ctx context.Context, client L1Client) error { // Validate the L1 Client Chain ID diff --git a/op-node/service.go b/op-node/service.go index 5ad0d2f3abb..fbd56028592 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" + "github.com/ethereum-optimism/optimism/espresso" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/flags" @@ -216,7 +217,7 @@ func NewRollupConfigFromCLI(log log.Logger, ctx *cli.Context) (*rollup.Config, e applyCeloHardforks(rollupConfig) applyOverrides(ctx, rollupConfig) - rollupConfig.CaffNodeConfig = *NewCaffNodeConfig(ctx) + rollupConfig.CaffNodeConfig = espresso.ReadCLIConfig(ctx) return rollupConfig, nil } @@ -338,14 +339,3 @@ func NewSyncConfig(ctx *cli.Context, log log.Logger) (*sync.Config, error) { return cfg, nil } - -func NewCaffNodeConfig(ctx *cli.Context) *rollup.CaffNodeConfig { - return &rollup.CaffNodeConfig{ - IsCaffNode: ctx.Bool(flags.CaffNodeFlag.Name), - NextHotShotBlockNum: ctx.Uint64(flags.CaffNodeNextHotShotBlockNum.Name), - PollingHotShotPollingInterval: ctx.Duration(flags.CaffNodePollingHotShotPollingInterval.Name), - HotShotUrls: ctx.StringSlice(flags.CaffNodeHotShotUrls.Name), - L1EthRpc: ctx.String(flags.CaffNodeL1EthRpc.Name), - EspressoLightClientAddr: ctx.String(flags.CaffNodeEspressoLightClientAddr.Name), - } -}