diff --git a/op-batcher/batcher/channel_builder_test.go b/op-batcher/batcher/channel_builder_test.go index c45762fef4917..84cd1c7f7d885 100644 --- a/op-batcher/batcher/channel_builder_test.go +++ b/op-batcher/batcher/channel_builder_test.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/stretchr/testify/require" @@ -76,7 +77,7 @@ func newMiniL2BlockWithNumberParentAndL1Information(numTx int, l2Number *big.Int Number: big.NewInt(l1Number), Time: blockTime, }, nil, nil, trie.NewStackTrie(nil), types.DefaultBlockConfig) - l1InfoTx, err := derive.L1InfoDeposit(defaultTestRollupConfig, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), blockTime) + l1InfoTx, err := derive.L1InfoDeposit(defaultTestRollupConfig, params.MergedTestChainConfig, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), blockTime) if err != nil { panic(err) } diff --git a/op-chain-ops/cmd/check-ecotone/main.go b/op-chain-ops/cmd/check-ecotone/main.go index 37e590c47fa11..3c636dd46905e 100644 --- a/op-chain-ops/cmd/check-ecotone/main.go +++ b/op-chain-ops/cmd/check-ecotone/main.go @@ -512,7 +512,7 @@ func checkBlobTxDenial(ctx context.Context, env *actionEnv) error { return fmt.Errorf("the L1 block %s (time %d) is not ecotone yet", latestHeader.Hash(), latestHeader.Time) } - blobBaseFee := eth.CalcBlobFeeDefault(latestHeader) + blobBaseFee := eth.CalcBlobFeeCancun(*latestHeader.ExcessBlobGas) blobFeeCap := new(uint256.Int).Mul(uint256.NewInt(2), uint256.MustFromBig(blobBaseFee)) if blobFeeCap.Lt(uint256.NewInt(params.GWei)) { // ensure we meet 1 gwei geth tx-pool minimum blobFeeCap = uint256.NewInt(params.GWei) diff --git a/op-challenger/config/config_test.go b/op-challenger/config/config_test.go index 7602063b2d6bd..71944f26081c5 100644 --- a/op-challenger/config/config_test.go +++ b/op-challenger/config/config_test.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "runtime" + "slices" "testing" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" @@ -144,6 +145,28 @@ func validConfig(t *testing.T, traceType types.TraceType) Config { return cfg } +func validConfigWithNoNetworks(t *testing.T, traceType types.TraceType) Config { + cfg := validConfig(t, traceType) + + mutateVmConfig := func(cfg *vm.Config) { + cfg.Networks = nil + cfg.RollupConfigPaths = []string{"foo.json"} + cfg.L2GenesisPaths = []string{"genesis.json"} + cfg.L1GenesisPath = "bar.json" + cfg.DepsetConfigPath = "foo.json" + } + if slices.Contains(allCannonTraceTypes, traceType) { + mutateVmConfig(&cfg.Cannon) + } + if slices.Contains(asteriscTraceTypes, traceType) { + mutateVmConfig(&cfg.Asterisc) + } + if slices.Contains(asteriscKonaTraceTypes, traceType) { + mutateVmConfig(&cfg.AsteriscKona) + } + return cfg +} + // TestValidConfigIsValid checks that the config provided by validConfig is actually valid func TestValidConfigIsValid(t *testing.T) { for _, traceType := range types.TraceTypes { @@ -261,20 +284,14 @@ func TestCannonRequiredArgs(t *testing.T) { }) t.Run(fmt.Sprintf("TestCannonNetworkOrRollupConfigRequired-%v", traceType), func(t *testing.T) { - cfg := validConfig(t, traceType) - cfg.Cannon.Networks = nil + cfg := validConfigWithNoNetworks(t, traceType) cfg.Cannon.RollupConfigPaths = nil - cfg.Cannon.L2GenesisPaths = []string{"genesis.json"} - cfg.Cannon.DepsetConfigPath = "foo.json" require.ErrorIs(t, cfg.Check(), vm.ErrMissingRollupConfig) }) t.Run(fmt.Sprintf("TestCannonNetworkOrL2GenesisRequired-%v", traceType), func(t *testing.T) { - cfg := validConfig(t, traceType) - cfg.Cannon.Networks = nil - cfg.Cannon.RollupConfigPaths = []string{"foo.json"} + cfg := validConfigWithNoNetworks(t, traceType) cfg.Cannon.L2GenesisPaths = nil - cfg.Cannon.DepsetConfigPath = "foo.json" require.ErrorIs(t, cfg.Check(), vm.ErrMissingL2Genesis) }) @@ -354,6 +371,7 @@ func TestDepsetConfig(t *testing.T) { cfg := validConfig(t, traceType) cfg.Cannon.Networks = nil cfg.Cannon.RollupConfigPaths = []string{"foo.json"} + cfg.Cannon.L1GenesisPath = "bar.json" cfg.Cannon.L2GenesisPaths = []string{"genesis.json"} cfg.Cannon.DepsetConfigPath = "" require.NoError(t, cfg.Check()) @@ -366,6 +384,7 @@ func TestDepsetConfig(t *testing.T) { cfg := validConfig(t, traceType) cfg.AsteriscKona.Networks = nil cfg.AsteriscKona.RollupConfigPaths = []string{"foo.json"} + cfg.AsteriscKona.L1GenesisPath = "bar.json" cfg.AsteriscKona.L2GenesisPaths = []string{"genesis.json"} cfg.AsteriscKona.DepsetConfigPath = "" require.NoError(t, cfg.Check()) @@ -441,17 +460,13 @@ func TestAsteriscRequiredArgs(t *testing.T) { }) t.Run(fmt.Sprintf("TestAsteriscNetworkOrRollupConfigRequired-%v", traceType), func(t *testing.T) { - cfg := validConfig(t, traceType) - cfg.Asterisc.Networks = nil + cfg := validConfigWithNoNetworks(t, traceType) cfg.Asterisc.RollupConfigPaths = nil - cfg.Asterisc.L2GenesisPaths = []string{"genesis.json"} require.ErrorIs(t, cfg.Check(), vm.ErrMissingRollupConfig) }) t.Run(fmt.Sprintf("TestAsteriscNetworkOrL2GenesisRequired-%v", traceType), func(t *testing.T) { - cfg := validConfig(t, traceType) - cfg.Asterisc.Networks = nil - cfg.Asterisc.RollupConfigPaths = []string{"foo.json"} + cfg := validConfigWithNoNetworks(t, traceType) cfg.Asterisc.L2GenesisPaths = nil require.ErrorIs(t, cfg.Check(), vm.ErrMissingL2Genesis) }) @@ -557,17 +572,13 @@ func TestAsteriscKonaRequiredArgs(t *testing.T) { }) t.Run(fmt.Sprintf("TestAsteriscKonaNetworkOrRollupConfigRequired-%v", traceType), func(t *testing.T) { - cfg := validConfig(t, traceType) - cfg.AsteriscKona.Networks = nil + cfg := validConfigWithNoNetworks(t, traceType) cfg.AsteriscKona.RollupConfigPaths = nil - cfg.AsteriscKona.L2GenesisPaths = []string{"genesis.json"} require.ErrorIs(t, cfg.Check(), vm.ErrMissingRollupConfig) }) t.Run(fmt.Sprintf("TestAsteriscKonaNetworkOrL2GenesisRequired-%v", traceType), func(t *testing.T) { - cfg := validConfig(t, traceType) - cfg.AsteriscKona.Networks = nil - cfg.AsteriscKona.RollupConfigPaths = []string{"foo.json"} + cfg := validConfigWithNoNetworks(t, traceType) cfg.AsteriscKona.L2GenesisPaths = nil require.ErrorIs(t, cfg.Check(), vm.ErrMissingL2Genesis) }) diff --git a/op-challenger/flags/flags.go b/op-challenger/flags/flags.go index 235589c23327e..ee966af495033 100644 --- a/op-challenger/flags/flags.go +++ b/op-challenger/flags/flags.go @@ -44,6 +44,11 @@ var ( Usage: "Address of L1 Beacon API endpoint to use", EnvVars: prefixEnvVars("L1_BEACON"), } + L1GenesisFlag = &cli.StringFlag{ + Name: "l1-genesis-path", + Usage: "Path to the L1 genesis file. Only required if the L1 is not mainnet, sepolia, holesky, or hoodi.", + EnvVars: prefixEnvVars("L1_GENESIS_PATH"), + } SupervisorRpcFlag = &cli.StringFlag{ Name: "supervisor-rpc", Usage: "Provider URL for supervisor RPC", @@ -283,6 +288,7 @@ var optionalFlags = []cli.Flag{ GameWindowFlag, SelectiveClaimResolutionFlag, UnsafeAllowInvalidPrestate, + L1GenesisFlag, } func init() { @@ -625,6 +631,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro RollupRpc: ctx.String(RollupRpcFlag.Name), SupervisorRPC: ctx.String(SupervisorRpcFlag.Name), Cannon: vm.Config{ + L1GenesisPath: ctx.String(L1GenesisFlag.Name), VmType: types.TraceTypeCannon, L1: l1EthRpc, L1Beacon: l1Beacon, @@ -646,6 +653,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro CannonAbsolutePreStateBaseURL: cannonPreStatesURL, Datadir: ctx.String(DatadirFlag.Name), Asterisc: vm.Config{ + L1GenesisPath: ctx.String(L1GenesisFlag.Name), VmType: types.TraceTypeAsterisc, L1: l1EthRpc, L1Beacon: l1Beacon, @@ -664,6 +672,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro AsteriscAbsolutePreState: ctx.String(AsteriscPreStateFlag.Name), AsteriscAbsolutePreStateBaseURL: asteriscPreStatesURL, AsteriscKona: vm.Config{ + L1GenesisPath: ctx.String(L1GenesisFlag.Name), VmType: types.TraceTypeAsteriscKona, L1: l1EthRpc, L1Beacon: l1Beacon, diff --git a/op-challenger/game/fault/trace/vm/executor.go b/op-challenger/game/fault/trace/vm/executor.go index c6339a6a723d0..5f1484ea8fa02 100644 --- a/op-challenger/game/fault/trace/vm/executor.go +++ b/op-challenger/game/fault/trace/vm/executor.go @@ -57,6 +57,7 @@ type Config struct { Networks []string L2Custom bool RollupConfigPaths []string + L1GenesisPath string L2GenesisPaths []string DepsetConfigPath string } diff --git a/op-challenger/game/fault/trace/vm/kona_server_executor.go b/op-challenger/game/fault/trace/vm/kona_server_executor.go index 8f1e9b60872de..4ba2b5632a873 100644 --- a/op-challenger/game/fault/trace/vm/kona_server_executor.go +++ b/op-challenger/game/fault/trace/vm/kona_server_executor.go @@ -57,5 +57,9 @@ func (s *KonaExecutor) OracleCommand(cfg Config, dataDir string, inputs utils.Lo args = append(args, "--l2-chain-id", strconv.FormatUint(chainCfg.ChainID, 10)) } + if cfg.L1GenesisPath != "" { + args = append(args, "--l1-config-path", cfg.L1GenesisPath) + } + return args, nil } diff --git a/op-challenger/game/fault/trace/vm/kona_server_executor_test.go b/op-challenger/game/fault/trace/vm/kona_server_executor_test.go index 05413d8f4e038..458303de45835 100644 --- a/op-challenger/game/fault/trace/vm/kona_server_executor_test.go +++ b/op-challenger/game/fault/trace/vm/kona_server_executor_test.go @@ -13,11 +13,12 @@ import ( func TestKonaFillHostCommand(t *testing.T) { dir := "mockdir" cfg := Config{ - L1: "http://localhost:8888", - L1Beacon: "http://localhost:9000", - L2s: []string{"http://localhost:9999"}, - Server: "./bin/mockserver", - Networks: []string{"op-mainnet"}, + L1: "http://localhost:8888", + L1Beacon: "http://localhost:9000", + L2s: []string{"http://localhost:9999"}, + Server: "./bin/mockserver", + Networks: []string{"op-mainnet"}, + L1GenesisPath: "mockdir/l1-genesis-1.json", } inputs := utils.LocalGameInputs{ L1Head: common.Hash{0x11}, @@ -43,4 +44,5 @@ func TestKonaFillHostCommand(t *testing.T) { require.True(t, slices.Contains(args, "--l2-output-root")) require.True(t, slices.Contains(args, "--l2-claim")) require.True(t, slices.Contains(args, "--l2-block-number")) + require.True(t, slices.Contains(args, "--l1-config-path")) } diff --git a/op-challenger/game/fault/trace/vm/kona_super_server_executor.go b/op-challenger/game/fault/trace/vm/kona_super_server_executor.go index 03a2747501903..5aa347f70e5f7 100644 --- a/op-challenger/game/fault/trace/vm/kona_super_server_executor.go +++ b/op-challenger/game/fault/trace/vm/kona_super_server_executor.go @@ -50,5 +50,9 @@ func (s *KonaSuperExecutor) OracleCommand(cfg Config, dataDir string, inputs uti args = append(args, "--rollup-config-paths", strings.Join(cfg.RollupConfigPaths, ",")) } + if cfg.L1GenesisPath != "" { + args = append(args, "--l1-config-path", cfg.L1GenesisPath) + } + return args, nil } diff --git a/op-challenger/game/fault/trace/vm/op_program_server_executor.go b/op-challenger/game/fault/trace/vm/op_program_server_executor.go index f50196c1cb402..c56d3aa4b79d0 100644 --- a/op-challenger/game/fault/trace/vm/op_program_server_executor.go +++ b/op-challenger/game/fault/trace/vm/op_program_server_executor.go @@ -72,5 +72,8 @@ func (s *OpProgramServerExecutor) OracleCommand(cfg Config, dataDir string, inpu if cfg.L2Custom { args = append(args, "--l2.custom") } + if cfg.L1GenesisPath != "" { + args = append(args, "--l1.config", cfg.L1GenesisPath) + } return args, nil } diff --git a/op-challenger/game/fault/trace/vm/op_program_server_executor_test.go b/op-challenger/game/fault/trace/vm/op_program_server_executor_test.go index 7f9ce30a93a17..37fa6a2600733 100644 --- a/op-challenger/game/fault/trace/vm/op_program_server_executor_test.go +++ b/op-challenger/game/fault/trace/vm/op_program_server_executor_test.go @@ -33,10 +33,11 @@ func TestOpProgramFillHostCommand(t *testing.T) { oracleCommand := func(t *testing.T, lvl slog.Level, configModifier func(c *Config, inputs *utils.LocalGameInputs)) map[string]string { cfg := Config{ - L1: "http://localhost:8888", - L1Beacon: "http://localhost:9000", - L2s: []string{"http://localhost:9999", "http://localhost:9999/two"}, - Server: "./bin/mockserver", + L1: "http://localhost:8888", + L1Beacon: "http://localhost:9000", + L2s: []string{"http://localhost:9999", "http://localhost:9999/two"}, + Server: "./bin/mockserver", + L1GenesisPath: "mockdir/l1-genesis-1.json", } inputs := utils.LocalGameInputs{ L1Head: common.Hash{0x11}, @@ -60,6 +61,7 @@ func TestOpProgramFillHostCommand(t *testing.T) { require.Equal(t, inputs.L1Head.Hex(), pairs["--l1.head"]) require.Equal(t, inputs.L2Claim.Hex(), pairs["--l2.claim"]) require.Equal(t, inputs.L2SequenceNumber.String(), pairs["--l2.blocknumber"]) + require.Equal(t, cfg.L1GenesisPath, pairs["--l1.config"]) return pairs } diff --git a/op-devstack/shared/challenger/challenger.go b/op-devstack/shared/challenger/challenger.go index 2f5af4e08c11a..8ca63f7012821 100644 --- a/op-devstack/shared/challenger/challenger.go +++ b/op-devstack/shared/challenger/challenger.go @@ -52,7 +52,7 @@ func WithPrivKey(key *ecdsa.PrivateKey) Option { } } -func applyCannonConfig(c *config.Config, rollupCfgs []*rollup.Config, l2Geneses []*core.Genesis, prestateVariant PrestateVariant) error { +func applyCannonConfig(c *config.Config, rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis, prestateVariant PrestateVariant) error { root, err := findMonorepoRoot() if err != nil { return err @@ -79,6 +79,17 @@ func applyCannonConfig(c *config.Config, rollupCfgs []*rollup.Config, l2Geneses c.Cannon.L2GenesisPaths = append(c.Cannon.L2GenesisPaths, genesisFile) } + l1GenesisBytes, err := json.Marshal(l1Genesis) + if err != nil { + return fmt.Errorf("marshall l1 genesis config: %w", err) + } + l1GenesisFile := filepath.Join(c.Datadir, fmt.Sprintf("l1-genesis-%v.json", l1Genesis.Config.ChainID)) + err = os.WriteFile(l1GenesisFile, l1GenesisBytes, 0o644) + if err != nil { + return fmt.Errorf("write l1 genesis config: %w", err) + } + c.Cannon.L1GenesisPath = l1GenesisFile + for _, rollupCfg := range rollupCfgs { rollupBytes, err := json.Marshal(rollupCfg) if err != nil { @@ -101,9 +112,9 @@ func WithFactoryAddress(addr common.Address) Option { } } -func WithCannonConfig(rollupCfgs []*rollup.Config, l2Geneses []*core.Genesis, prestateVariant PrestateVariant) Option { +func WithCannonConfig(rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis, prestateVariant PrestateVariant) Option { return func(c *config.Config) error { - return applyCannonConfig(c, rollupCfgs, l2Geneses, prestateVariant) + return applyCannonConfig(c, rollupCfgs, l1Genesis, l2Geneses, prestateVariant) } } diff --git a/op-devstack/sysgo/l2_challenger.go b/op-devstack/sysgo/l2_challenger.go index db112b8b92f7e..59511dd486772 100644 --- a/op-devstack/sysgo/l2_challenger.go +++ b/op-devstack/sysgo/l2_challenger.go @@ -101,6 +101,12 @@ func WithL2ChallengerPostDeploy(orch *Orchestrator, challengerID stack.L2Challen l2NetIDs = append(l2NetIDs, l2Net.id) } + l1Net, ok := orch.l1Nets.Get(l1ELID.ChainID()) + if !ok { + require.Fail("l1 network not found") + } + l1Genesis := l1Net.genesis + dir := p.TempDir() var cfg *config.Config // If interop is scheduled, or if we cannot do the pre-interop connection, then set up with supervisor @@ -122,7 +128,7 @@ func WithL2ChallengerPostDeploy(orch *Orchestrator, challengerID stack.L2Challen shared.WithFactoryAddress(disputeGameFactoryAddr), shared.WithPrivKey(challengerSecret), shared.WithDepset(cluster.DepSet()), - shared.WithCannonConfig(rollupCfgs, l2Geneses, prestateVariant), + shared.WithCannonConfig(rollupCfgs, l1Genesis, l2Geneses, prestateVariant), shared.WithSuperCannonTraceType(), shared.WithSuperPermissionedTraceType(), ) @@ -146,7 +152,7 @@ func WithL2ChallengerPostDeploy(orch *Orchestrator, challengerID stack.L2Challen cfg, err = shared.NewPreInteropChallengerConfig(dir, l1EL.userRPC, l1CL.beaconHTTPAddr, l2CL.userRPC, l2EL.userRPC, shared.WithFactoryAddress(disputeGameFactoryAddr), shared.WithPrivKey(challengerSecret), - shared.WithCannonConfig(rollupCfgs, l2Geneses, prestateVariant), + shared.WithCannonConfig(rollupCfgs, l1Genesis, l2Geneses, prestateVariant), shared.WithCannonTraceType(), shared.WithPermissionedTraceType(), shared.WithFastGames(), diff --git a/op-devstack/sysgo/test_sequencer.go b/op-devstack/sysgo/test_sequencer.go index ffc0a52f6dfe1..56f2e7361addf 100644 --- a/op-devstack/sysgo/test_sequencer.go +++ b/op-devstack/sysgo/test_sequencer.go @@ -109,10 +109,14 @@ func WithTestSequencer(testSequencerID stack.TestSequencerID, l1CLID stack.L1CLN l2SequencerID := seqtypes.SequencerID(fmt.Sprintf("test-seq-%s", l2CLID.ChainID())) l1SequencerID := seqtypes.SequencerID(fmt.Sprintf("test-seq-%s", l1ELID.ChainID())) + l1Net, ok := orch.l1Nets.Get(l1ELID.ChainID()) + require.True(ok, "l1 net required") + v := &config.Ensemble{ Builders: map[seqtypes.BuilderID]*config.BuilderEntry{ bid_L2: { Standard: &standardbuilder.Config{ + L1ChainConfig: l1Net.genesis.Config, L1EL: endpoint.MustRPC{ Value: endpoint.HttpURL(l1EL.userRPC), }, diff --git a/op-e2e/actions/altda/altda_test.go b/op-e2e/actions/altda/altda_test.go index 4f63391990e98..bb8aed85b63df 100644 --- a/op-e2e/actions/altda/altda_test.go +++ b/op-e2e/actions/altda/altda_test.go @@ -92,7 +92,7 @@ func NewL2AltDA(t helpers.Testing, params ...AltDAParam) *L2AltDA { storage := &altda.DAErrFaker{Client: altda.NewMockDAClient(log)} daMgr := altda.NewAltDAWithStorage(log, altDACfg, storage, &altda.NoopMetrics{}) - sequencer := helpers.NewL2Sequencer(t, log, l1F, miner.BlobStore(), daMgr, engCl, sd.RollupCfg, sd.DependencySet, 0) + sequencer := helpers.NewL2Sequencer(t, log, l1F, miner.BlobStore(), daMgr, engCl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, 0) miner.ActL1SetFeeRecipient(common.Address{'A'}) sequencer.ActL2PipelineFull(t) @@ -150,7 +150,7 @@ func (a *L2AltDA) NewVerifier(t helpers.Testing) *helpers.L2Verifier { daMgr := altda.NewAltDAWithStorage(a.log, a.altDACfg, a.storage, &altda.NoopMetrics{}) - verifier := helpers.NewL2Verifier(t, a.log, l1F, a.miner.BlobStore(), daMgr, engCl, a.sd.RollupCfg, a.sd.DependencySet, &sync.Config{}, safedb.Disabled) + verifier := helpers.NewL2Verifier(t, a.log, l1F, a.miner.BlobStore(), daMgr, engCl, a.sd.RollupCfg, a.sd.L1Cfg.Config, a.sd.DependencySet, &sync.Config{}, safedb.Disabled) return verifier } diff --git a/op-e2e/actions/derivation/batch_queue_test.go b/op-e2e/actions/derivation/batch_queue_test.go index bb32d10284ec9..65ced92ea1dfa 100644 --- a/op-e2e/actions/derivation/batch_queue_test.go +++ b/op-e2e/actions/derivation/batch_queue_test.go @@ -95,7 +95,7 @@ func TestDeriveChainFromNearL1Genesis(gt *testing.T) { l2Cl, err := sources.NewEngineClient(seqEngine.RPCClient(), logger, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(gt, err) verifier := helpers.NewL2Verifier(t, logger, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), altda.Disabled, - l2Cl, sd.RollupCfg, sd.DependencySet, &sync.Config{}, safedb.Disabled) + l2Cl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, &sync.Config{}, safedb.Disabled) verifier.ActL2PipelineFull(t) // Should not get stuck in a reset loop forever require.EqualValues(gt, l2BlockNum, seqEngine.L2Chain().CurrentSafeBlock().Number.Uint64()) require.EqualValues(gt, l2BlockNum, seqEngine.L2Chain().CurrentFinalBlock().Number.Uint64()) diff --git a/op-e2e/actions/derivation/reorg_test.go b/op-e2e/actions/derivation/reorg_test.go index 36d2458b425e7..b1390952a136f 100644 --- a/op-e2e/actions/derivation/reorg_test.go +++ b/op-e2e/actions/derivation/reorg_test.go @@ -596,7 +596,7 @@ func RestartOpGeth(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { engRpc := &rpcWrapper{seqEng.RPCClient()} l2Cl, err := sources.NewEngineClient(engRpc, log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) - sequencer := actionsHelpers.NewL2Sequencer(t, log, l1F, miner.BlobStore(), altda.Disabled, l2Cl, sd.RollupCfg, sd.DependencySet, 0) + sequencer := actionsHelpers.NewL2Sequencer(t, log, l1F, miner.BlobStore(), altda.Disabled, l2Cl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, 0) batcher := actionsHelpers.NewL2Batcher(log, sd.RollupCfg, actionsHelpers.DefaultBatcherCfg(dp), sequencer.RollupClient(), miner.EthClient(), seqEng.EthClient(), seqEng.EngineClient(t, sd.RollupCfg)) @@ -684,7 +684,7 @@ func ConflictingL2Blocks(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { require.NoError(t, err) l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard)) require.NoError(t, err) - altSequencer := actionsHelpers.NewL2Sequencer(t, log, l1F, miner.BlobStore(), altda.Disabled, altSeqEngCl, sd.RollupCfg, sd.DependencySet, 0) + altSequencer := actionsHelpers.NewL2Sequencer(t, log, l1F, miner.BlobStore(), altda.Disabled, altSeqEngCl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, 0) altBatcher := actionsHelpers.NewL2Batcher(log, sd.RollupCfg, actionsHelpers.DefaultBatcherCfg(dp), altSequencer.RollupClient(), miner.EthClient(), altSeqEng.EthClient(), altSeqEng.EngineClient(t, sd.RollupCfg)) diff --git a/op-e2e/actions/helpers/l2_batcher.go b/op-e2e/actions/helpers/l2_batcher.go index f3799e79b7d8e..05373733e0851 100644 --- a/op-e2e/actions/helpers/l2_batcher.go +++ b/op-e2e/actions/helpers/l2_batcher.go @@ -42,6 +42,7 @@ type L1TxAPI interface { PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) SendTransaction(ctx context.Context, tx *types.Transaction) error + BlobBaseFee(ctx context.Context) (*big.Int, error) } type AltDAInputSetter interface { @@ -389,7 +390,8 @@ func (s *L2Batcher) ActL2BatchSubmitRaw(t Testing, payload []byte, txOpts ...fun sidecar, blobHashes, err := txmgr.MakeSidecar([]*eth.Blob{&b}) require.NoError(t, err) require.NotNil(t, pendingHeader.ExcessBlobGas, "need L1 header with 4844 properties") - blobBaseFee := eth.CalcBlobFeeDefault(pendingHeader) + blobBaseFee, err := s.l1.BlobBaseFee(t.Ctx()) + require.NoError(t, err, "need blob base fee") blobFeeCap := new(uint256.Int).Mul(uint256.NewInt(2), uint256.MustFromBig(blobBaseFee)) if blobFeeCap.Lt(uint256.NewInt(params.GWei)) { // ensure we meet 1 gwei geth tx-pool minimum blobFeeCap = uint256.NewInt(params.GWei) @@ -472,8 +474,8 @@ func (s *L2Batcher) ActL2BatchSubmitMultiBlob(t Testing, numBlobs int) { sidecar, blobHashes, err := txmgr.MakeSidecar(blobs) require.NoError(t, err) - require.NotNil(t, pendingHeader.ExcessBlobGas, "need L1 header with 4844 properties") - blobBaseFee := eth.CalcBlobFeeDefault(pendingHeader) + blobBaseFee, err := s.l1.BlobBaseFee(t.Ctx()) + require.NoError(t, err, "need blob base fee") blobFeeCap := new(uint256.Int).Mul(uint256.NewInt(2), uint256.MustFromBig(blobBaseFee)) if blobFeeCap.Lt(uint256.NewInt(params.GWei)) { // ensure we meet 1 gwei geth tx-pool minimum blobFeeCap = uint256.NewInt(params.GWei) diff --git a/op-e2e/actions/helpers/l2_sequencer.go b/op-e2e/actions/helpers/l2_sequencer.go index e9dbaca457923..b32ec0bb873e8 100644 --- a/op-e2e/actions/helpers/l2_sequencer.go +++ b/op-e2e/actions/helpers/l2_sequencer.go @@ -7,6 +7,7 @@ import ( "golang.org/x/time/rate" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-node/config" "github.com/ethereum-optimism/optimism/op-node/metrics" @@ -56,10 +57,11 @@ type L2Sequencer struct { } func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, blobSrc derive.L1BlobsFetcher, - altDASrc driver.AltDAIface, eng L2API, cfg *rollup.Config, depSet depset.DependencySet, seqConfDepth uint64, + altDASrc driver.AltDAIface, eng L2API, cfg *rollup.Config, l1ChainConfig *params.ChainConfig, + depSet depset.DependencySet, seqConfDepth uint64, ) *L2Sequencer { - ver := NewL2Verifier(t, log, l1, blobSrc, altDASrc, eng, cfg, depSet, &sync.Config{}, safedb.Disabled) - attrBuilder := derive.NewFetchingAttributesBuilder(cfg, depSet, l1, eng) + ver := NewL2Verifier(t, log, l1, blobSrc, altDASrc, eng, cfg, l1ChainConfig, depSet, &sync.Config{}, safedb.Disabled) + attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1ChainConfig, depSet, l1, eng) seqConfDepthL1 := confdepth.NewConfDepth(seqConfDepth, ver.syncStatus.L1Head, l1) originSelector := sequencing.NewL1OriginSelector(t.Ctx(), log, cfg, seqConfDepthL1) l1OriginSelector := &MockL1OriginSelector{ diff --git a/op-e2e/actions/helpers/l2_verifier.go b/op-e2e/actions/helpers/l2_verifier.go index a22048c578403..1acb59a2b6a7c 100644 --- a/op-e2e/actions/helpers/l2_verifier.go +++ b/op-e2e/actions/helpers/l2_verifier.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" gnode "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-node/node" @@ -73,7 +74,8 @@ type L2Verifier struct { L2PipelineIdle bool l2Building bool - RollupCfg *rollup.Config + L1ChainConfig *params.ChainConfig + RollupCfg *rollup.Config rpc *rpc.Server @@ -109,7 +111,8 @@ type safeDB interface { func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher, blobsSrc derive.L1BlobsFetcher, altDASrc driver.AltDAIface, - eng L2API, cfg *rollup.Config, depSet depset.DependencySet, syncCfg *sync.Config, safeHeadListener safeDB, + eng L2API, cfg *rollup.Config, l1ChainConfig *params.ChainConfig, + depSet depset.DependencySet, syncCfg *sync.Config, safeHeadListener safeDB, ) *L2Verifier { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -162,7 +165,7 @@ func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher, attributes.NewAttributesHandler(log, cfg, ctx, eng), opts) indexingMode := interopSys != nil - pipeline := derive.NewDerivationPipeline(log, cfg, depSet, l1, blobsSrc, altDASrc, eng, metrics, indexingMode) + pipeline := derive.NewDerivationPipeline(log, cfg, depSet, l1, blobsSrc, altDASrc, eng, metrics, indexingMode, l1ChainConfig) sys.Register("pipeline", derive.NewPipelineDeriver(ctx, pipeline), opts) testActionEmitter := sys.Register("test-action", nil, opts) @@ -200,6 +203,7 @@ func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher, syncStatus: syncStatusTracker, L2PipelineIdle: true, l2Building: false, + L1ChainConfig: l1ChainConfig, RollupCfg: cfg, rpc: rpc.NewServer(), synchronousEvents: testActionEmitter, diff --git a/op-e2e/actions/helpers/setups.go b/op-e2e/actions/helpers/setups.go index d981bf223459f..375c88bbff1ea 100644 --- a/op-e2e/actions/helpers/setups.go +++ b/op-e2e/actions/helpers/setups.go @@ -29,7 +29,7 @@ func SetupSequencerTest(t Testing, sd *e2eutils.SetupData, log log.Logger, opts l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) - sequencer := NewL2Sequencer(t, log.New("role", "sequencer"), l1F, miner.BlobStore(), altda.Disabled, l2Cl, sd.RollupCfg, sd.DependencySet, 0) + sequencer := NewL2Sequencer(t, log.New("role", "sequencer"), l1F, miner.BlobStore(), altda.Disabled, l2Cl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, 0) return miner, engine, sequencer } @@ -42,7 +42,7 @@ func SetupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger, jwtPath := e2eutils.WriteDefaultJWT(t) engine := NewL2Engine(t, log.New("role", "verifier-engine"), sd.L2Cfg, jwtPath, EngineWithP2P()) engCl := engine.EngineClient(t, sd.RollupCfg) - verifier := NewL2Verifier(t, log.New("role", "verifier"), l1F, blobSrc, altda.Disabled, engCl, sd.RollupCfg, sd.DependencySet, syncCfg, cfg.SafeHeadListener) + verifier := NewL2Verifier(t, log.New("role", "verifier"), l1F, blobSrc, altda.Disabled, engCl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, syncCfg, cfg.SafeHeadListener) return engine, verifier } diff --git a/op-e2e/actions/interop/dsl/interop.go b/op-e2e/actions/interop/dsl/interop.go index 0b70b6e5f7d46..21a08c3456b4c 100644 --- a/op-e2e/actions/interop/dsl/interop.go +++ b/op-e2e/actions/interop/dsl/interop.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum-optimism/optimism/op-supervisor/supervisor" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" altda "github.com/ethereum-optimism/optimism/op-alt-da" @@ -42,6 +43,7 @@ type Chain struct { ChainID eth.ChainID RollupCfg *rollup.Config + L1ChainConfig *params.ChainConfig DependencySet depset.DependencySet L2Genesis *core.Genesis BatcherAddr common.Address @@ -204,8 +206,8 @@ func (is *InteropSetup) CreateActors() *InteropActors { is.T.Cleanup(func() { require.NoError(is.T, supervisorAPI.backend.Stop(context.Background())) }) - chainA := createL2Services(is.T, is.Log, l1Miner, is.Keys, is.Out.L2s["900200"], is.CfgSet) - chainB := createL2Services(is.T, is.Log, l1Miner, is.Keys, is.Out.L2s["900201"], is.CfgSet) + chainA := createL2Services(is.T, is.Log, l1Miner, is.Keys, is.Out.L2s["900200"], is.CfgSet, is.Out.L1.Genesis.Config) + chainB := createL2Services(is.T, is.Log, l1Miner, is.Keys, is.Out.L2s["900201"], is.CfgSet, is.Out.L1.Genesis.Config) // Hook up L2 RPCs to supervisor, to fetch event data from srcA := chainA.Sequencer.InteropSyncNode(is.T) srcB := chainB.Sequencer.InteropSyncNode(is.T) @@ -292,6 +294,7 @@ func createL2Services( keys devkeys.Keys, output *interopgen.L2Output, depSet depset.DependencySet, + l1ChainConfig *params.ChainConfig, ) *Chain { logger = logger.New("chain", output.Genesis.Config.ChainID) @@ -307,8 +310,7 @@ func createL2Services( require.NoError(t, err) seq := helpers.NewL2Sequencer(t, logger.New("role", "sequencer"), l1F, - l1Miner.BlobStore(), altda.Disabled, seqCl, output.RollupCfg, depSet, - 0) + l1Miner.BlobStore(), altda.Disabled, seqCl, output.RollupCfg, l1ChainConfig, depSet, 0) batcherKey, err := keys.Secret(devkeys.ChainOperatorKey{ ChainID: output.Genesis.Config.ChainID, @@ -330,6 +332,7 @@ func createL2Services( return &Chain{ ChainID: eth.ChainIDFromBig(output.Genesis.Config.ChainID), RollupCfg: output.RollupCfg, + L1ChainConfig: l1ChainConfig, DependencySet: depSet, L2Genesis: output.Genesis, BatcherAddr: crypto.PubkeyToAddress(batcherKey.PublicKey), diff --git a/op-e2e/actions/interop/proofs_test.go b/op-e2e/actions/interop/proofs_test.go index 7b5e0487e13cf..a680f0dd8f398 100644 --- a/op-e2e/actions/interop/proofs_test.go +++ b/op-e2e/actions/interop/proofs_test.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" ) @@ -1498,7 +1499,7 @@ func WithInteropEnabled(t helpers.StatefulTesting, actors *dsl.InteropActors, de f.DependencySet = depSet for _, chain := range []*dsl.Chain{actors.ChainA, actors.ChainB} { - verifier, canonicalOnlyEngine := createVerifierWithOnlyCanonicalBlocks(t, actors.L1Miner, chain) + verifier, canonicalOnlyEngine := createVerifierWithOnlyCanonicalBlocks(t, actors.L1Miner, chain, chain.L1ChainConfig) f.L2Sources = append(f.L2Sources, &fpHelpers.FaultProofProgramL2Source{ Node: verifier, Engine: canonicalOnlyEngine, @@ -1510,7 +1511,7 @@ func WithInteropEnabled(t helpers.StatefulTesting, actors *dsl.InteropActors, de // createVerifierWithOnlyCanonicalBlocks creates a new L2Verifier and associated L2Engine that only has the canonical // blocks from chain in its database. Non-canonical blocks, their world state, receipts and other data are not available -func createVerifierWithOnlyCanonicalBlocks(t helpers.StatefulTesting, l1Miner *helpers.L1Miner, chain *dsl.Chain) (*helpers.L2Verifier, *helpers.L2Engine) { +func createVerifierWithOnlyCanonicalBlocks(t helpers.StatefulTesting, l1Miner *helpers.L1Miner, chain *dsl.Chain, l1ChainConfig *params.ChainConfig) (*helpers.L2Verifier, *helpers.L2Engine) { jwtPath := e2eutils.WriteDefaultJWT(t) canonicalOnlyEngine := helpers.NewL2Engine(t, testlog.Logger(t, log.LvlInfo).New("role", "canonicalOnlyEngine"), chain.L2Genesis, jwtPath) head := chain.Sequencer.L2Unsafe() @@ -1548,9 +1549,11 @@ func createVerifierWithOnlyCanonicalBlocks(t helpers.StatefulTesting, l1Miner *h altda.Disabled, canonicalOnlyEngine.EngineClient(t, chain.RollupCfg), chain.RollupCfg, + l1ChainConfig, chain.DependencySet, &sync2.Config{}, - safedb.Disabled) + safedb.Disabled, + ) return verifier, canonicalOnlyEngine } diff --git a/op-e2e/actions/proofs/block_data_hint_test.go b/op-e2e/actions/proofs/block_data_hint_test.go index 2b47bb0ef9835..5a63a7abdaa05 100644 --- a/op-e2e/actions/proofs/block_data_hint_test.go +++ b/op-e2e/actions/proofs/block_data_hint_test.go @@ -152,6 +152,7 @@ func createVerifier(t actionsHelpers.Testing, env *helpers.L2FaultProofEnv) (*ac altda.Disabled, l2EngineCl, env.Sd.RollupCfg, + env.Sd.L1Cfg.Config, env.Sd.DependencySet, &sync.Config{}, safedb.Disabled, diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index 2c30591d4cec5..c6065985af703 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -72,7 +72,7 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut l2EngineCl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) - sequencer := helpers.NewL2Sequencer(t, log.New("role", "sequencer"), l1Cl, miner.BlobStore(), altda.Disabled, l2EngineCl, sd.RollupCfg, sd.DependencySet, 0) + sequencer := helpers.NewL2Sequencer(t, log.New("role", "sequencer"), l1Cl, miner.BlobStore(), altda.Disabled, l2EngineCl, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, 0) miner.ActL1SetFeeRecipient(common.Address{0xCA, 0xFE, 0xBA, 0xBE}) sequencer.ActL2PipelineFull(t) engCl := engine.EngineClient(t, sd.RollupCfg) @@ -210,13 +210,15 @@ func NewOpProgramCfg( fi *FixtureInputs, ) *config.Config { var rollupConfigs []*rollup.Config - var chainConfigs []*params.ChainConfig + var l2chainConfigs []*params.ChainConfig + var l1chainConfig *params.ChainConfig for _, source := range fi.L2Sources { rollupConfigs = append(rollupConfigs, source.Node.RollupCfg) - chainConfigs = append(chainConfigs, source.ChainConfig) + l2chainConfigs = append(l2chainConfigs, source.ChainConfig) + l1chainConfig = source.Node.L1ChainConfig } - dfault := config.NewConfig(rollupConfigs, chainConfigs, fi.L1Head, fi.L2Head, fi.L2OutputRoot, fi.L2Claim, fi.L2BlockNumber) + dfault := config.NewConfig(rollupConfigs, l2chainConfigs, l1chainConfig, fi.L1Head, fi.L2Head, fi.L2OutputRoot, fi.L2Claim, fi.L2BlockNumber) dfault.L2ChainID = boot.CustomChainIDIndicator if fi.InteropEnabled { dfault.AgreedPrestate = fi.AgreedPrestate diff --git a/op-e2e/actions/proofs/helpers/kona.go b/op-e2e/actions/proofs/helpers/kona.go index 8187e78843998..7d5996ab14af6 100644 --- a/op-e2e/actions/proofs/helpers/kona.go +++ b/op-e2e/actions/proofs/helpers/kona.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-program/client/claim" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" ) @@ -27,10 +28,25 @@ func IsKonaConfigured() bool { return konaHostPath != "" } +func writeConfigs[T any](t helpers.Testing, workDir string, name string, cfg []*T, cfgPaths []string) { + for i, cfg := range cfg { + cfgPath := filepath.Join(workDir, fmt.Sprintf("%s_%d.json", name, i)) + writeConfig(t, workDir, name, cfg, cfgPath) + cfgPaths[i] = cfgPath + } +} + +func writeConfig[T any](t helpers.Testing, workDir string, name string, cfg *T, cfgPath string) { + ser, err := json.Marshal(cfg) + require.NoError(t, err) + require.NoError(t, os.WriteFile(cfgPath, ser, fs.ModePerm)) +} + func RunKonaNative( t helpers.Testing, workDir string, rollupCfgs []*rollup.Config, + l1chainConfig *params.ChainConfig, l1Rpc string, l1BeaconRpc string, l2Rpcs []string, @@ -38,14 +54,11 @@ func RunKonaNative( ) error { // Write rollup config to tempdir. rollupCfgPaths := make([]string, len(rollupCfgs)) - for i, cfg := range rollupCfgs { - rollupConfigPath := filepath.Join(workDir, fmt.Sprintf("rollup_%d.json", i)) - ser, err := json.Marshal(cfg) - require.NoError(t, err) - require.NoError(t, os.WriteFile(rollupConfigPath, ser, fs.ModePerm)) + writeConfigs(t, workDir, "rollup", rollupCfgs, rollupCfgPaths) - rollupCfgPaths[i] = rollupConfigPath - } + // Write l1 chain config to tempdir. + l1chainConfigPath := filepath.Join(workDir, "l1chain.json") + writeConfig(t, workDir, "l1chain", l1chainConfig, l1chainConfigPath) // Run the fault proof program from the state transition from L2 block L2Blocknumber - 1 -> L2BlockNumber. vmCfg := vm.Config{ @@ -53,6 +66,7 @@ func RunKonaNative( L1Beacon: l1BeaconRpc, L2s: l2Rpcs, RollupConfigPaths: rollupCfgPaths, + L1GenesisPath: l1chainConfigPath, Server: konaHostPath, } inputs := utils.LocalGameInputs{ diff --git a/op-e2e/actions/proofs/helpers/runner.go b/op-e2e/actions/proofs/helpers/runner.go index 8beae9abf5b93..9ae8d408faffb 100644 --- a/op-e2e/actions/proofs/helpers/runner.go +++ b/op-e2e/actions/proofs/helpers/runner.go @@ -83,13 +83,14 @@ func RunFaultProofProgram(t helpers.Testing, logger log.Logger, l1 *helpers.L1Mi defer fakeBeacon.Close() rollupCfgs := make([]*rollup.Config, 0, len(fixtureInputs.L2Sources)) + l1chainConfig := l1.L1Chain().Config() l2Endpoints := make([]string, 0, len(fixtureInputs.L2Sources)) for _, source := range fixtureInputs.L2Sources { rollupCfgs = append(rollupCfgs, source.Node.RollupCfg) l2Endpoints = append(l2Endpoints, source.Engine.HTTPEndpoint()) } - err = RunKonaNative(t, workDir, rollupCfgs, l1.HTTPEndpoint(), fakeBeacon.BeaconAddr(), l2Endpoints, *fixtureInputs) + err = RunKonaNative(t, workDir, rollupCfgs, l1chainConfig, l1.HTTPEndpoint(), fakeBeacon.BeaconAddr(), l2Endpoints, *fixtureInputs) checkResult(t, err) } else { programCfg := NewOpProgramCfg(fixtureInputs) diff --git a/op-e2e/actions/proofs/l1_prague_fork_test.go b/op-e2e/actions/proofs/l1_prague_fork_test.go index d9fcaf47aa51e..532ff8ec834ce 100644 --- a/op-e2e/actions/proofs/l1_prague_fork_test.go +++ b/op-e2e/actions/proofs/l1_prague_fork_test.go @@ -92,9 +92,7 @@ func Test_ProgramAction_PragueForkAfterGenesis(gt *testing.T) { checkL1BlockBlobBaseFee := func(t actionsHelpers.StatefulTesting, l2Block eth.L2BlockRef) { l1BlockID := l2Block.L1Origin l1BlockHeader := miner.L1Chain().GetHeaderByHash(l1BlockID.Hash) - expectedBbf := eth.CalcBlobFeeDefault(l1BlockHeader) - upstreamExpectedBbf := eip4844.CalcBlobFee(env.Sd.L1Cfg.Config, l1BlockHeader) - require.Equal(t, expectedBbf.Uint64(), upstreamExpectedBbf.Uint64(), "expected blob base fee should match upstream calculation") + expectedBbf := eip4844.CalcBlobFee(env.Sd.L1Cfg.Config, l1BlockHeader) bbf, err := l1Block.BlobBaseFee(&bind.CallOpts{BlockHash: l2Block.Hash}) require.NoError(t, err, "failed to get blob base fee") require.Equal(t, expectedBbf.Uint64(), bbf.Uint64(), "l1Block blob base fee does not match expectation, l1BlockNum %d, l2BlockNum %d", l1BlockID.Number, l2Block.Number) diff --git a/op-e2e/actions/proofs/pectra_blob_schedule_test.go b/op-e2e/actions/proofs/pectra_blob_schedule_test.go index 87571b050374e..71b9bba7b10f4 100644 --- a/op-e2e/actions/proofs/pectra_blob_schedule_test.go +++ b/op-e2e/actions/proofs/pectra_blob_schedule_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" ) type pectraBlobScheduleTestCfg struct { @@ -82,7 +83,7 @@ func testPectraBlobSchedule(gt *testing.T, testCfg *helpers.TestCfg[any]) { sequencer.ActBuildToL1HeadUnsafe(t) cancunBBF1 := eth.CalcBlobFeeCancun(*l1_1.ExcessBlobGas) - pragueBBF1 := eth.CalcBlobFeeDefault(l1_1) + pragueBBF1 := eip4844.CalcBlobFee(env.Sd.L1Cfg.Config, l1_1) // Make sure they differ. require.Less(t, pragueBBF1.Uint64(), cancunBBF1.Uint64()) opts := &bind.CallOpts{} @@ -113,7 +114,7 @@ func testPectraBlobSchedule(gt *testing.T, testCfg *helpers.TestCfg[any]) { sequencer.ActBuildToL1HeadUnsafe(t) cancunBBF2 := eth.CalcBlobFeeCancun(*l1_2.ExcessBlobGas) - pragueBBF2 := eth.CalcBlobFeeDefault(l1_2) + pragueBBF2 := eip4844.CalcBlobFee(env.Sd.L1Cfg.Config, l1_2) require.Less(t, pragueBBF2.Uint64(), cancunBBF2.Uint64()) bbf2, err := l1Block.BlobBaseFee(opts) require.NoError(t, err) diff --git a/op-e2e/actions/sequencer/l2_sequencer_test.go b/op-e2e/actions/sequencer/l2_sequencer_test.go index 7a48d24a5c286..f4f564b3b4018 100644 --- a/op-e2e/actions/sequencer/l2_sequencer_test.go +++ b/op-e2e/actions/sequencer/l2_sequencer_test.go @@ -180,7 +180,7 @@ func TestL2SequencerAPI(gt *testing.T) { l2Cl := seqEng.EngineClient(t, cfg) // Prepare a block - fb := derive.NewFetchingAttributesBuilder(cfg, sd.DependencySet, l1Cl, l2Cl) + fb := derive.NewFetchingAttributesBuilder(cfg, sd.L1Cfg.Config, sd.DependencySet, l1Cl, l2Cl) parent, err := l2Cl.L2BlockRefByLabel(t.Ctx(), eth.Unsafe) require.NoError(t, err) l1Origin := parent.L1Origin // repeat the L1 origin diff --git a/op-e2e/actions/sync/sync_test.go b/op-e2e/actions/sync/sync_test.go index 86fb80e939846..49f66e2a13057 100644 --- a/op-e2e/actions/sync/sync_test.go +++ b/op-e2e/actions/sync/sync_test.go @@ -821,7 +821,7 @@ func TestELSyncTransitionsToCLSyncAfterNodeRestart(gt *testing.T) { PrepareELSyncedNode(t, miner, sequencer, seqEng, verifier, verEng, seqEngCl, batcher, dp) // Create a new verifier which is essentially a new op-node with the sync mode of ELSync and default geth engine kind. - verifier = actionsHelpers.NewL2Verifier(t, captureLog, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), altda.Disabled, verifier.Eng, sd.RollupCfg, sd.DependencySet, &sync.Config{SyncMode: sync.ELSync}, actionsHelpers.DefaultVerifierCfg().SafeHeadListener) + verifier = actionsHelpers.NewL2Verifier(t, captureLog, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), altda.Disabled, verifier.Eng, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, &sync.Config{SyncMode: sync.ELSync}, actionsHelpers.DefaultVerifierCfg().SafeHeadListener) // Build another 10 L1 blocks on the sequencer for i := 0; i < 10; i++ { @@ -863,7 +863,7 @@ func TestForcedELSyncCLAfterNodeRestart(gt *testing.T) { PrepareELSyncedNode(t, miner, sequencer, seqEng, verifier, verEng, seqEngCl, batcher, dp) // Create a new verifier which is essentially a new op-node with the sync mode of ELSync and erigon engine kind. - verifier2 := actionsHelpers.NewL2Verifier(t, captureLog, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), altda.Disabled, verifier.Eng, sd.RollupCfg, sd.DependencySet, &sync.Config{SyncMode: sync.ELSync, SupportsPostFinalizationELSync: true}, actionsHelpers.DefaultVerifierCfg().SafeHeadListener) + verifier2 := actionsHelpers.NewL2Verifier(t, captureLog, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), altda.Disabled, verifier.Eng, sd.RollupCfg, sd.L1Cfg.Config, sd.DependencySet, &sync.Config{SyncMode: sync.ELSync, SupportsPostFinalizationELSync: true}, actionsHelpers.DefaultVerifierCfg().SafeHeadListener) // Build another 10 L1 blocks on the sequencer for i := 0; i < 10; i++ { diff --git a/op-e2e/e2eutils/challenger/helper.go b/op-e2e/e2eutils/challenger/helper.go index fbe61f691a55b..aa3b7d0cdeaaf 100644 --- a/op-e2e/e2eutils/challenger/helper.go +++ b/op-e2e/e2eutils/challenger/helper.go @@ -43,6 +43,7 @@ type EndpointProvider interface { type System interface { RollupCfgs() []*rollup.Config + L1Genesis() *core.Genesis L2Geneses() []*core.Genesis PrestateVariant() shared.PrestateVariant } @@ -121,21 +122,21 @@ func handleOptError(t *testing.T, opt shared.Option) Option { } func WithCannon(t *testing.T, system System) Option { return func(c *config.Config) { - handleOptError(t, shared.WithCannonConfig(system.RollupCfgs(), system.L2Geneses(), system.PrestateVariant()))(c) + handleOptError(t, shared.WithCannonConfig(system.RollupCfgs(), system.L1Genesis(), system.L2Geneses(), system.PrestateVariant()))(c) handleOptError(t, shared.WithCannonTraceType())(c) } } func WithPermissioned(t *testing.T, system System) Option { return func(c *config.Config) { - handleOptError(t, shared.WithCannonConfig(system.RollupCfgs(), system.L2Geneses(), system.PrestateVariant()))(c) + handleOptError(t, shared.WithCannonConfig(system.RollupCfgs(), system.L1Genesis(), system.L2Geneses(), system.PrestateVariant()))(c) handleOptError(t, shared.WithPermissionedTraceType())(c) } } func WithSuperCannon(t *testing.T, system System) Option { return func(c *config.Config) { - handleOptError(t, shared.WithCannonConfig(system.RollupCfgs(), system.L2Geneses(), system.PrestateVariant()))(c) + handleOptError(t, shared.WithCannonConfig(system.RollupCfgs(), system.L1Genesis(), system.L2Geneses(), system.PrestateVariant()))(c) handleOptError(t, shared.WithSuperCannonTraceType())(c) } } diff --git a/op-e2e/e2eutils/disputegame/helper.go b/op-e2e/e2eutils/disputegame/helper.go index d3b061f1f9857..b98a200b639b7 100644 --- a/op-e2e/e2eutils/disputegame/helper.go +++ b/op-e2e/e2eutils/disputegame/helper.go @@ -89,6 +89,7 @@ type DisputeSystem interface { DisputeGameFactoryAddr() common.Address RollupCfgs() []*rollup.Config DependencySet() *depset.StaticConfigDependencySet + L1Genesis() *core.Genesis L2Geneses() []*core.Genesis PrestateVariant() shared.PrestateVariant diff --git a/op-e2e/e2eutils/disputegame/super_dispute_system.go b/op-e2e/e2eutils/disputegame/super_dispute_system.go index f13ade2158a7a..f3093066cb661 100644 --- a/op-e2e/e2eutils/disputegame/super_dispute_system.go +++ b/op-e2e/e2eutils/disputegame/super_dispute_system.go @@ -99,6 +99,10 @@ func (s *SuperDisputeSystem) DependencySet() *depset.StaticConfigDependencySet { return s.sys.DependencySet() } +func (s *SuperDisputeSystem) L1Genesis() *core.Genesis { + return s.sys.L1Genesis() +} + func (s *SuperDisputeSystem) L2Geneses() []*core.Genesis { networks := s.sys.L2IDs() cfgs := make([]*core.Genesis, len(networks)) diff --git a/op-e2e/e2eutils/opnode/opnode.go b/op-e2e/e2eutils/opnode/opnode.go index 675b82f4c11b1..3a8cf3b6963c4 100644 --- a/op-e2e/e2eutils/opnode/opnode.go +++ b/op-e2e/e2eutils/opnode/opnode.go @@ -55,6 +55,9 @@ func (o *Opnode) P2P() p2p.Node { var _ services.RollupNode = (*Opnode)(nil) func NewOpnode(l log.Logger, c *config.Config, errFn func(error)) (*Opnode, error) { + if err := c.Check(); err != nil { + return nil, err + } var cycle cliapp.Lifecycle c.Cancel = func(errCause error) { l.Warn("node requested early shutdown!", "err", errCause) diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index b48209fe668c4..c8bad92df18a6 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -88,6 +88,7 @@ type SuperSystem interface { // L2 level ChainID(network string) *big.Int RollupConfig(network string) *rollup.Config + L1Genesis() *core.Genesis L2Genesis(network string) *core.Genesis UserKey(network, username string) ecdsa.PrivateKey L2OperatorKey(network string, role devkeys.ChainOperatorRole) ecdsa.PrivateKey @@ -448,6 +449,14 @@ func (s *interopE2ESystem) L2OperatorKey(id string, role devkeys.ChainOperatorRo return s.l2s[id].operatorKeys[role] } +func (s *interopE2ESystem) L1ID() string { + return s.worldOutput.L1.Genesis.Config.ChainID.String() +} + +func (s *interopE2ESystem) L1Genesis() *core.Genesis { + return s.worldOutput.L1.Genesis +} + // L2IDs returns the list of L2 IDs, which are the keys of the L2s map func (s *interopE2ESystem) L2IDs() []string { ids := make([]string, 0, len(s.l2s)) diff --git a/op-e2e/interop/supersystem_l2.go b/op-e2e/interop/supersystem_l2.go index 164bbebf3ddf1..7aa4f3f6e5051 100644 --- a/op-e2e/interop/supersystem_l2.go +++ b/op-e2e/interop/supersystem_l2.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" gn "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/require" ) @@ -112,7 +113,7 @@ func (s *interopE2ESystem) L2RollupClient(id string, name string) *sources.Rollu func (s *interopE2ESystem) newL2(id string, l2Out *interopgen.L2Output, depSet depset.DependencySet) l2Net { operatorKeys := s.newOperatorKeysForL2(l2Out) l2Geth := s.newGethForL2(id, "sequencer", l2Out) - opNode := s.newNodeForL2(id, "sequencer", l2Out, depSet, operatorKeys, l2Geth, true) + opNode := s.newNodeForL2(id, "sequencer", l2Out, depSet, operatorKeys, l2Geth, true, s.l1.Backend.BlockChain().Config()) proposer := s.newProposerForL2(id, operatorKeys) batcher := s.newBatcherForL2(id, operatorKeys, l2Geth, opNode) @@ -131,7 +132,7 @@ func (s *interopE2ESystem) newL2(id string, l2Out *interopgen.L2Output, depSet d func (s *interopE2ESystem) AddNode(id string, name string) { l2 := s.l2s[id] l2Geth := s.newGethForL2(id, name, l2.l2Out) - opNode := s.newNodeForL2(id, name, l2.l2Out, s.DependencySet(), l2.operatorKeys, l2Geth, false) + opNode := s.newNodeForL2(id, name, l2.l2Out, s.DependencySet(), l2.operatorKeys, l2Geth, false, s.l1.Backend.BlockChain().Config()) l2.nodes[name] = &l2Node{name: name, opNode: opNode, l2Geth: l2Geth} endpoint, secret := l2.nodes[name].opNode.InteropRPC() @@ -148,6 +149,7 @@ func (s *interopE2ESystem) newNodeForL2( operatorKeys map[devkeys.ChainOperatorRole]ecdsa.PrivateKey, l2Geth *geth.GethInstance, isSequencer bool, + l1ChainConfig *params.ChainConfig, ) *opnode.Opnode { logger := s.logger.New("role", "op-node-"+id+"-"+name) p2pKey := operatorKeys[devkeys.SequencerP2PRole] @@ -158,6 +160,7 @@ func (s *interopE2ESystem) newNodeForL2( TrustRPC: false, RPCProviderKind: sources.RPCKindDebugGeth, }, + L1ChainConfig: l1ChainConfig, L2: &config.L2EndpointConfig{ L2EngineAddr: l2Geth.AuthRPC().RPC(), L2EngineJWTSecret: testingJWTSecret, diff --git a/op-e2e/opgeth/op_geth.go b/op-e2e/opgeth/op_geth.go index a8b57ff5a093e..00a1da4663a17 100644 --- a/op-e2e/opgeth/op_geth.go +++ b/op-e2e/opgeth/op_geth.go @@ -202,7 +202,7 @@ func (d *OpGeth) StartBlockBuilding(ctx context.Context, attrs *eth.PayloadAttri // CreatePayloadAttributes creates a valid PayloadAttributes containing a L1Info deposit transaction followed by the supplied transactions. func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.PayloadAttributes, error) { timestamp := d.L2Head.Timestamp + 2 - l1Info, err := derive.L1InfoDepositBytes(d.l2Engine.RollupConfig(), d.SystemConfig, d.sequenceNum, d.L1Head, uint64(timestamp)) + l1Info, err := derive.L1InfoDepositBytes(d.l2Engine.RollupConfig(), d.L1ChainConfig, d.SystemConfig, d.sequenceNum, d.L1Head, uint64(timestamp)) if err != nil { return nil, err } diff --git a/op-e2e/opgeth/op_geth_test.go b/op-e2e/opgeth/op_geth_test.go index 159552520e0ab..1330e0bda7e8a 100644 --- a/op-e2e/opgeth/op_geth_test.go +++ b/op-e2e/opgeth/op_geth_test.go @@ -406,7 +406,7 @@ func TestPreregolith(t *testing.T) { defer opGeth.Close() rollupCfg := rollup.Config{} - systemTx, err := derive.L1InfoDeposit(&rollupCfg, opGeth.SystemConfig, 1, opGeth.L1Head, 0) + systemTx, err := derive.L1InfoDeposit(&rollupCfg, opGeth.L1ChainConfig, opGeth.SystemConfig, 1, opGeth.L1Head, 0) systemTx.IsSystemTransaction = true require.NoError(t, err) @@ -599,7 +599,7 @@ func TestRegolith(t *testing.T) { test.activateRegolith(ctx, t, opGeth) rollupCfg := rollup.Config{} - systemTx, err := derive.L1InfoDeposit(&rollupCfg, opGeth.SystemConfig, 1, opGeth.L1Head, 0) + systemTx, err := derive.L1InfoDeposit(&rollupCfg, opGeth.L1ChainConfig, opGeth.SystemConfig, 1, opGeth.L1Head, 0) systemTx.IsSystemTransaction = true require.NoError(t, err) diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index c128fad46dd57..8cf6eadce4d51 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -365,7 +365,7 @@ type System struct { Cfg SystemConfig RollupConfig *rollup.Config - + L1GenesisCfg *core.Genesis L2GenesisCfg *core.Genesis // Connections to running nodes @@ -480,6 +480,10 @@ func (sys *System) RollupCfgs() []*rollup.Config { return []*rollup.Config{sys.RollupConfig} } +func (sys *System) L1Genesis() *core.Genesis { + return sys.L1GenesisCfg +} + func (sys *System) L2Genesis() *core.Genesis { return sys.L2GenesisCfg } @@ -633,6 +637,8 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, return nil, err } + sys.L1GenesisCfg = l1Genesis + for addr, amount := range cfg.Premine { if existing, ok := l1Genesis.Alloc[addr]; ok { l1Genesis.Alloc[addr] = types.Account{ @@ -894,6 +900,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, return nil, err } c.AltDA = altDACLIConfig + c.L1ChainConfig = l1Genesis.Config if p, ok := p2pNodes[name]; ok { c.P2P = p diff --git a/op-e2e/system/fees/l1info_test.go b/op-e2e/system/fees/l1info_test.go index 7ae066d993d5c..6edf3cf79b0fe 100644 --- a/op-e2e/system/fees/l1info_test.go +++ b/op-e2e/system/fees/l1info_test.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/require" @@ -171,7 +172,7 @@ func TestL1InfoContract(t *testing.T) { l1blocks[h].BlobBaseFeeScalar = scalars.BlobBaseFeeScalar l1blocks[h].BaseFeeScalar = scalars.BaseFeeScalar if excess := b.ExcessBlobGas(); excess != nil { - l1blocks[h].BlobBaseFee = eth.CalcBlobFeeDefault(b.Header()) + l1blocks[h].BlobBaseFee = eip4844.CalcBlobFee(sys.L1Genesis().Config, b.Header()) } else { l1blocks[h].BlobBaseFee = big.NewInt(1) } diff --git a/op-e2e/system/proofs/system_fpp_test.go b/op-e2e/system/proofs/system_fpp_test.go index e21fbcff4a722..c6a3593068881 100644 --- a/op-e2e/system/proofs/system_fpp_test.go +++ b/op-e2e/system/proofs/system_fpp_test.go @@ -285,7 +285,7 @@ type FaultProofProgramTestScenario struct { // testFaultProofProgramScenario runs the fault proof program in several contexts, given a test scenario. func testFaultProofProgramScenario(t *testing.T, ctx context.Context, sys *e2esys.System, s *FaultProofProgramTestScenario) { preimageDir := t.TempDir() - fppConfig := oppconf.NewSingleChainConfig(sys.RollupConfig, sys.L2GenesisCfg.Config, s.L1Head, s.L2Head, s.L2OutputRoot, common.Hash(s.L2Claim), s.L2ClaimBlockNumber) + fppConfig := oppconf.NewSingleChainConfig(sys.RollupConfig, sys.L2GenesisCfg.Config, sys.L1GenesisCfg.Config, s.L1Head, s.L2Head, s.L2OutputRoot, common.Hash(s.L2Claim), s.L2ClaimBlockNumber) fppConfig.L1URL = sys.NodeEndpoint("l1").RPC() fppConfig.L2URLs = []string{sys.NodeEndpoint("sequencer").RPC()} fppConfig.L1BeaconURL = sys.L1BeaconEndpoint().RestHTTP() diff --git a/op-node/benchmarks/batchbuilding_test.go b/op-node/benchmarks/batchbuilding_test.go index 52d529f0b4b1b..6fc94ff24d986 100644 --- a/op-node/benchmarks/batchbuilding_test.go +++ b/op-node/benchmarks/batchbuilding_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" ) @@ -114,7 +115,7 @@ func randomBlock(cfg *rollup.Config, rng *rand.Rand, txCount int, timestamp uint // should only be used for testing purposes, as the batch input doesn't contain the necessary information // to build the full block (only non-deposit transactions and a subset of header fields are populated). func singularBatchToBlock(rollupCfg *rollup.Config, batch *derive.SingularBatch) (*types.Block, error) { - l1InfoTx, err := derive.L1InfoDeposit(rollupCfg, eth.SystemConfig{}, 0, &testutils.MockBlockInfo{ + l1InfoTx, err := derive.L1InfoDeposit(rollupCfg, params.MergedTestChainConfig, eth.SystemConfig{}, 0, &testutils.MockBlockInfo{ InfoNum: uint64(batch.EpochNum), InfoHash: batch.EpochHash, }, batch.Timestamp) diff --git a/op-node/config/config.go b/op-node/config/config.go index 80a438c18834a..4aaa32ccf970a 100644 --- a/op-node/config/config.go +++ b/op-node/config/config.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/oppprof" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" + "github.com/ethereum/go-ethereum/params" ) type Config struct { @@ -34,6 +35,8 @@ type Config struct { Rollup rollup.Config + L1ChainConfig *params.ChainConfig + DependencySet depset.DependencySet // P2PSigner will be used for signing off on published content @@ -120,6 +123,9 @@ func (cfg *Config) Check() error { if err := cfg.L2.Check(); err != nil { return fmt.Errorf("l2 endpoint config error: %w", err) } + if cfg.L1ChainConfig == nil { + return fmt.Errorf("missing L1ChainConfig") + } if cfg.Rollup.EcotoneTime != nil { if cfg.Beacon == nil { return fmt.Errorf("the Ecotone upgrade is scheduled (timestamp = %d) but no L1 Beacon API endpoint is configured", *cfg.Rollup.EcotoneTime) diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index 8001fd61df99e..c93ad94a09f1f 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -187,6 +187,12 @@ var ( Value: time.Second * 12, Category: L1RPCCategory, } + L1ChainConfig = &cli.PathFlag{ + Name: "rollup.l1-chain-config", + Usage: "Path to .json file with the chain configuration for the L1, either in the direct format or genesis.json format (i.e. embedded under the .config property). Not necessary / will be ignored if using Ethereum mainnet or Sepolia as an L1.", + EnvVars: prefixEnvVars("ROLLUP_L1_CHAIN_CONFIG"), + Category: RollupCategory, + } L2EngineKind = &cli.GenericFlag{ Name: "l2.enginekind", Usage: "The kind of engine client, used to control the behavior of optimism in respect to different types of engine clients. Valid options: " + @@ -464,6 +470,7 @@ var optionalFlags = []cli.Flag{ ConductorRpcFlag, ConductorRpcTimeoutFlag, SafeDBPath, + L1ChainConfig, L2EngineKind, L2EngineRpcTimeout, InteropRPCAddr, diff --git a/op-node/node/node.go b/op-node/node/node.go index 8eea556531aa0..1ef601418f78e 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -455,7 +455,7 @@ func (n *OpNode) initL2(ctx context.Context, cfg *config.Config) error { return fmt.Errorf("cfg.Rollup.ChainOpConfig is nil. Please see https://github.com/ethereum-optimism/optimism/releases/tag/op-node/v1.11.0: %w", err) } - n.l2Driver = driver.NewDriver(n.eventSys, n.eventDrain, &cfg.Driver, &cfg.Rollup, cfg.DependencySet, n.l2Source, n.l1Source, + n.l2Driver = driver.NewDriver(n.eventSys, n.eventDrain, &cfg.Driver, &cfg.Rollup, cfg.L1ChainConfig, cfg.DependencySet, n.l2Source, n.l1Source, n.beacon, n, n, n.log, n.metrics, cfg.ConfigPersistence, n.safeDB, &cfg.Sync, sequencerConductor, altDA, indexingMode) return nil } diff --git a/op-node/rollup/attributes/attributes_test.go b/op-node/rollup/attributes/attributes_test.go index aaf5359b89938..6812e319b308f 100644 --- a/op-node/rollup/attributes/attributes_test.go +++ b/op-node/rollup/attributes/attributes_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" @@ -86,7 +87,7 @@ func TestAttributesHandler(t *testing.T) { emptyWithdrawals := make(types.Withdrawals, 0) - a1L1Info, err := derive.L1InfoDepositBytes(cfg, cfg.Genesis.SystemConfig, 1, aL1Info, refA0.Time+cfg.BlockTime) + a1L1Info, err := derive.L1InfoDepositBytes(cfg, params.MergedTestChainConfig, cfg.Genesis.SystemConfig, 1, aL1Info, refA0.Time+cfg.BlockTime) require.NoError(t, err) parentBeaconBlockRoot := testutils.RandomHash(rng) payloadA1 := ð.ExecutionPayloadEnvelope{ExecutionPayload: ð.ExecutionPayload{ diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 54fa6a4570309..dee30207195fc 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -7,6 +7,7 @@ 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/params" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -30,23 +31,25 @@ type SystemConfigL2Fetcher interface { // FetchingAttributesBuilder fetches inputs for the building of L2 payload attributes on the fly. type FetchingAttributesBuilder struct { - rollupCfg *rollup.Config - depSet DependencySet - l1 L1ReceiptsFetcher - l2 SystemConfigL2Fetcher + rollupCfg *rollup.Config + l1ChainConfig *params.ChainConfig + depSet DependencySet + l1 L1ReceiptsFetcher + l2 SystemConfigL2Fetcher // whether to skip the L1 origin timestamp check - only for testing purposes testSkipL1OriginCheck bool } -func NewFetchingAttributesBuilder(rollupCfg *rollup.Config, depSet DependencySet, l1 L1ReceiptsFetcher, l2 SystemConfigL2Fetcher) *FetchingAttributesBuilder { +func NewFetchingAttributesBuilder(rollupCfg *rollup.Config, l1ChainConfig *params.ChainConfig, depSet DependencySet, l1 L1ReceiptsFetcher, l2 SystemConfigL2Fetcher) *FetchingAttributesBuilder { if rollupCfg.InteropTime != nil && depSet == nil { panic("FetchingAttributesBuilder requires a dependency set when interop fork is scheduled") } return &FetchingAttributesBuilder{ - rollupCfg: rollupCfg, - depSet: depSet, - l1: l1, - l2: l2, + rollupCfg: rollupCfg, + l1ChainConfig: l1ChainConfig, + depSet: depSet, + l1: l1, + l2: l2, } } @@ -158,7 +161,7 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex } } - l1InfoTx, err := L1InfoDepositBytes(ba.rollupCfg, sysConfig, seqNumber, l1Info, nextL2Time) + l1InfoTx, err := L1InfoDepositBytes(ba.rollupCfg, ba.l1ChainConfig, sysConfig, seqNumber, l1Info, nextL2Time) if err != nil { return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err)) } diff --git a/op-node/rollup/derive/attributes_queue_test.go b/op-node/rollup/derive/attributes_queue_test.go index 2f6e59141e4c7..2f63983ab73ba 100644 --- a/op-node/rollup/derive/attributes_queue_test.go +++ b/op-node/rollup/derive/attributes_queue_test.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -67,7 +68,7 @@ func TestAttributesQueue(t *testing.T) { l2Fetcher.ExpectSystemConfigByL2Hash(safeHead.Hash, parentL1Cfg, nil) rollupCfg := rollup.Config{} - l1InfoTx, err := L1InfoDepositBytes(&rollupCfg, expectedL1Cfg, safeHead.SequenceNumber+1, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(&rollupCfg, params.MergedTestChainConfig, expectedL1Cfg, safeHead.SequenceNumber+1, l1Info, 0) require.NoError(t, err) attrs := eth.PayloadAttributes{ Timestamp: eth.Uint64Quantity(safeHead.Time + cfg.BlockTime), @@ -77,7 +78,7 @@ func TestAttributesQueue(t *testing.T) { NoTxPool: true, GasLimit: (*eth.Uint64Quantity)(&expectedL1Cfg.GasLimit), } - attrBuilder := NewFetchingAttributesBuilder(cfg, nil, l1Fetcher, l2Fetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, nil, l1Fetcher, l2Fetcher) aq := NewAttributesQueue(testlog.Logger(t, log.LevelError), cfg, attrBuilder, nil) diff --git a/op-node/rollup/derive/attributes_test.go b/op-node/rollup/derive/attributes_test.go index 3be85d8406741..f835b8566661f 100644 --- a/op-node/rollup/derive/attributes_test.go +++ b/op-node/rollup/derive/attributes_test.go @@ -16,6 +16,7 @@ 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/params" "github.com/stretchr/testify/require" ) @@ -50,7 +51,7 @@ func TestPreparePayloadAttributes(t *testing.T) { l1Info.InfoNum = l2Parent.L1Origin.Number + 1 epoch := l1Info.ID() l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil) - attrBuilder := NewFetchingAttributesBuilder(mkCfg(), nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(mkCfg(), params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) _, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NotNil(t, err, "inconsistent L1 origin error expected") require.ErrorIs(t, err, ErrReset, "inconsistent L1 origin transition must be handled like a critical error with reorg") @@ -66,7 +67,7 @@ func TestPreparePayloadAttributes(t *testing.T) { l1Info := testutils.RandomBlockInfo(rng) l1Info.InfoNum = l2Parent.L1Origin.Number epoch := l1Info.ID() - attrBuilder := NewFetchingAttributesBuilder(mkCfg(), nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(mkCfg(), params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) _, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NotNil(t, err, "inconsistent L1 origin error expected") require.ErrorIs(t, err, ErrReset, "inconsistent L1 origin transition must be handled like a critical error with reorg") @@ -83,7 +84,7 @@ func TestPreparePayloadAttributes(t *testing.T) { epoch.Number += 1 mockRPCErr := errors.New("mock rpc error") l1Fetcher.ExpectFetchReceipts(epoch.Hash, nil, nil, mockRPCErr) - attrBuilder := NewFetchingAttributesBuilder(mkCfg(), nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(mkCfg(), params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) _, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.ErrorIs(t, err, mockRPCErr, "mock rpc error expected") require.ErrorIs(t, err, ErrTemporary, "rpc errors should not be critical, it is not necessary to reorg") @@ -99,7 +100,7 @@ func TestPreparePayloadAttributes(t *testing.T) { epoch := l2Parent.L1Origin mockRPCErr := errors.New("mock rpc error") l1Fetcher.ExpectInfoByHash(epoch.Hash, nil, mockRPCErr) - attrBuilder := NewFetchingAttributesBuilder(mkCfg(), nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(mkCfg(), params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) _, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.ErrorIs(t, err, mockRPCErr, "mock rpc error expected") require.ErrorIs(t, err, ErrTemporary, "rpc errors should not be critical, it is not necessary to reorg") @@ -117,10 +118,10 @@ func TestPreparePayloadAttributes(t *testing.T) { l1Info.InfoParentHash = l2Parent.L1Origin.Hash l1Info.InfoNum = l2Parent.L1Origin.Number + 1 epoch := l1Info.ID() - l1InfoTx, err := L1InfoDepositBytes(mkCfg(), testSysCfg, 0, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(mkCfg(), params.MergedTestChainConfig, testSysCfg, 0, l1Info, 0) require.NoError(t, err) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.NotNil(t, attrs) @@ -156,13 +157,13 @@ func TestPreparePayloadAttributes(t *testing.T) { require.NoError(t, err) epoch := l1Info.ID() - l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, 0, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(cfg, params.MergedTestChainConfig, testSysCfg, 0, l1Info, 0) require.NoError(t, err) l2Txs := append(append(make([]eth.Data, 0), l1InfoTx), usedDepositTxs...) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, receipts, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.NotNil(t, attrs) @@ -187,11 +188,11 @@ func TestPreparePayloadAttributes(t *testing.T) { l1Info.InfoNum = l2Parent.L1Origin.Number epoch := l1Info.ID() - l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, l2Parent.SequenceNumber+1, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(cfg, params.MergedTestChainConfig, testSysCfg, l2Parent.SequenceNumber+1, l1Info, 0) require.NoError(t, err) l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.NotNil(t, attrs) @@ -233,7 +234,7 @@ func TestPreparePayloadAttributes(t *testing.T) { seqNumber := uint64(0) epoch := l1Info.ID() - l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, seqNumber, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(cfg, params.MergedTestChainConfig, testSysCfg, seqNumber, l1Info, 0) require.NoError(t, err) require.NoError(t, err) @@ -242,7 +243,7 @@ func TestPreparePayloadAttributes(t *testing.T) { l2Txs = append(l2Txs, userDepositTxs...) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, receipts, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, depSet, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, depSet, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.NotNil(t, attrs) @@ -274,7 +275,7 @@ func TestPreparePayloadAttributes(t *testing.T) { seqNumber := l2Parent.SequenceNumber + 1 epoch := l1Info.ID() - l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, seqNumber, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(cfg, params.MergedTestChainConfig, testSysCfg, seqNumber, l1Info, 0) require.NoError(t, err) require.NoError(t, err) @@ -282,7 +283,7 @@ func TestPreparePayloadAttributes(t *testing.T) { l2Txs = append(l2Txs, l1InfoTx) l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, depSet, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, depSet, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.NotNil(t, attrs) @@ -315,10 +316,10 @@ func TestPreparePayloadAttributes(t *testing.T) { l1Info.InfoParentHash = l2Parent.L1Origin.Hash l1Info.InfoNum = l2Parent.L1Origin.Number + 1 epoch := l1Info.ID() - l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, 0, l1Info, 0) + l1InfoTx, err := L1InfoDepositBytes(cfg, params.MergedTestChainConfig, testSysCfg, 0, l1Info, 0) require.NoError(t, err) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.Equal(t, eip1559Params, *attrs.EIP1559Params) @@ -367,10 +368,10 @@ func TestPreparePayloadAttributes(t *testing.T) { if !tc.regolith { time-- } - l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, 0, l1Info, time) + l1InfoTx, err := L1InfoDepositBytes(cfg, params.MergedTestChainConfig, testSysCfg, 0, l1Info, time) require.NoError(t, err) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, nil, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, nil, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) require.Equal(t, l1InfoTx, []byte(attrs.Transactions[0])) @@ -401,7 +402,7 @@ func TestPreparePayloadAttributes(t *testing.T) { epoch := l1Info.ID() l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil) - attrBuilder := NewFetchingAttributesBuilder(cfg, depSet, l1Fetcher, l1CfgFetcher) + attrBuilder := NewFetchingAttributesBuilder(cfg, params.MergedTestChainConfig, depSet, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) require.NoError(t, err) return attrs diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index ddeb35c5cdb24..af063d65b337a 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -404,7 +405,7 @@ func L1BlockInfoFromBytes(rollupCfg *rollup.Config, l2BlockTime uint64, data []b // L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block, // and the L2 block-height difference with the start of the epoch. -func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber uint64, block eth.BlockInfo, l2Timestamp uint64) (*types.DepositTx, error) { +func L1InfoDeposit(rollupCfg *rollup.Config, l1ChainConfig *params.ChainConfig, sysCfg eth.SystemConfig, seqNumber uint64, block eth.BlockInfo, l2Timestamp uint64) (*types.DepositTx, error) { l1BlockInfo := L1BlockInfo{ Number: block.NumberU64(), Time: block.Time(), @@ -416,7 +417,7 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber var data []byte if isEcotoneButNotFirstBlock(rollupCfg, l2Timestamp) { isIsthmusActivated := isIsthmusButNotFirstBlock(rollupCfg, l2Timestamp) - l1BlockInfo.BlobBaseFee = block.BlobBaseFee() + l1BlockInfo.BlobBaseFee = block.BlobBaseFee(l1ChainConfig) // Apply Cancun blob base fee calculation if this chain needs the L1 Pectra // blob schedule fix (mostly Holesky and Sepolia OP-Stack chains). @@ -496,8 +497,8 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber } // L1InfoDepositBytes returns a serialized L1-info attributes transaction. -func L1InfoDepositBytes(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber uint64, l1Info eth.BlockInfo, l2Timestamp uint64) ([]byte, error) { - dep, err := L1InfoDeposit(rollupCfg, sysCfg, seqNumber, l1Info, l2Timestamp) +func L1InfoDepositBytes(rollupCfg *rollup.Config, l1ChainConfig *params.ChainConfig, sysCfg eth.SystemConfig, seqNumber uint64, l1Info eth.BlockInfo, l2Timestamp uint64) ([]byte, error) { + dep, err := L1InfoDeposit(rollupCfg, l1ChainConfig, sysCfg, seqNumber, l1Info, l2Timestamp) if err != nil { return nil, fmt.Errorf("failed to create L1 info tx: %w", err) } diff --git a/op-node/rollup/derive/l1_block_info_test.go b/op-node/rollup/derive/l1_block_info_test.go index c634a9ae4ea25..0a7def874f052 100644 --- a/op-node/rollup/derive/l1_block_info_test.go +++ b/op-node/rollup/derive/l1_block_info_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -69,7 +70,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { info := testCase.mkInfo(rng) l1Cfg := testCase.mkL1Cfg(rng, info) seqNr := testCase.seqNr(rng) - depTx, err := L1InfoDeposit(&rollupCfg, l1Cfg, seqNr, info, 0) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, l1Cfg, seqNr, info, 0) require.NoError(t, err) res, err := L1BlockInfoFromBytes(&rollupCfg, info.Time(), depTx.Data) require.NoError(t, err, "expected valid deposit info") @@ -99,7 +100,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { t.Run("invalid selector", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) info := testutils.MakeBlockInfo(nil)(rng) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) require.NoError(t, err) _, err = crand.Read(depTx.Data[0:4]) require.NoError(t, err) @@ -111,7 +112,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{} rollupCfg.ActivateAtGenesis(rollup.Regolith) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -123,7 +124,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Ecotone) // run 1 block after ecotone transition timestamp := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -136,7 +137,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Delta) ecotoneTime := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime // activate ecotone just after genesis rollupCfg.EcotoneTime = &ecotoneTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, ecotoneTime) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, ecotoneTime) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -147,7 +148,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} rollupCfg.ActivateAtGenesis(rollup.Ecotone) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -160,7 +161,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Isthmus) // run 1 block after isthmus transition timestamp := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -173,7 +174,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Granite) isthmusTime := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime // activate isthmus just after genesis rollupCfg.InteropTime = &isthmusTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, isthmusTime) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, isthmusTime) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -186,7 +187,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} rollupCfg.ActivateAtGenesis(rollup.Isthmus) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -199,7 +200,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Interop) // run 1 block after interop transition timestamp := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, timestamp) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -212,7 +213,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { rollupCfg.ActivateAtGenesis(rollup.Isthmus) interopTime := rollupCfg.Genesis.L2Time + rollupCfg.BlockTime // activate interop just after genesis rollupCfg.InteropTime = &interopTime - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, interopTime) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, interopTime) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) @@ -225,7 +226,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { info := testutils.MakeBlockInfo(nil)(rng) rollupCfg := rollup.Config{BlockTime: 2, Genesis: rollup.Genesis{L2Time: 1000}} rollupCfg.ActivateAtGenesis(rollup.Interop) - depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, randomL1Cfg(rng, info), randomSeqNr(rng), info, rollupCfg.Genesis.L2Time) require.NoError(t, err) require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) diff --git a/op-node/rollup/derive/l1_block_info_tob_test.go b/op-node/rollup/derive/l1_block_info_tob_test.go index d7c9f2f8931dc..7aacd13cf8779 100644 --- a/op-node/rollup/derive/l1_block_info_tob_test.go +++ b/op-node/rollup/derive/l1_block_info_tob_test.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils/fuzzerutils" + "github.com/ethereum/go-ethereum/params" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/require" ) @@ -29,7 +30,7 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) { var rollupCfg rollup.Config // Create our deposit tx from our info - depTx, err := L1InfoDeposit(&rollupCfg, sysCfg, seqNr, &l1Info, 0) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, sysCfg, seqNr, &l1Info, 0) require.NoError(t, err, "error creating deposit tx from L1 info") // Get our info from out deposit tx @@ -74,7 +75,7 @@ func FuzzDecodeDepositTxDataToL1Info(f *testing.F) { GasLimit: uint64(0), } - depTx, err := L1InfoDeposit(&rollupCfg, sysCfg, res.SequenceNumber, &l1Info, 0) + depTx, err := L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, sysCfg, res.SequenceNumber, &l1Info, 0) require.NoError(t, err, "error creating deposit tx from L1 info") require.Equal(t, depTx.Data, fuzzedData) }) diff --git a/op-node/rollup/derive/pipeline.go b/op-node/rollup/derive/pipeline.go index a593dd1bc94cf..732200661805c 100644 --- a/op-node/rollup/derive/pipeline.go +++ b/op-node/rollup/derive/pipeline.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -96,7 +97,7 @@ type DerivationPipeline struct { // NewDerivationPipeline creates a DerivationPipeline, to turn L1 data into L2 block-inputs. func NewDerivationPipeline(log log.Logger, rollupCfg *rollup.Config, depSet DependencySet, l1Fetcher L1Fetcher, l1Blobs L1BlobsFetcher, - altDA AltDAInputFetcher, l2Source L2Source, metrics Metrics, managedBySupervisor bool, + altDA AltDAInputFetcher, l2Source L2Source, metrics Metrics, managedBySupervisor bool, l1ChainConfig *params.ChainConfig, ) *DerivationPipeline { spec := rollup.NewChainSpec(rollupCfg) // Stages are strung together into a pipeline, @@ -113,7 +114,7 @@ func NewDerivationPipeline(log log.Logger, rollupCfg *rollup.Config, depSet Depe channelMux := NewChannelMux(log, spec, frameQueue, metrics) chInReader := NewChannelInReader(rollupCfg, log, channelMux, metrics) batchMux := NewBatchMux(log, rollupCfg, chInReader, l2Source) - attrBuilder := NewFetchingAttributesBuilder(rollupCfg, depSet, l1Fetcher, l2Source) + attrBuilder := NewFetchingAttributesBuilder(rollupCfg, l1ChainConfig, depSet, l1Fetcher, l2Source) attributesQueue := NewAttributesQueue(log, rollupCfg, attrBuilder, batchMux) // Reset from ResetEngine then up from L1 Traversal. The stages do not talk to each other during diff --git a/op-node/rollup/derive/test/random.go b/op-node/rollup/derive/test/random.go index 8502855d01463..24f28bb174a37 100644 --- a/op-node/rollup/derive/test/random.go +++ b/op-node/rollup/derive/test/random.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) @@ -23,7 +24,7 @@ func RandomL2Block(rng *rand.Rand, txCount int, t time.Time) (*types.Block, []*t t := uint64(0) rollupCfg.RegolithTime = &t } - l1InfoTx, err := derive.L1InfoDeposit(&rollupCfg, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), 0) + l1InfoTx, err := derive.L1InfoDeposit(&rollupCfg, params.MergedTestChainConfig, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), 0) if err != nil { panic("L1InfoDeposit: " + err.Error()) } diff --git a/op-node/rollup/driver/driver.go b/op-node/rollup/driver/driver.go index 2e5057b35efae..f03f44b0645d7 100644 --- a/op-node/rollup/driver/driver.go +++ b/op-node/rollup/driver/driver.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/sync" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/event" + "github.com/ethereum/go-ethereum/params" ) // aliases to not disrupt op-conductor code @@ -162,6 +163,7 @@ func NewDriver( drain Drain, driverCfg *Config, cfg *rollup.Config, + l1ChainConfig *params.ChainConfig, depSet derive.DependencySet, l2 L2Chain, l1 L1Chain, @@ -208,7 +210,7 @@ func NewDriver( sys.Register("attributes-handler", attributes.NewAttributesHandler(log, cfg, driverCtx, l2)) - derivationPipeline := derive.NewDerivationPipeline(log, cfg, depSet, verifConfDepth, l1Blobs, altDA, l2, metrics, indexingMode) + derivationPipeline := derive.NewDerivationPipeline(log, cfg, depSet, verifConfDepth, l1Blobs, altDA, l2, metrics, indexingMode, l1ChainConfig) sys.Register("pipeline", derive.NewPipelineDeriver(driverCtx, derivationPipeline)) @@ -236,7 +238,7 @@ func NewDriver( var sequencer sequencing.SequencerIface if driverCfg.SequencerEnabled { asyncGossiper := async.NewAsyncGossiper(driverCtx, network, log, metrics) - attrBuilder := derive.NewFetchingAttributesBuilder(cfg, depSet, l1, l2) + attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1ChainConfig, depSet, l1, l2) var seqL1Blocks sequencing.L1Blocks if driverCfg.SequencerUseFinalized { seqL1Blocks = finalized.NewFinalized(statusTracker.L1Finalized, l1, log) diff --git a/op-node/service.go b/op-node/service.go index 66efbf49bbea2..d918f4dcaae52 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "os" "strings" @@ -23,7 +24,9 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/engine" "github.com/ethereum-optimism/optimism/op-node/rollup/interop" "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/eth" opflags "github.com/ethereum-optimism/optimism/op-service/flags" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" "github.com/ethereum-optimism/optimism/op-service/oppprof" "github.com/ethereum-optimism/optimism/op-service/rpc" @@ -42,6 +45,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*config.Config, error) { return nil, err } + l1ChainConfig, err := NewL1ChainConfig(rollupConfig.L1ChainID, ctx, log) + if err != nil { + return nil, err + } + depSet, err := NewDependencySetFromCLI(ctx) if err != nil { return nil, err @@ -92,6 +100,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*config.Config, error) { cfg := &config.Config{ L1: l1Endpoint, L2: l2Endpoint, + L1ChainConfig: l1ChainConfig, Rollup: *rollupConfig, DependencySet: depSet, Driver: *driverConfig, @@ -313,6 +322,50 @@ func applyCeloHardforks(rollupConfig *rollup.Config) { } } +func NewL1ChainConfig(chainId *big.Int, ctx *cli.Context, log log.Logger) (*params.ChainConfig, error) { + if chainId == nil { + panic("l1 chain id is nil") + } + + if cfg := eth.L1ChainConfigByChainID(eth.ChainIDFromBig(chainId)); cfg != nil { + return cfg, nil + } + + // if the chain id is not known, we fallback to the CLI config + cf, err := NewL1ChainConfigFromCLI(log, ctx) + if err != nil { + return nil, err + } + if cf.ChainID.Cmp(chainId) != 0 { + return nil, fmt.Errorf("l1 chain config chain ID mismatch: %v != %v", cf.ChainID, chainId) + } + if cf.BlobScheduleConfig == nil { + return nil, fmt.Errorf("L1 chain config does not have a blob schedule config") + } + return cf, nil +} + +func NewL1ChainConfigFromCLI(log log.Logger, ctx *cli.Context) (*params.ChainConfig, error) { + l1ChainConfigPath := ctx.String(flags.L1ChainConfig.Name) + file, err := os.Open(l1ChainConfigPath) + if err != nil { + return nil, fmt.Errorf("failed to read chain spec: %w", err) + } + defer file.Close() + + // Attempt to decode directly as a ChainConfig + var chainConfig params.ChainConfig + dec := json.NewDecoder(file) + dec.DisallowUnknownFields() + if err := dec.Decode(&chainConfig); err == nil { + return &chainConfig, nil + } + + // If that fails, try to load the config from the .config property. + // This should work if the provided file is a genesis file / chainspec + return jsonutil.LoadJSONFieldStrict[params.ChainConfig](l1ChainConfigPath, "config") +} + func NewDependencySetFromCLI(ctx *cli.Context) (depset.DependencySet, error) { if !ctx.IsSet(flags.InteropDependencySet.Name) { return nil, nil diff --git a/op-node/service_l1_chain_config_test.go b/op-node/service_l1_chain_config_test.go new file mode 100644 index 0000000000000..27e8d66832418 --- /dev/null +++ b/op-node/service_l1_chain_config_test.go @@ -0,0 +1,121 @@ +package opnode + +import ( + "encoding/json" + "math/big" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + + nodeflags "github.com/ethereum-optimism/optimism/op-node/flags" +) + +func TestNewL1ChainConfig_KnownChains(t *testing.T) { + logger := log.New() + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + + t.Run("mainnet", func(t *testing.T) { + cfg, err := NewL1ChainConfig(new(big.Int).Set(params.MainnetChainConfig.ChainID), ctx, logger) + require.NoError(t, err) + require.Equal(t, params.MainnetChainConfig, cfg) + }) + + t.Run("sepolia", func(t *testing.T) { + cfg, err := NewL1ChainConfig(new(big.Int).Set(params.SepoliaChainConfig.ChainID), ctx, logger) + require.NoError(t, err) + require.Equal(t, params.SepoliaChainConfig, cfg) + }) +} + +func TestNewL1ChainConfig_CustomDirectAndEmbeddedAndNil(t *testing.T) { + logger := log.New() + + testChainID := big.NewInt(424242) + + // Build a minimal custom ChainConfig + custom := ¶ms.ChainConfig{ + ChainID: testChainID, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{}, + } + + customFaulty := ¶ms.ChainConfig{ + ChainID: testChainID, + BlobScheduleConfig: nil, + } + + // Prepare temp dir + dir := t.TempDir() + + encode := func(path string, cfg any) { + f, err := os.Create(path) + require.NoError(t, err) + enc := json.NewEncoder(f) + err = enc.Encode(cfg) + require.NoError(t, err) + require.NoError(t, f.Close()) + } + + // Direct JSON file containing a ChainConfig + directPath := filepath.Join(dir, "chainconfig.json") + encode(directPath, custom) + + directFaultyPath := filepath.Join(dir, "chainconfig_faulty.json") + encode(directFaultyPath, customFaulty) + + // Embedded JSON file that contains { "config": } + embeddedPath := filepath.Join(dir, "genesis_like.json") + type wrapper struct { + Config *params.ChainConfig `json:"config"` + } + encode(embeddedPath, wrapper{Config: custom}) + + // Helper to run the CLI with a given file path + runWithPath := func(path string) (*params.ChainConfig, error) { + app := cli.NewApp() + app.Flags = []cli.Flag{nodeflags.L1ChainConfig} + var out *params.ChainConfig + app.Action = func(ctx *cli.Context) error { + cfg, err := NewL1ChainConfig(testChainID, ctx, logger) + out = cfg + return err + } + // run with arg: --rollup.l1-chain-config + err := app.Run([]string{"op-node", "--" + nodeflags.L1ChainConfig.Name, path}) + return out, err + } + + t.Run("custom-direct", func(t *testing.T) { + cfg, err := runWithPath(directPath) + require.NoError(t, err) + require.NotNil(t, cfg) + require.Equal(t, custom.ChainID, cfg.ChainID) + }) + + t.Run("custom-embedded", func(t *testing.T) { + cfg, err := runWithPath(embeddedPath) + require.NoError(t, err) + require.NotNil(t, cfg) + require.Equal(t, custom.ChainID, cfg.ChainID) + }) + + t.Run("nil-chainid-panics", func(t *testing.T) { + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + require.Panics(t, func() { + _, _ = NewL1ChainConfig(nil, ctx, logger) + }) + }) + + t.Run("nil-blob-schedule-config-returns-error", func(t *testing.T) { + cfg, err := runWithPath(directFaultyPath) + require.Nil(t, cfg) + require.Error(t, err) + }) +} diff --git a/op-program/chainconfig/chaincfg.go b/op-program/chainconfig/chaincfg.go index a08fff8c287b9..41c8be28e7e70 100644 --- a/op-program/chainconfig/chaincfg.go +++ b/op-program/chainconfig/chaincfg.go @@ -25,7 +25,7 @@ var ( // OPSepoliaChainConfig loads the op-sepolia chain config. This is intended for tests that need an arbitrary, valid chain config. func OPSepoliaChainConfig() *params.ChainConfig { - return mustLoadChainConfig("op-sepolia") + return mustLoadL2ChainConfig("op-sepolia") } //go:embed configs/*json @@ -78,17 +78,17 @@ func rollupConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*rollup return &customRollupConfig, customRollupConfig.ParseRollupConfig(file) } -// ChainConfigByChainID locates the genesis chain config from either the superchain-registry or the embed. +// L2ChainConfigByChainID locates the genesis chain config from either the superchain-registry or the embed. // Returns ErrMissingChainConfig if the chain config is not found. -func ChainConfigByChainID(chainID eth.ChainID) (*params.ChainConfig, error) { +func L2ChainConfigByChainID(chainID eth.ChainID) (*params.ChainConfig, error) { config, err := superutil.LoadOPStackChainConfigFromChainID(eth.EvilChainIDToUInt64(chainID)) if err == nil { return config, err } - return chainConfigByChainID(chainID, customChainConfigFS) + return l2ChainConfigByChainID(chainID, customChainConfigFS) } -func chainConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*params.ChainConfig, error) { +func l2ChainConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*params.ChainConfig, error) { // Load from custom chain configs from embed FS data, err := customChainFS.ReadFile(fmt.Sprintf("configs/%v-genesis-l2.json", chainID)) if errors.Is(err, os.ErrNotExist) { @@ -104,12 +104,36 @@ func chainConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*params. return genesis.Config, nil } -func mustLoadChainConfig(name string) *params.ChainConfig { +func L1ChainConfigByChainID(chainID eth.ChainID) (*params.ChainConfig, error) { + if cfg := eth.L1ChainConfigByChainID(chainID); cfg != nil { + return cfg, nil + } + // if the l1 chain id is not known, we fallback to the custom chain config + return l1ChainConfigByChainID(chainID, customChainConfigFS) +} + +func l1ChainConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*params.ChainConfig, error) { + // Load from custom chain configs from embed FS + data, err := customChainFS.ReadFile(fmt.Sprintf("configs/%v-genesis-l1.json", chainID)) + if errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("%w: no chain config available for chain ID: %v", ErrMissingChainConfig, chainID) + } else if err != nil { + return nil, fmt.Errorf("failed to get chain config for chain ID %v: %w", chainID, err) + } + var genesis core.Genesis + err = json.Unmarshal(data, &genesis) + if err != nil { + return nil, fmt.Errorf("failed to parse chain config for chain ID %v: %w", chainID, err) + } + return genesis.Config, nil +} + +func mustLoadL2ChainConfig(name string) *params.ChainConfig { chainCfg := chaincfg.ChainByName(name) if chainCfg == nil { panic(fmt.Errorf("%w: unknown chain config %q", errChainNotFound, name)) } - cfg, err := ChainConfigByChainID(eth.ChainIDFromUInt64(chainCfg.ChainID)) + cfg, err := L2ChainConfigByChainID(eth.ChainIDFromUInt64(chainCfg.ChainID)) if err != nil { panic(fmt.Errorf("failed to load rollup config: %q: %w", name, err)) } @@ -159,18 +183,23 @@ func checkConfigFilenames(customChainFS embed.FS, configPath string) error { return fmt.Errorf("failed to check custom configs directory: %w", err) } var rollupChainIDs []eth.ChainID - var genesisChainIDs []eth.ChainID + var l2genesisChainIDs []eth.ChainID for _, entry := range entries { entryName := entry.Name() switch { case "placeholder.json" == entryName: case "depsets.json" == entryName: + case strings.HasSuffix(entryName, "-genesis-l1.json"): + _, err := eth.ParseDecimalChainID(strings.TrimSuffix(entry.Name(), "-genesis-l1.json")) + if err != nil { + return fmt.Errorf("incorrectly named genesis-l1 config (%s). expected -genesis-l1.json: %w", entryName, err) + } case strings.HasSuffix(entryName, "-genesis-l2.json"): id, err := eth.ParseDecimalChainID(strings.TrimSuffix(entry.Name(), "-genesis-l2.json")) if err != nil { return fmt.Errorf("incorrectly named genesis-l2 config (%s). expected -genesis-l2.json: %w", entryName, err) } - genesisChainIDs = append(genesisChainIDs, id) + l2genesisChainIDs = append(l2genesisChainIDs, id) case strings.HasSuffix(entryName, "-rollup.json"): id, err := eth.ParseDecimalChainID(strings.TrimSuffix(entry.Name(), "-rollup.json")) if err != nil { @@ -181,8 +210,9 @@ func checkConfigFilenames(customChainFS embed.FS, configPath string) error { return fmt.Errorf("invalid config file name: %s, Make sure that the only files in the custom config directory are placeholder.json, depsets.json, -genesis-l2.json or -rollup.json", entryName) } } - if !slices.Equal(rollupChainIDs, genesisChainIDs) { - return fmt.Errorf("mismatched chain IDs in custom configs: rollup chain IDs %v, genesis chain IDs %v. Make sure that the rollup and genesis configs have the same set of chain IDs prefixes", rollupChainIDs, genesisChainIDs) + if !slices.Equal(rollupChainIDs, l2genesisChainIDs) { + return fmt.Errorf("mismatched chain IDs in custom configs: rollup chain IDs %v, l2 genesis chain IDs %v. Make sure that the rollup and l2 genesis configs have the same set of chain IDs prefixes", rollupChainIDs, l2genesisChainIDs) } + return nil } diff --git a/op-program/chainconfig/chaincfg_test.go b/op-program/chainconfig/chaincfg_test.go index c84909c408cef..5b96180e7bac4 100644 --- a/op-program/chainconfig/chaincfg_test.go +++ b/op-program/chainconfig/chaincfg_test.go @@ -27,16 +27,27 @@ func TestGetCustomRollupConfig_Missing(t *testing.T) { // TestGetCustomChainConfig tests loading the custom chain configs from test embed FS. func TestGetCustomChainConfig(t *testing.T) { - config, err := chainConfigByChainID(eth.ChainIDFromUInt64(901), test.TestCustomChainConfigFS) + config, err := l2ChainConfigByChainID(eth.ChainIDFromUInt64(901), test.TestCustomChainConfigFS) require.NoError(t, err) require.Equal(t, config.ChainID.Uint64(), uint64(901)) - _, err = chainConfigByChainID(eth.ChainIDFromUInt64(900), test.TestCustomChainConfigFS) + _, err = l2ChainConfigByChainID(eth.ChainIDFromUInt64(900), test.TestCustomChainConfigFS) require.Error(t, err) } func TestGetCustomChainConfig_Missing(t *testing.T) { - _, err := chainConfigByChainID(eth.ChainIDFromUInt64(11111), test.TestCustomChainConfigFS) + _, err := l2ChainConfigByChainID(eth.ChainIDFromUInt64(11111), test.TestCustomChainConfigFS) + require.ErrorIs(t, err, ErrMissingChainConfig) +} + +func TestGetCustomL1ChainConfig(t *testing.T) { + config, err := l1ChainConfigByChainID(eth.ChainIDFromUInt64(900), test.TestCustomChainConfigFS) + require.NoError(t, err) + require.Equal(t, config.ChainID.Uint64(), uint64(900)) +} + +func TestGetCustomL1ChainConfig_Missing(t *testing.T) { + _, err := l1ChainConfigByChainID(eth.ChainIDFromUInt64(11111), test.TestCustomChainConfigFS) require.ErrorIs(t, err, ErrMissingChainConfig) } @@ -79,6 +90,16 @@ func TestCheckConfigFilenames(t *testing.T) { require.NoError(t, err) } +func TestCheckConfigFilenames_WithoutCustomL1Genesis(t *testing.T) { + err := checkConfigFilenames(test.TestCustomChainConfigNoL1FS, "configs_no_l1") + require.NoError(t, err) +} + +func TestCheckConfigFilenames_MultipleL1Genesis(t *testing.T) { + err := checkConfigFilenames(test.TestCustomChainConfigMultipleL1FS, "configs_multiple_l1") + require.NoError(t, err) +} + func TestCheckConfigFilenames_Missing(t *testing.T) { err := checkConfigFilenames(test.TestCustomChainConfigEmptyFS, "configs_empty") require.NoError(t, err) diff --git a/op-program/chainconfig/test/configs/900-genesis-l1.json b/op-program/chainconfig/test/configs/900-genesis-l1.json new file mode 100644 index 0000000000000..55d82624362c7 --- /dev/null +++ b/op-program/chainconfig/test/configs/900-genesis-l1.json @@ -0,0 +1,32 @@ +{ + "config": { + "chainId": 900, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "parisBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0 + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": {}, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/op-program/chainconfig/test/configs_multiple_l1/899-genesis-l1.json b/op-program/chainconfig/test/configs_multiple_l1/899-genesis-l1.json new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/op-program/chainconfig/test/configs_multiple_l1/900-genesis-l1.json b/op-program/chainconfig/test/configs_multiple_l1/900-genesis-l1.json new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/op-program/chainconfig/test/configs_no_l1/901-genesis-l2.json b/op-program/chainconfig/test/configs_no_l1/901-genesis-l2.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/op-program/chainconfig/test/configs_no_l1/901-genesis-l2.json @@ -0,0 +1 @@ +{} diff --git a/op-program/chainconfig/test/configs_no_l1/901-rollup.json b/op-program/chainconfig/test/configs_no_l1/901-rollup.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/op-program/chainconfig/test/configs_no_l1/901-rollup.json @@ -0,0 +1 @@ +{} diff --git a/op-program/chainconfig/test/test.go b/op-program/chainconfig/test/test.go index 9d74fd9aff592..0e9ff6907a193 100644 --- a/op-program/chainconfig/test/test.go +++ b/op-program/chainconfig/test/test.go @@ -10,3 +10,9 @@ var TestCustomChainConfigEmptyFS embed.FS //go:embed configs_typo/*json var TestCustomChainConfigTypoFS embed.FS + +//go:embed configs_no_l1/*.json +var TestCustomChainConfigNoL1FS embed.FS + +//go:embed configs_multiple_l1/*.json +var TestCustomChainConfigMultipleL1FS embed.FS diff --git a/op-program/client/boot/boot.go b/op-program/client/boot/boot.go index 3520771f0bfe1..220a9b253b4c4 100644 --- a/op-program/client/boot/boot.go +++ b/op-program/client/boot/boot.go @@ -3,6 +3,7 @@ package boot import ( "encoding/binary" "encoding/json" + "fmt" "math" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -24,6 +25,7 @@ type BootInfo struct { L2ChainConfig *params.ChainConfig RollupConfig *rollup.Config + L1ChainConfig *params.ChainConfig } type BootstrapClient struct { @@ -41,6 +43,7 @@ func (br *BootstrapClient) BootInfo() *BootInfo { l2ClaimBlockNumber := binary.BigEndian.Uint64(br.r.Get(L2ClaimBlockNumberLocalIndex)) l2ChainID := eth.ChainIDFromUInt64(binary.BigEndian.Uint64(br.r.Get(L2ChainIDLocalIndex))) + var l1ChainConfig *params.ChainConfig var l2ChainConfig *params.ChainConfig var rollupConfig *rollup.Config if l2ChainID == CustomChainIDIndicator { @@ -54,13 +57,27 @@ func (br *BootstrapClient) BootInfo() *BootInfo { if err != nil { panic("failed to bootstrap rollup config") } + l1ChainConfig = new(params.ChainConfig) + err = json.Unmarshal(br.r.Get(L1ChainConfigLocalIndex), l1ChainConfig) + if err != nil { + panic("failed to bootstrap l1ChainConfig: " + fmt.Sprintf("%v", err)) + } + if l1ChainConfig.ChainID.Cmp(rollupConfig.L1ChainID) != 0 { + panic(fmt.Sprintf("l1ChainConfig chain ID does not match rollup config L1 chain ID: %v != %v", + l1ChainConfig.ChainID, rollupConfig.L1ChainID)) + } } else { var err error rollupConfig, err = chainconfig.RollupConfigByChainID(l2ChainID) if err != nil { panic(err) } - l2ChainConfig, err = chainconfig.ChainConfigByChainID(l2ChainID) + l1ChainID := eth.ChainIDFromBig(rollupConfig.L1ChainID) + l1ChainConfig, err = chainconfig.L1ChainConfigByChainID(l1ChainID) + if err != nil { + panic(err) + } + l2ChainConfig, err = chainconfig.L2ChainConfigByChainID(l2ChainID) if err != nil { panic(err) } @@ -74,5 +91,6 @@ func (br *BootstrapClient) BootInfo() *BootInfo { L2ChainID: l2ChainID, L2ChainConfig: l2ChainConfig, RollupConfig: rollupConfig, + L1ChainConfig: l1ChainConfig, } } diff --git a/op-program/client/boot/boot_interop.go b/op-program/client/boot/boot_interop.go index dcf8d44447a07..2cea211b83eff 100644 --- a/op-program/client/boot/boot_interop.go +++ b/op-program/client/boot/boot_interop.go @@ -15,7 +15,8 @@ import ( ) var ( - ErrUnknownChainID = errors.New("unknown chain id") + ErrUnknownChainID = errors.New("unknown chain id") + ErrL1ChainConfigMismatch = errors.New("l1 chain config chain ID mismatch") ) type BootInfoInterop struct { @@ -30,6 +31,7 @@ type BootInfoInterop struct { type ConfigSource interface { RollupConfig(chainID eth.ChainID) (*rollup.Config, error) ChainConfig(chainID eth.ChainID) (*params.ChainConfig, error) + L1ChainConfig(chainID eth.ChainID) (*params.ChainConfig, error) DependencySet(chainID eth.ChainID) (depset.DependencySet, error) } @@ -38,6 +40,7 @@ type OracleConfigSource struct { customConfigsLoaded bool + l1ChainConfig *params.ChainConfig l2ChainConfigs map[eth.ChainID]*params.ChainConfig rollupConfigs map[eth.ChainID]*rollup.Config depset depset.DependencySet @@ -66,7 +69,7 @@ func (c *OracleConfigSource) ChainConfig(chainID eth.ChainID) (*params.ChainConf if cfg, ok := c.l2ChainConfigs[chainID]; ok { return cfg, nil } - cfg, err := chainconfig.ChainConfigByChainID(chainID) + cfg, err := chainconfig.L2ChainConfigByChainID(chainID) if !c.customConfigsLoaded && errors.Is(err, chainconfig.ErrMissingChainConfig) { c.loadCustomConfigs() if cfg, ok := c.l2ChainConfigs[chainID]; !ok { @@ -99,6 +102,21 @@ func (c *OracleConfigSource) DependencySet(chainID eth.ChainID) (depset.Dependen return c.depset, nil } +func (c *OracleConfigSource) L1ChainConfig(chainID eth.ChainID) (*params.ChainConfig, error) { + if c.l1ChainConfig != nil { + if c.l1ChainConfig.ChainID.Cmp(chainID.ToBig()) != 0 { + panic(fmt.Errorf("%w: %v != %v", ErrL1ChainConfigMismatch, c.l1ChainConfig.ChainID, chainID)) + } + return c.l1ChainConfig, nil + } + cfg, err := chainconfig.L1ChainConfigByChainID(chainID) + if err != nil { + return nil, err + } + c.l1ChainConfig = cfg + return cfg, nil +} + func (c *OracleConfigSource) loadCustomConfigs() { var rollupConfigs []*rollup.Config err := json.Unmarshal(c.oracle.Get(RollupConfigLocalIndex), &rollupConfigs) @@ -125,6 +143,13 @@ func (c *OracleConfigSource) loadCustomConfigs() { } c.depset = &depset c.customConfigsLoaded = true + + var l1ChainConfig *params.ChainConfig + err = json.Unmarshal(c.oracle.Get(L1ChainConfigLocalIndex), &l1ChainConfig) + if err != nil { + panic("failed to bootstrap l1 chain configs: " + fmt.Sprintf("%v", err)) + } + c.l1ChainConfig = l1ChainConfig } func BootstrapInterop(r oracleClient) *BootInfoInterop { diff --git a/op-program/client/boot/boot_interop_test.go b/op-program/client/boot/boot_interop_test.go index 773ce6bda7b98..7db252520c56f 100644 --- a/op-program/client/boot/boot_interop_test.go +++ b/op-program/client/boot/boot_interop_test.go @@ -24,7 +24,7 @@ func TestInteropBootstrap_SimpleValues(t *testing.T) { Claim: common.Hash{0xcc}, GameTimestamp: 49829482, } - mockOracle := newMockInteropBootstrapOracle(expected, false) + mockOracle := newMockInteropBootstrapOracle(expected, false, params.SepoliaChainConfig) actual := BootstrapInterop(mockOracle) require.Equal(t, expected.L1Head, actual.L1Head) require.Equal(t, expected.AgreedPrestate, actual.AgreedPrestate) @@ -40,7 +40,7 @@ func TestInteropBootstrap_RollupConfigBuiltIn(t *testing.T) { Claim: common.Hash{0xcc}, GameTimestamp: 49829482, } - mockOracle := newMockInteropBootstrapOracle(expected, false) + mockOracle := newMockInteropBootstrapOracle(expected, false, params.SepoliaChainConfig) actual := BootstrapInterop(mockOracle) actualCfg, err := actual.Configs.RollupConfig(eth.ChainIDFromBig(expectedCfg.L2ChainID)) require.NoError(t, err) @@ -56,7 +56,7 @@ func TestInteropBootstrap_RollupConfigCustom(t *testing.T) { Claim: common.Hash{0xcc}, GameTimestamp: 49829482, } - mockOracle := newMockInteropBootstrapOracle(source, true) + mockOracle := newMockInteropBootstrapOracle(source, true, params.SepoliaChainConfig) mockOracle.rollupCfgs = []*rollup.Config{config1, config2} actual := BootstrapInterop(mockOracle) actualCfg, err := actual.Configs.RollupConfig(eth.ChainIDFromBig(config1.L2ChainID)) @@ -76,7 +76,7 @@ func TestInteropBootstrap_ChainConfigBuiltIn(t *testing.T) { Claim: common.Hash{0xcc}, GameTimestamp: 49829482, } - mockOracle := newMockInteropBootstrapOracle(expected, false) + mockOracle := newMockInteropBootstrapOracle(expected, false, params.SepoliaChainConfig) actual := BootstrapInterop(mockOracle) actualCfg, err := actual.Configs.ChainConfig(eth.ChainIDFromBig(expectedCfg.ChainID)) require.NoError(t, err) @@ -92,7 +92,7 @@ func TestInteropBootstrap_ChainConfigCustom(t *testing.T) { Claim: common.Hash{0xcc}, GameTimestamp: 49829482, } - mockOracle := newMockInteropBootstrapOracle(expected, true) + mockOracle := newMockInteropBootstrapOracle(expected, true, params.SepoliaChainConfig) mockOracle.chainCfgs = []*params.ChainConfig{config1, config2} mockOracle.depset, _ = depset.NewStaticConfigDependencySet(map[eth.ChainID]*depset.StaticConfigDependency{ eth.ChainIDFromBig(config1.ChainID): {}, @@ -107,6 +107,11 @@ func TestInteropBootstrap_ChainConfigCustom(t *testing.T) { actualCfg, err = actual.Configs.ChainConfig(eth.ChainIDFromBig(config2.ChainID)) require.NoError(t, err) require.Equal(t, config2, actualCfg) + + actualCfg, err = actual.Configs.L1ChainConfig(eth.ChainIDFromBig(params.SepoliaChainConfig.ChainID)) + require.NoError(t, err) + require.Equal(t, params.SepoliaChainConfig, actualCfg) + } func TestInteropBootstrap_DependencySetCustom(t *testing.T) { @@ -118,7 +123,7 @@ func TestInteropBootstrap_DependencySetCustom(t *testing.T) { Claim: common.Hash{0xcc}, GameTimestamp: 49829482, } - mockOracle := newMockInteropBootstrapOracle(expected, true) + mockOracle := newMockInteropBootstrapOracle(expected, true, params.SepoliaChainConfig) var err error mockOracle.depset, err = depset.NewStaticConfigDependencySet(map[eth.ChainID]*depset.StaticConfigDependency{ eth.ChainIDFromBig(config1.ChainID): {}, @@ -132,7 +137,7 @@ func TestInteropBootstrap_DependencySetCustom(t *testing.T) { require.Equal(t, mockOracle.depset, depset) } -func newMockInteropBootstrapOracle(b *BootInfoInterop, custom bool) *mockInteropBootstrapOracle { +func newMockInteropBootstrapOracle(b *BootInfoInterop, custom bool, l1ChainCfg *params.ChainConfig) *mockInteropBootstrapOracle { return &mockInteropBootstrapOracle{ mockBootstrapOracle: mockBootstrapOracle{ l1Head: b.L1Head, @@ -140,7 +145,8 @@ func newMockInteropBootstrapOracle(b *BootInfoInterop, custom bool) *mockInterop l2Claim: b.Claim, l2ClaimBlockNumber: b.GameTimestamp, }, - custom: custom, + custom: custom, + l1ChainCfg: l1ChainCfg, } } @@ -148,6 +154,7 @@ type mockInteropBootstrapOracle struct { mockBootstrapOracle rollupCfgs []*rollup.Config chainCfgs []*params.ChainConfig + l1ChainCfg *params.ChainConfig depset *depset.StaticConfigDependencySet custom bool } @@ -174,6 +181,12 @@ func (o *mockInteropBootstrapOracle) Get(key preimage.Key) []byte { } b, _ := json.Marshal(o.depset) return b + case L1ChainConfigLocalIndex.PreimageKey(): + if !o.custom { + panic(fmt.Sprintf("unexpected oracle request for preimage key %x", key.PreimageKey())) + } + b, _ := json.Marshal(o.l1ChainCfg) + return b default: return o.mockBootstrapOracle.Get(key) } diff --git a/op-program/client/boot/boot_test.go b/op-program/client/boot/boot_test.go index 3d2be5512d740..df052e2aae5d6 100644 --- a/op-program/client/boot/boot_test.go +++ b/op-program/client/boot/boot_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum-optimism/optimism/op-program/chainconfig" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" ) @@ -22,6 +23,7 @@ func TestBootstrapClient(t *testing.T) { L2Claim: common.HexToHash("0x3333"), L2ClaimBlockNumber: 1, L2ChainID: eth.ChainIDFromBig(rollupCfg.L2ChainID), + L1ChainConfig: params.SepoliaChainConfig, L2ChainConfig: chainconfig.OPSepoliaChainConfig(), RollupConfig: rollupCfg, } @@ -37,6 +39,7 @@ func TestBootstrapClient_CustomChain(t *testing.T) { L2Claim: common.HexToHash("0x3333"), L2ClaimBlockNumber: 1, L2ChainID: CustomChainIDIndicator, + L1ChainConfig: params.SepoliaChainConfig, L2ChainConfig: chainconfig.OPSepoliaChainConfig(), RollupConfig: chaincfg.OPSepolia(), } @@ -45,6 +48,21 @@ func TestBootstrapClient_CustomChain(t *testing.T) { require.EqualValues(t, bootInfo, readBootInfo) } +func TestBootstrapClient_CustomChain_L1ChainConfigMismatch(t *testing.T) { + bootInfo := &BootInfo{ + L1Head: common.HexToHash("0x1111"), + L2OutputRoot: common.HexToHash("0x2222"), + L2Claim: common.HexToHash("0x3333"), + L2ClaimBlockNumber: 1, + L2ChainID: CustomChainIDIndicator, + L1ChainConfig: params.MainnetChainConfig, + L2ChainConfig: chainconfig.OPSepoliaChainConfig(), + RollupConfig: chaincfg.OPSepolia(), + } + mockOracle := newMockPreinteropBootstrapOracle(bootInfo, true) + require.Panics(t, func() { NewBootstrapClient(mockOracle).BootInfo() }) +} + func TestBootstrapClient_UnknownChainPanics(t *testing.T) { bootInfo := &BootInfo{ L1Head: common.HexToHash("0x1111"), @@ -87,6 +105,12 @@ func (o *mockPreinteropBootstrapOracle) Get(key preimage.Key) []byte { } b, _ := json.Marshal(o.b.L2ChainConfig) return b + case L1ChainConfigLocalIndex.PreimageKey(): + if !o.custom { + panic(fmt.Sprintf("unexpected oracle request for preimage key %x", key.PreimageKey())) + } + b, _ := json.Marshal(o.b.L1ChainConfig) + return b case RollupConfigLocalIndex.PreimageKey(): if !o.custom { panic(fmt.Sprintf("unexpected oracle request for preimage key %x", key.PreimageKey())) diff --git a/op-program/client/boot/common.go b/op-program/client/boot/common.go index a81e97e26e186..d2c9ba859c738 100644 --- a/op-program/client/boot/common.go +++ b/op-program/client/boot/common.go @@ -13,6 +13,7 @@ const ( L2ChainConfigLocalIndex RollupConfigLocalIndex DependencySetLocalIndex + L1ChainConfigLocalIndex ) type oracleClient interface { diff --git a/op-program/client/driver/driver.go b/op-program/client/driver/driver.go index d68f03978ed1c..a7f90784061bf 100644 --- a/op-program/client/driver/driver.go +++ b/op-program/client/driver/driver.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-node/metrics" @@ -33,13 +34,13 @@ type Driver struct { } func NewDriver(logger log.Logger, cfg *rollup.Config, depSet derive.DependencySet, l1Source derive.L1Fetcher, - l1BlobsSource derive.L1BlobsFetcher, l2Source engine.Engine, targetBlockNum uint64) *Driver { + l1BlobsSource derive.L1BlobsFetcher, l2Source engine.Engine, targetBlockNum uint64, l1ChainConfig *params.ChainConfig) *Driver { d := &Driver{ logger: logger, } - pipeline := derive.NewDerivationPipeline(logger, cfg, depSet, l1Source, l1BlobsSource, altda.Disabled, l2Source, metrics.NoopMetrics, false) + pipeline := derive.NewDerivationPipeline(logger, cfg, depSet, l1Source, l1BlobsSource, altda.Disabled, l2Source, metrics.NoopMetrics, false, l1ChainConfig) pipelineDeriver := derive.NewPipelineDeriver(context.Background(), pipeline) pipelineDeriver.AttachEmitter(d) diff --git a/op-program/client/interop/interop.go b/op-program/client/interop/interop.go index 00f191be35371..592967650038d 100644 --- a/op-program/client/interop/interop.go +++ b/op-program/client/interop/interop.go @@ -38,6 +38,7 @@ type taskExecutor interface { RunDerivation( logger log.Logger, rollupCfg *rollup.Config, + l1ChainConfig *params.ChainConfig, depSet depset.DependencySet, l2ChainConfig *params.ChainConfig, l1Head common.Hash, @@ -153,7 +154,11 @@ func deriveOptimisticBlock(logger log.Logger, bootInfo *boot.BootInfoInterop, l1 } l2ChainConfig, err := bootInfo.Configs.ChainConfig(chainAgreedPrestate.ChainID) if err != nil { - return types.OptimisticBlock{}, fmt.Errorf("no chain config available for chain ID %v: %w", chainAgreedPrestate.ChainID, err) + return types.OptimisticBlock{}, fmt.Errorf("no l2 chain config available for chain ID %v: %w", chainAgreedPrestate.ChainID, err) + } + l1ChainConfig, err := bootInfo.Configs.L1ChainConfig(eth.ChainIDFromBig(rollupCfg.L1ChainID)) + if err != nil { + return types.OptimisticBlock{}, fmt.Errorf("no l1 chain config available for chain ID %v: %w", eth.ChainIDFromBig(rollupCfg.L1ChainID), err) } depSet, err := bootInfo.Configs.DependencySet(chainAgreedPrestate.ChainID) if err != nil { @@ -166,6 +171,7 @@ func deriveOptimisticBlock(logger log.Logger, bootInfo *boot.BootInfoInterop, l1 derivationResult, err := tasks.RunDerivation( logger, rollupCfg, + l1ChainConfig, depSet, l2ChainConfig, bootInfo.L1Head, @@ -194,6 +200,7 @@ type interopTaskExecutor struct { func (t *interopTaskExecutor) RunDerivation( logger log.Logger, rollupCfg *rollup.Config, + l1ChainConfig *params.ChainConfig, depSet depset.DependencySet, l2ChainConfig *params.ChainConfig, l1Head common.Hash, @@ -205,6 +212,7 @@ func (t *interopTaskExecutor) RunDerivation( return tasks.RunDerivation( logger, rollupCfg, + l1ChainConfig, depSet, l2ChainConfig, l1Head, diff --git a/op-program/client/interop/interop_test.go b/op-program/client/interop/interop_test.go index e61aa18128975..d154d9fe75b53 100644 --- a/op-program/client/interop/interop_test.go +++ b/op-program/client/interop/interop_test.go @@ -82,10 +82,11 @@ func setupTwoChains(opts ...func(*chainSetupOpts)) (*staticConfigSource, *eth.Su }) } configSource := &staticConfigSource{ - rollupCfgs: []*rollup.Config{&rollupCfg1, &rollupCfg2}, - chainConfigs: []*params.ChainConfig{&chainCfg1, &chainCfg2}, - depset: ds, - chainIDs: []eth.ChainID{eth.ChainIDFromBig(rollupCfg1.L2ChainID), eth.ChainIDFromBig(rollupCfg2.L2ChainID)}, + rollupCfgs: []*rollup.Config{&rollupCfg1, &rollupCfg2}, + chainConfigs: []*params.ChainConfig{&chainCfg1, &chainCfg2}, + l1ChainConfig: params.SepoliaChainConfig, + depset: ds, + chainIDs: []eth.ChainID{eth.ChainIDFromBig(rollupCfg1.L2ChainID), eth.ChainIDFromBig(rollupCfg2.L2ChainID)}, } tasksStub := &stubTasks{ l2SafeHead: eth.L2BlockRef{Number: 918429823450218}, // Past the claimed block @@ -774,6 +775,7 @@ var _ taskExecutor = (*stubTasks)(nil) func (t *stubTasks) RunDerivation( _ log.Logger, _ *rollup.Config, + _ *params.ChainConfig, _ depset.DependencySet, _ *params.ChainConfig, _ common.Hash, @@ -835,10 +837,11 @@ func (t *stubTasks) ExpectBuildDepositOnlyBlock( } type staticConfigSource struct { - rollupCfgs []*rollup.Config - chainConfigs []*params.ChainConfig - depset *depset.StaticConfigDependencySet - chainIDs []eth.ChainID + rollupCfgs []*rollup.Config + chainConfigs []*params.ChainConfig + l1ChainConfig *params.ChainConfig + depset *depset.StaticConfigDependencySet + chainIDs []eth.ChainID } func (s *staticConfigSource) RollupConfig(chainID eth.ChainID) (*rollup.Config, error) { @@ -859,6 +862,10 @@ func (s *staticConfigSource) ChainConfig(chainID eth.ChainID) (*params.ChainConf panic(fmt.Sprintf("no chain config found for chain %d", chainID)) } +func (s *staticConfigSource) L1ChainConfig(l1ChainID eth.ChainID) (*params.ChainConfig, error) { + return s.l1ChainConfig, nil +} + func (s *staticConfigSource) DependencySet(chainID eth.ChainID) (depset.DependencySet, error) { return s.depset, nil } diff --git a/op-program/client/l2/engine_test.go b/op-program/client/l2/engine_test.go index 183465ec39fff..fbb7769b90833 100644 --- a/op-program/client/l2/engine_test.go +++ b/op-program/client/l2/engine_test.go @@ -190,10 +190,11 @@ func createOracleEngine(t *testing.T, headBlockOnIsthmus bool) (*OracleEngine, * } func createL2Block(t *testing.T, number int, setWithdrawalsRoot bool) *types.Block { - tx, err := derive.L1InfoDeposit(chaincfg.OPSepolia(), eth.SystemConfig{}, uint64(1), eth.HeaderBlockInfo(&types.Header{ - Number: big.NewInt(32), - BaseFee: big.NewInt(7), - }), 0) + tx, err := derive.L1InfoDeposit(chaincfg.OPSepolia(), params.MergedTestChainConfig, + eth.SystemConfig{}, uint64(1), eth.HeaderBlockInfo(&types.Header{ + Number: big.NewInt(32), + BaseFee: big.NewInt(7), + }), 0) require.NoError(t, err) header := &types.Header{ Number: big.NewInt(int64(number)), diff --git a/op-program/client/l2/engineapi/test/l2_engine_api_tests.go b/op-program/client/l2/engineapi/test/l2_engine_api_tests.go index 77168255e9629..062f70cd60070 100644 --- a/op-program/client/l2/engineapi/test/l2_engine_api_tests.go +++ b/op-program/client/l2/engineapi/test/l2_engine_api_tests.go @@ -39,7 +39,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi. api := newTestHelper(t, createBackend) genesis := api.backend.CurrentHeader() - txData, err := derive.L1InfoDeposit(rollupCfg, eth.SystemConfig{}, 1, eth.HeaderBlockInfo(genesis), 0) + txData, err := derive.L1InfoDeposit(rollupCfg, params.MergedTestChainConfig, eth.SystemConfig{}, 1, eth.HeaderBlockInfo(genesis), 0) api.assert.NoError(err) tx := types.NewTx(txData) block := api.addBlock(tx) @@ -57,7 +57,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi. api := newTestHelper(t, createBackend) genesis := api.backend.CurrentHeader() - txData, err := derive.L1InfoDeposit(rollupCfg, eth.SystemConfig{}, 1, eth.HeaderBlockInfo(genesis), 0) + txData, err := derive.L1InfoDeposit(rollupCfg, params.MergedTestChainConfig, eth.SystemConfig{}, 1, eth.HeaderBlockInfo(genesis), 0) api.assert.NoError(err) txData.Gas = uint64(gasLimit + 1) tx := types.NewTx(txData) diff --git a/op-program/client/preinterop.go b/op-program/client/preinterop.go index f99835fb0ec6b..b9057acc76d3b 100644 --- a/op-program/client/preinterop.go +++ b/op-program/client/preinterop.go @@ -22,6 +22,7 @@ func RunPreInteropProgram( result, err := tasks.RunDerivation( logger, bootInfo.RollupConfig, + bootInfo.L1ChainConfig, nil, // No dependency set pre-interop bootInfo.L2ChainConfig, bootInfo.L1Head, diff --git a/op-program/client/tasks/derive.go b/op-program/client/tasks/derive.go index 30aec383ff47e..6877b73c0f6a6 100644 --- a/op-program/client/tasks/derive.go +++ b/op-program/client/tasks/derive.go @@ -45,6 +45,7 @@ type DerivationOptions struct { func RunDerivation( logger log.Logger, cfg *rollup.Config, + l1ChainConfig *params.ChainConfig, depSet derive.DependencySet, l2Cfg *params.ChainConfig, l1Head common.Hash, @@ -63,7 +64,7 @@ func RunDerivation( l2Source := l2.NewOracleEngine(cfg, logger, engineBackend, l2Oracle.Hinter()) logger.Info("Starting derivation", "chainID", cfg.L2ChainID) - d := cldr.NewDriver(logger, cfg, depSet, l1Source, l1BlobsSource, l2Source, l2ClaimBlockNum) + d := cldr.NewDriver(logger, cfg, depSet, l1Source, l1BlobsSource, l2Source, l2ClaimBlockNum, l1ChainConfig) result, err := d.RunComplete() if err != nil { return DerivationResult{}, fmt.Errorf("failed to run program to completion: %w", err) diff --git a/op-program/host/cmd/main_test.go b/op-program/host/cmd/main_test.go index f2c5424ca37c3..dfd1d10c496e7 100644 --- a/op-program/host/cmd/main_test.go +++ b/op-program/host/cmd/main_test.go @@ -82,6 +82,7 @@ func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) { defaultCfg := config.NewSingleChainConfig( rollupCfg, chainconfig.OPSepoliaChainConfig(), + params.SepoliaChainConfig, common.HexToHash(l1HeadValue), common.HexToHash(l2HeadValue), common.HexToHash(l2OutputRoot), diff --git a/op-program/host/config/config.go b/op-program/host/config/config.go index f1cff823404d7..3aa5190bdcdc6 100644 --- a/op-program/host/config/config.go +++ b/op-program/host/config/config.go @@ -16,13 +16,13 @@ import ( "github.com/ethereum-optimism/optimism/op-program/client/boot" "github.com/ethereum-optimism/optimism/op-program/host/types" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-program/host/flags" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" @@ -86,6 +86,10 @@ type Config struct { // L2ChainConfigs are the op-geth chain config for the L2 execution engines // Must have one chain config for each rollup config L2ChainConfigs []*params.ChainConfig + // L1ChainConfig is the geth chain config for the L1 execution engine + // For interop, we only have one L1 chain config + // since all L2 chains must have the same L1 + L1ChainConfig *params.ChainConfig // ExecCmd specifies the client program to execute in a separate process. // If unset, the fault proof client is run in the same process. ExecCmd string @@ -183,6 +187,7 @@ func (c *Config) FetchingEnabled() bool { func NewSingleChainConfig( rollupCfg *rollup.Config, l2ChainConfig *params.ChainConfig, + l1ChainConfig *params.ChainConfig, l1Head common.Hash, l2Head common.Hash, l2OutputRoot common.Hash, @@ -198,6 +203,7 @@ func NewSingleChainConfig( cfg := NewConfig( []*rollup.Config{rollupCfg}, []*params.ChainConfig{l2ChainConfig}, + l1ChainConfig, l1Head, l2Head, l2OutputRoot, @@ -211,6 +217,7 @@ func NewSingleChainConfig( func NewConfig( rollupCfgs []*rollup.Config, l2ChainConfigs []*params.ChainConfig, + l1ChainConfig *params.ChainConfig, l1Head common.Hash, l2Head common.Hash, l2OutputRoot common.Hash, @@ -219,6 +226,7 @@ func NewConfig( ) *Config { return &Config{ Rollups: rollupCfgs, + L1ChainConfig: l1ChainConfig, L2ChainConfigs: l2ChainConfigs, L1Head: l1Head, L2Head: l2Head, @@ -289,7 +297,7 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) { chainID = eth.ChainIDFromUInt64(ch.ChainID) } - l2ChainConfig, err := chainconfig.ChainConfigByChainID(chainID) + l2ChainConfig, err := chainconfig.L2ChainConfigByChainID(chainID) if err != nil { return nil, fmt.Errorf("failed to load chain config for chain %d: %w", chainID, err) } @@ -300,6 +308,8 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) { } rollupCfgs = append(rollupCfgs, rollupCfg) + // L1 chain config resolution deferred until after all rollup configs are loaded + if interopEnabled { depSet, err := depset.FromRegistry(chainID) if err != nil && !errors.Is(err, superchain.ErrUnknownChain) { @@ -328,8 +338,38 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) { return nil, fmt.Errorf("invalid rollup config: %w", err) } rollupCfgs = append(rollupCfgs, rollupCfg) + } + // Resolve L1 chain config akin to op-node's NewL1ChainConfig + if len(rollupCfgs) == 0 { + return nil, fmt.Errorf("no rollup configs provided to resolve L1 chain config") + } + l1ChainIDBig := rollupCfgs[0].L1ChainID + l1ChainConfig := eth.L1ChainConfigByChainID(eth.ChainIDFromBig(l1ChainIDBig)) + if l1ChainConfig == nil { + // if the l1 chain config is not known, we fallback to the CLI flag if set... + if ctx.IsSet(flags.L1ChainConfig.Name) { + cf, err := loadL1ChainConfigFromFile(ctx.String(flags.L1ChainConfig.Name)) + if err != nil { + return nil, fmt.Errorf("invalid l1 chain config: %w", err) + } + if cf.ChainID.Cmp(l1ChainIDBig) != 0 { + return nil, fmt.Errorf("l1 chain config chain ID mismatch: %v != %v", cf.ChainID, l1ChainIDBig) + } + l1ChainConfig = cf + } else { + // ... or the program-embedded lookup if no CLI flag is set + lc, err := chainconfig.L1ChainConfigByChainID(eth.ChainIDFromBig(l1ChainIDBig)) + if err != nil { + return nil, fmt.Errorf("failed to load l1 chain config for chain %d: %w", eth.EvilChainIDToUInt64(eth.ChainIDFromBig(l1ChainIDBig)), err) + } + l1ChainConfig = lc + } + } + if l1ChainConfig == nil || l1ChainConfig.BlobScheduleConfig == nil { + return nil, fmt.Errorf("L1 chain config does not have a blob schedule config") } + if ctx.Bool(flags.L2Custom.Name) { log.Warn("Using custom chain configuration via preimage oracle. This is not compatible with on-chain execution.") l2ChainID = boot.CustomChainIDIndicator @@ -358,6 +398,7 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) { return &Config{ L2ChainID: l2ChainID, Rollups: rollupCfgs, + L1ChainConfig: l1ChainConfig, DataDir: ctx.String(flags.DataDir.Name), DataFormat: dbFormat, L2URLs: ctx.StringSlice(flags.L2NodeAddr.Name), @@ -381,16 +422,30 @@ func NewConfigFromCLI(log log.Logger, ctx *cli.Context) (*Config, error) { } func loadChainConfigFromGenesis(path string) (*params.ChainConfig, error) { - data, err := os.ReadFile(path) + cfg, err := jsonutil.LoadJSONFieldStrict[params.ChainConfig](path, "config") if err != nil { - return nil, fmt.Errorf("read l2 genesis file: %w", err) + return nil, fmt.Errorf("parse genesis file: %w", err) } - var genesis core.Genesis - err = json.Unmarshal(data, &genesis) + return cfg, nil +} + +// loadL1ChainConfigFromFile attempts to decode a file as a params.ChainConfig directly, +// and if that fails, it attempts to load the config from the .config field (genesis.json format). +func loadL1ChainConfigFromFile(path string) (*params.ChainConfig, error) { + file, err := os.Open(path) if err != nil { - return nil, fmt.Errorf("parse l2 genesis file: %w", err) + return nil, fmt.Errorf("failed to read chain spec: %w", err) } - return genesis.Config, nil + defer file.Close() + + var chainConfig params.ChainConfig + dec := json.NewDecoder(file) + dec.DisallowUnknownFields() + if err := dec.Decode(&chainConfig); err == nil { + return &chainConfig, nil + } + + return jsonutil.LoadJSONFieldStrict[params.ChainConfig](path, "config") } func loadRollupConfig(rollupConfigPath string) (*rollup.Config, error) { diff --git a/op-program/host/config/config_test.go b/op-program/host/config/config_test.go index 128183a6e552a..0dc868adb6c77 100644 --- a/op-program/host/config/config_test.go +++ b/op-program/host/config/config_test.go @@ -18,6 +18,7 @@ import ( ) var ( + validL1ChainConfig = params.SepoliaChainConfig validRollupConfig = chaincfg.OPSepolia() validL2Genesis = chainconfig.OPSepoliaChainConfig() validL1Head = common.Hash{0xaa} @@ -234,8 +235,9 @@ func TestCustomL2ChainID(t *testing.T) { require.Equal(t, cfg.L2ChainID, eth.ChainIDFromBig(validL2Genesis.ChainID)) }) t.Run("custom", func(t *testing.T) { - customChainConfig := ¶ms.ChainConfig{ChainID: big.NewInt(0x1212121212)} - cfg := NewSingleChainConfig(validRollupConfig, customChainConfig, validL1Head, validL2Head, validL2OutputRoot, validL2Claim, validL2ClaimBlockNum) + customL1ChainConfig := ¶ms.ChainConfig{ChainID: big.NewInt(0x1212121212)} + customL2ChainConfig := ¶ms.ChainConfig{ChainID: big.NewInt(0x2323232323)} + cfg := NewSingleChainConfig(validRollupConfig, customL1ChainConfig, customL2ChainConfig, validL1Head, validL2Head, validL2OutputRoot, validL2Claim, validL2ClaimBlockNum) require.Equal(t, cfg.L2ChainID, boot.CustomChainIDIndicator) }) } @@ -296,7 +298,7 @@ func TestDBFormat(t *testing.T) { } func validConfig() *Config { - cfg := NewSingleChainConfig(validRollupConfig, validL2Genesis, validL1Head, validL2Head, validL2OutputRoot, validL2Claim, validL2ClaimBlockNum) + cfg := NewSingleChainConfig(validRollupConfig, validL2Genesis, validL1ChainConfig, validL1Head, validL2Head, validL2OutputRoot, validL2Claim, validL2ClaimBlockNum) cfg.DataDir = "/tmp/configTest" return cfg } diff --git a/op-program/host/flags/flags.go b/op-program/host/flags/flags.go index baeb3d2f84f5a..01aab1990718c 100644 --- a/op-program/host/flags/flags.go +++ b/op-program/host/flags/flags.go @@ -34,6 +34,11 @@ var ( Usage: "Rollup chain parameters", EnvVars: prefixEnvVars("ROLLUP_CONFIG"), } + L1ChainConfig = &cli.StringFlag{ + Name: "l1.chainconfig", + Usage: "L1 chain config file (path to genesis.json)", + EnvVars: prefixEnvVars("L1_CHAINCONFIG"), + } Network = &cli.StringSliceFlag{ Name: "network", Usage: fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")), @@ -155,6 +160,7 @@ var programFlags = []cli.Flag{ L2AgreedPrestate, L2Custom, RollupConfig, + L1ChainConfig, Network, DataDir, DataFormat, diff --git a/op-program/host/host.go b/op-program/host/host.go index 4c43464d66741..be9063f316960 100644 --- a/op-program/host/host.go +++ b/op-program/host/host.go @@ -145,6 +145,13 @@ func (p *programExecutor) RunProgram( return fmt.Errorf("could not find rollup config in the host for chain ID %v", chainID) } + var l1ChainConfig *params.ChainConfig + if eth.ChainIDFromBig(p.cfg.L1ChainConfig.ChainID).Cmp(eth.ChainIDFromBig(rollupConfig.L1ChainID)) == 0 { + l1ChainConfig = p.cfg.L1ChainConfig + } else { + return fmt.Errorf("L1 chain config chain ID mismatch: %v != %v", eth.ChainIDFromBig(p.cfg.L1ChainConfig.ChainID), eth.ChainIDFromBig(rollupConfig.L1ChainID)) + } + prefetcherCreator := func(context.Context, log.Logger, kvstore.KV, *config.Config) (hostcommon.Prefetcher, error) { // TODO(#13663): prevent recursive block execution return prefetcher, nil @@ -165,6 +172,7 @@ func (p *programExecutor) RunProgram( result, err := tasks.RunDerivation( p.logger, rollupConfig, + l1ChainConfig, p.cfg.DependencySet, l2ChainConfig, p.cfg.L1Head, diff --git a/op-program/host/host_test.go b/op-program/host/host_test.go index 31f2de67bcc6d..d5153d243f010 100644 --- a/op-program/host/host_test.go +++ b/op-program/host/host_test.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" ) @@ -25,7 +26,7 @@ func TestServerMode(t *testing.T) { l1Head := common.Hash{0x11} l2OutputRoot := common.Hash{0x33} - cfg := config.NewSingleChainConfig(chaincfg.OPSepolia(), chainconfig.OPSepoliaChainConfig(), l1Head, common.Hash{0x22}, l2OutputRoot, common.Hash{0x44}, 1000) + cfg := config.NewSingleChainConfig(chaincfg.OPSepolia(), params.SepoliaChainConfig, chainconfig.OPSepoliaChainConfig(), l1Head, common.Hash{0x22}, l2OutputRoot, common.Hash{0x44}, 1000) cfg.DataDir = dir cfg.ServerMode = true diff --git a/op-program/host/kvstore/local.go b/op-program/host/kvstore/local.go index 4d52c6decebf9..823bf42431668 100644 --- a/op-program/host/kvstore/local.go +++ b/op-program/host/kvstore/local.go @@ -28,6 +28,7 @@ var ( l2ChainConfigKey = boot.L2ChainConfigLocalIndex.PreimageKey() rollupKey = boot.RollupConfigLocalIndex.PreimageKey() dependencySetKey = boot.DependencySetLocalIndex.PreimageKey() + l1ChainConfigKey = boot.L1ChainConfigLocalIndex.PreimageKey() ) func (s *LocalPreimageSource) Get(key common.Hash) ([]byte, error) { @@ -63,6 +64,12 @@ func (s *LocalPreimageSource) Get(key common.Hash) ([]byte, error) { return nil, errors.New("host is not configured to serve dependencySet local keys") } return json.Marshal(s.config.DependencySet) + case l1ChainConfigKey: + // NOTE: We check the L2 chain ID again to determine if we are using custom configs + if s.config.L2ChainID != boot.CustomChainIDIndicator { + return nil, ErrNotFound + } + return json.Marshal(s.config.L1ChainConfig) default: return nil, ErrNotFound } diff --git a/op-program/host/subcmds/configs_cmd.go b/op-program/host/subcmds/configs_cmd.go index 752a2af627a80..bb49850a56a16 100644 --- a/op-program/host/subcmds/configs_cmd.go +++ b/op-program/host/subcmds/configs_cmd.go @@ -97,7 +97,7 @@ func listChain(chainID eth.ChainID) error { return err } // Double check the L2 genesis is really available - _, err = chainconfig.ChainConfigByChainID(chainID) + _, err = chainconfig.L2ChainConfigByChainID(chainID) if err != nil { return err } @@ -131,12 +131,17 @@ func CheckCustomChains(ctx *cli.Context) error { errs = append(errs, err) continue } - _, err = chainconfig.ChainConfigByChainID(chainID) + _, err = chainconfig.L2ChainConfigByChainID(chainID) + if err != nil { + errs = append(errs, err) + continue + } + l1ChainID := eth.ChainIDFromBig(cfg.L1ChainID) + _, err = chainconfig.L1ChainConfigByChainID(l1ChainID) if err != nil { errs = append(errs, err) continue } - if cfg.InteropTime != nil { depset, err := chainconfig.DependencySetByChainID(chainID) if err != nil { diff --git a/op-program/verify/verify.go b/op-program/verify/verify.go index 5ba5ea77f715a..725b6f629c23e 100644 --- a/op-program/verify/verify.go +++ b/op-program/verify/verify.go @@ -36,6 +36,7 @@ type Runner struct { dataDir string network string chainCfg *params.ChainConfig + l1ChainCfg *params.ChainConfig l2Client *sources.L2Client logCfg oplog.CLIConfig setupLog log.Logger @@ -60,11 +61,16 @@ func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl s return nil, fmt.Errorf("failed to load rollup config: %w", err) } - chainCfg, err := chainconfig.ChainConfigByChainID(chainID) + chainCfg, err := chainconfig.L2ChainConfigByChainID(chainID) if err != nil { return nil, fmt.Errorf("failed to load chain config: %w", err) } + l1ChainCfg, err := chainconfig.L1ChainConfigByChainID(eth.ChainIDFromBig(rollupCfg.L1ChainID)) + if err != nil { + return nil, fmt.Errorf("failed to load l1 chain config: %w", err) + } + l2ClientCfg := sources.L2ClientDefaultConfig(rollupCfg, false) l2RPC := client.NewBaseRPCClient(l2RawRpc) l2Client, err := sources.NewL2Client(l2RPC, setupLog, nil, l2ClientCfg) @@ -84,6 +90,7 @@ func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl s setupLog: setupLog, l2Client: l2Client, rollupCfg: rollupCfg, + l1ChainCfg: l1ChainCfg, runInProcess: runInProcess, }, nil } @@ -210,7 +217,7 @@ func (r *Runner) run(ctx context.Context, l1Head common.Hash, agreedBlockInfo et if r.runInProcess { offlineCfg := config.NewSingleChainConfig( - r.rollupCfg, r.chainCfg, l1Head, agreedBlockInfo.Hash(), agreedOutputRoot, claimedOutputRoot, claimedBlockInfo.NumberU64()) + r.rollupCfg, r.chainCfg, r.l1ChainCfg, l1Head, agreedBlockInfo.Hash(), agreedOutputRoot, claimedOutputRoot, claimedBlockInfo.NumberU64()) offlineCfg.DataDir = r.dataDir onlineCfg := *offlineCfg diff --git a/op-service/eth/blob.go b/op-service/eth/blob.go index 142feba08968f..ff3eccdc4068b 100644 --- a/op-service/eth/blob.go +++ b/op-service/eth/blob.go @@ -283,36 +283,23 @@ func (b *Blob) Clear() { } } -// CalcBlobFeeDefault calculates the blob fee for the given header using eip4844.CalcBlobFee, -// using the requests hash field of the header as a best-effort heuristic whether -// Prague is active, and the default Ethereum blob schedule. -// -// This is to deal in a best-effort way with situations where the chain config is not -// available, but it can be assumed that per the definition of the Prague fork that -// Prague is active iff the requests hash field is present. -func CalcBlobFeeDefault(header *types.Header) *big.Int { - // We make the assumption that eip4844.CalcBlobFee only needs - // - London and Cancun to be active - // - the Prague time to be set relative to the header time - // and that the caller assumes the default prod Ethereum Blob schedule config. - dummyChainCfg := ¶ms.ChainConfig{ - LondonBlock: common.Big0, - CancunTime: ptr(uint64(0)), - BlobScheduleConfig: params.DefaultBlobSchedule, - } - // We assume that the requests hash is set iff Prague is active. - if header.RequestsHash != nil { - dummyChainCfg.PragueTime = ptr(uint64(0)) - } - return eip4844.CalcBlobFee(dummyChainCfg, header) -} - +// CalcBlobFeeCancun calculates the blob fee for the given header using +// the default blob schedule for Cancun. This function only exists +// to support the L1 Pectra Blob Schedule Fix. The geth function +// eip4844.CalcBlobFee should be used instead. func CalcBlobFeeCancun(excessBlobGas uint64) *big.Int { // Dummy Cancun header for calculation. cancunHeader := &types.Header{ ExcessBlobGas: &excessBlobGas, } - return CalcBlobFeeDefault(cancunHeader) + + // Dummy Cancun chain config for calculation. + dummyChainCfg := ¶ms.ChainConfig{ + LondonBlock: common.Big0, + CancunTime: ptr(uint64(0)), + BlobScheduleConfig: params.DefaultBlobSchedule, + } + return eip4844.CalcBlobFee(dummyChainCfg, cancunHeader) } func ptr[T any](t T) *T { return &t } diff --git a/op-service/eth/blob_test.go b/op-service/eth/blob_test.go index 5b77e9bd29a35..52849fca65a38 100644 --- a/op-service/eth/blob_test.go +++ b/op-service/eth/blob_test.go @@ -5,9 +5,10 @@ import ( "math/rand" "testing" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -296,18 +297,52 @@ func TestExtraneousData(t *testing.T) { } } -// TestCalcBlobFeeDefault ensures that the best-effort implementation of CalcBlobFeeDefault -// works as expected. In particular, this test will quickly fail and help detect any changes -// made to the internals of the upstream eip4844.CalcBlobFee function, on which -// CalcBlobFeeDefault relies on with certain assumptions. -func TestCalcBlobFeeDefault(t *testing.T) { - header := &types.Header{ - ExcessBlobGas: ptr(uint64(20 * params.DefaultCancunBlobConfig.UpdateFraction)), - } - cancunBlobFee := CalcBlobFeeDefault(header) +func TestCalcBlobFeeCancun(t *testing.T) { + cancunBlobFee := CalcBlobFeeCancun(uint64(20 * params.DefaultCancunBlobConfig.UpdateFraction)) require.Equal(t, big.NewInt(485165195), cancunBlobFee) +} + +// TestCalcBlobFeeAcrossForksWithFixedExcess tests the blob base fee calculation for different forks. +// Using the Sepolia chain config. +func TestCalcBlobFeeAcrossForksWithFixedExcess(t *testing.T) { + excess := uint64(40_000_000) + header := &types.Header{ExcessBlobGas: &excess, Time: 1754904516, Number: big.NewInt(1)} + cfg := params.SepoliaChainConfig + tests := []struct { + name string + blockTime uint64 + wantBF int64 + }{ + { + name: "Cancun", + blockTime: *cfg.CancunTime, + wantBF: 159773, + }, + { + name: "Prague", + blockTime: *cfg.PragueTime, + wantBF: 2944, + }, + { + name: "Osaka", + blockTime: *cfg.OsakaTime, + wantBF: 2944, + }, + { + name: "BPO1", + blockTime: *cfg.BPO1Time, + wantBF: 120, + }, + { + name: "BPO2", + blockTime: *cfg.BPO2Time, + wantBF: 30, + }, + } - header.RequestsHash = &(common.Hash{}) - pragueBlobFee := CalcBlobFeeDefault(header) - require.Equal(t, big.NewInt(617436), pragueBlobFee) + for _, tt := range tests { + header.Time = tt.blockTime + bf := eip4844.CalcBlobFee(cfg, header) + assert.Equal(t, tt.wantBF, bf.Int64()) + } } diff --git a/op-service/eth/block_info.go b/op-service/eth/block_info.go index 0b1c7c46c41c8..10e6f5761f369 100644 --- a/op-service/eth/block_info.go +++ b/op-service/eth/block_info.go @@ -4,7 +4,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -20,7 +22,7 @@ type BlockInfo interface { BaseFee() *big.Int // BlobBaseFee returns the result of computing the blob fee from excessDataGas, or nil if the // block isn't a Dencun (4844 capable) block - BlobBaseFee() *big.Int + BlobBaseFee(chainConfig *params.ChainConfig) *big.Int ExcessBlobGas() *uint64 ReceiptHash() common.Hash GasUsed() uint64 @@ -57,12 +59,12 @@ func ToBlockID(b NumberAndHash) BlockID { // blockInfo is a conversion type of types.Block turning it into a BlockInfo type blockInfo struct{ *types.Block } -func (b blockInfo) BlobBaseFee() *big.Int { +func (b blockInfo) BlobBaseFee(chainConfig *params.ChainConfig) *big.Int { ebg := b.ExcessBlobGas() if ebg == nil { return nil } - return CalcBlobFeeDefault(b.Header()) + return eip4844.CalcBlobFee(chainConfig, b.Header()) } func (b blockInfo) HeaderRLP() ([]byte, error) { @@ -124,11 +126,11 @@ func (h *headerBlockInfo) BaseFee() *big.Int { return h.header.BaseFee } -func (h *headerBlockInfo) BlobBaseFee() *big.Int { +func (h *headerBlockInfo) BlobBaseFee(chainConfig *params.ChainConfig) *big.Int { if h.header.ExcessBlobGas == nil { return nil } - return CalcBlobFeeDefault(h.header) + return eip4844.CalcBlobFee(chainConfig, h.header) } func (h *headerBlockInfo) ExcessBlobGas() *uint64 { diff --git a/op-service/eth/config.go b/op-service/eth/config.go new file mode 100644 index 0000000000000..6632bce12922e --- /dev/null +++ b/op-service/eth/config.go @@ -0,0 +1,23 @@ +package eth + +import ( + "github.com/ethereum/go-ethereum/params" +) + +// L1ChainConfigByChainID returns the chain config for the given chain ID, +// if it is in the set of known chain IDs (Mainnet, Sepolia, Holesky, Hoodi). +// If the chain ID is not known, it returns nil. +func L1ChainConfigByChainID(chainID ChainID) *params.ChainConfig { + switch chainID { + case ChainIDFromBig(params.MainnetChainConfig.ChainID): + return params.MainnetChainConfig + case ChainIDFromBig(params.SepoliaChainConfig.ChainID): + return params.SepoliaChainConfig + case ChainIDFromBig(params.HoleskyChainConfig.ChainID): + return params.HoleskyChainConfig + case ChainIDFromBig(params.HoodiChainConfig.ChainID): + return params.HoodiChainConfig + default: + return nil + } +} diff --git a/op-service/eth/config_test.go b/op-service/eth/config_test.go new file mode 100644 index 0000000000000..2ab4cd4192749 --- /dev/null +++ b/op-service/eth/config_test.go @@ -0,0 +1,31 @@ +package eth + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestL1ChainConfigByChainID(t *testing.T) { + tc := []struct { + chainID uint64 + expectedDepositContractAddress common.Address + shouldBeNil bool + }{ + {1, common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), false}, // Mainnet + {11155111, common.HexToAddress("0x7f02c3e3c98b133055b8b348b2ac625669ed295d"), false}, // Sepolia + {17000, common.HexToAddress("0x4242424242424242424242424242424242424242"), false}, // Holesky + {560048, common.HexToAddress("0x00000000219ab540356cBB839Cbe05303d7705Fa"), false}, // Hoodi + {560049, common.HexToAddress("0xdeadbeef"), true}, // Unknown + } + for _, tc := range tc { + config := L1ChainConfigByChainID(ChainIDFromUInt64(tc.chainID)) + + if tc.shouldBeNil { + require.Nil(t, config) + } else { + require.Equal(t, tc.expectedDepositContractAddress, config.DepositContractAddress) + } + } +} diff --git a/op-service/jsonutil/json.go b/op-service/jsonutil/json.go index 8549c170d42a5..01a2da9cc5fd2 100644 --- a/op-service/jsonutil/json.go +++ b/op-service/jsonutil/json.go @@ -1,6 +1,7 @@ package jsonutil import ( + "bytes" "encoding/json" "errors" "fmt" @@ -151,3 +152,43 @@ func write[X any](value X, target ioutil.OutputTarget, enc EncoderFactory) error } return nil } + +// LoadJSONFieldStrict loads a JSON file and strictly decodes a specific top-level field into X. +// The rest of the file is ignored, but the selected field must decode without unknown fields +// and without trailing data. The input can be compressed; decompression is handled automatically. +func LoadJSONFieldStrict[X any](inputPath string, field string) (*X, error) { + if inputPath == "" { + return nil, errors.New("no path specified") + } + f, err := ioutil.OpenDecompressed(inputPath) + if err != nil { + return nil, fmt.Errorf("failed to open file %q: %w", inputPath, err) + } + defer f.Close() + + // Decode only the top-level object to extract the desired field, ignoring others. + var top map[string]json.RawMessage + dec := json.NewDecoder(f) + if err := dec.Decode(&top); err != nil { + return nil, fmt.Errorf("failed to decode JSON: %w", err) + } + if _, err := dec.Token(); err != io.EOF { + return nil, errors.New("unexpected trailing data") + } + + raw, ok := top[field] + if !ok { + return nil, fmt.Errorf("missing JSON field %q", field) + } + + strict := json.NewDecoder(bytes.NewReader(raw)) + strict.DisallowUnknownFields() + var out X + if err := strict.Decode(&out); err != nil { + return nil, fmt.Errorf("failed to decode JSON field %q: %w", field, err) + } + if _, err := strict.Token(); err != io.EOF { + return nil, fmt.Errorf("unexpected trailing data in JSON field %q", field) + } + return &out, nil +} diff --git a/op-service/testutils/l1info.go b/op-service/testutils/l1info.go index cc41669ad051c..39b03f4e00511 100644 --- a/op-service/testutils/l1info.go +++ b/op-service/testutils/l1info.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" ) var _ eth.BlockInfo = &MockBlockInfo{} @@ -66,7 +67,7 @@ func (l *MockBlockInfo) BaseFee() *big.Int { return l.InfoBaseFee } -func (l *MockBlockInfo) BlobBaseFee() *big.Int { +func (l *MockBlockInfo) BlobBaseFee(chainConfig *params.ChainConfig) *big.Int { return l.InfoBlobBaseFee } diff --git a/op-service/txmgr/estimator.go b/op-service/txmgr/estimator.go index 627e69910443b..0a3aa02694054 100644 --- a/op-service/txmgr/estimator.go +++ b/op-service/txmgr/estimator.go @@ -4,8 +4,6 @@ import ( "context" "errors" "math/big" - - "github.com/ethereum-optimism/optimism/op-service/eth" ) type GasPriceEstimatorFn func(ctx context.Context, backend ETHBackend) (*big.Int, *big.Int, *big.Int, error) @@ -24,9 +22,9 @@ func DefaultGasPriceEstimatorFn(ctx context.Context, backend ETHBackend) (*big.I return nil, nil, nil, errors.New("txmgr does not support pre-london blocks that do not have a base fee") } - var blobFee *big.Int - if head.ExcessBlobGas != nil { - blobFee = eth.CalcBlobFeeDefault(head) + blobFee, err := backend.BlobBaseFee(ctx) + if err != nil { + return nil, nil, nil, err } return tip, head.BaseFee, blobFee, nil diff --git a/op-service/txmgr/queue_test.go b/op-service/txmgr/queue_test.go index 29c372365faed..193b6da17deb5 100644 --- a/op-service/txmgr/queue_test.go +++ b/op-service/txmgr/queue_test.go @@ -56,6 +56,10 @@ func (b *mockBackendWithNonce) NonceAt(ctx context.Context, account common.Addre return uint64(len(b.minedTxs)), nil } +func (b *mockBackendWithNonce) BlobBaseFee(ctx context.Context) (*big.Int, error) { + return big.NewInt(0), nil +} + func TestQueue_Send(t *testing.T) { testCases := []struct { name string // name of the test @@ -300,6 +304,10 @@ func (b *mockBackendWithConfirmationDelay) MineAll() { } } +func (b *mockBackendWithConfirmationDelay) BlobBaseFee(ctx context.Context) (*big.Int, error) { + return big.NewInt(0), nil +} + // Simple test that we can call q.Send() up to the maxPending limit without blocking. func TestQueue_Send_MaxPendingMetrics(t *testing.T) { maxPending := 5 diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index 74400cba47568..ec0d8a9286e5c 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -119,6 +119,7 @@ type ETHBackend interface { // TODO: Maybe need a generic interface to support different RPC providers HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) SuggestGasTipCap(ctx context.Context) (*big.Int, error) + BlobBaseFee(ctx context.Context) (*big.Int, error) // NonceAt returns the account nonce of the given account. // The block number can be nil, in which case the nonce is taken from the latest known block. NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) @@ -815,8 +816,11 @@ func (m *SimpleTxManager) queryReceipt(ctx context.Context, txHash common.Hash, } m.metr.RecordBaseFee(tip.BaseFee) - if tip.ExcessBlobGas != nil { - blobFee := eth.CalcBlobFeeDefault(tip) + + if blobFee, err := m.backend.BlobBaseFee(ctx); err != nil { + m.metr.RPCError() + m.l.Warn("Unable to fetch blob base fee", "err", err) + } else { m.metr.RecordBlobBaseFee(blobFee) } diff --git a/op-service/txmgr/txmgr_test.go b/op-service/txmgr/txmgr_test.go index f025c11a321e7..82707cf475ec4 100644 --- a/op-service/txmgr/txmgr_test.go +++ b/op-service/txmgr/txmgr_test.go @@ -131,7 +131,7 @@ type gasPricer struct { mineAtEpoch int64 baseGasTipFee *big.Int baseBaseFee *big.Int - excessBlobGas uint64 + blobBaseFee *big.Int err error mu sync.Mutex } @@ -146,9 +146,7 @@ func newGasPricer(mineAtEpoch int64) *gasPricer { mineAtEpoch: mineAtEpoch, baseGasTipFee: big.NewInt(baseGasTipFee), baseBaseFee: big.NewInt(baseBaseFee), - // Simulate 100 excess blobs, which results in a blobBaseFee of 50 wei. This default means - // blob txs will be subject to the geth minimum blobgas fee of 1 gwei. - excessBlobGas: 100 * (params.BlobTxBlobGasPerBlob), + blobBaseFee: big.NewInt(50), } } @@ -158,9 +156,8 @@ func (g *gasPricer) expGasFeeCap() *big.Int { } func (g *gasPricer) expBlobFeeCap() *big.Int { - _, _, excessBlobGas := g.feesForEpoch(g.mineAtEpoch) - // Needs to be adjusted when Prague gas pricing is needed. - return eth.CalcBlobFeeCancun(excessBlobGas) + _, _, blobBaseFee := g.feesForEpoch(g.mineAtEpoch) + return blobBaseFee } func (g *gasPricer) shouldMine(gasFeeCap *big.Int) bool { @@ -171,13 +168,14 @@ func (g *gasPricer) shouldMineBlobTx(gasFeeCap, blobFeeCap *big.Int) bool { return g.shouldMine(gasFeeCap) && g.expBlobFeeCap().Cmp(blobFeeCap) <= 0 } -func (g *gasPricer) feesForEpoch(epoch int64) (*big.Int, *big.Int, uint64) { +func (g *gasPricer) feesForEpoch(epoch int64) (*big.Int, *big.Int, *big.Int) { e := big.NewInt(epoch) epochBaseFee := new(big.Int).Mul(g.baseBaseFee, e) epochGasTipCap := new(big.Int).Mul(g.baseGasTipFee, e) epochGasFeeCap := calcGasFeeCap(epochBaseFee, epochGasTipCap) - epochExcessBlobGas := g.excessBlobGas * uint64(epoch) - return epochGasTipCap, epochGasFeeCap, epochExcessBlobGas + epochBlobBaseFee := new(big.Int).Mul(g.blobBaseFee, new(big.Int).Exp(big.NewInt(2), e, nil)) + + return epochGasTipCap, epochGasFeeCap, epochBlobBaseFee } func (g *gasPricer) baseFee() *big.Int { @@ -186,20 +184,14 @@ func (g *gasPricer) baseFee() *big.Int { return new(big.Int).Mul(g.baseBaseFee, big.NewInt(g.epoch)) } -func (g *gasPricer) excessblobgas() uint64 { - g.mu.Lock() - defer g.mu.Unlock() - return g.excessBlobGas * uint64(g.epoch) -} - -func (g *gasPricer) sample() (*big.Int, *big.Int, uint64) { +func (g *gasPricer) sample() (*big.Int, *big.Int, *big.Int) { g.mu.Lock() defer g.mu.Unlock() g.epoch++ - epochGasTipCap, epochGasFeeCap, epochExcessBlobGas := g.feesForEpoch(g.epoch) + epochGasTipCap, epochGasFeeCap, epochBlobBaseFee := g.feesForEpoch(g.epoch) - return epochGasTipCap, epochGasFeeCap, epochExcessBlobGas + return epochGasTipCap, epochGasFeeCap, epochBlobBaseFee } type minedTxInfo struct { @@ -274,11 +266,9 @@ func (b *mockBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*typ if number != nil { num.Set(number) } - bg := b.g.excessblobgas() return &types.Header{ - Number: num, - BaseFee: b.g.baseFee(), - ExcessBlobGas: &bg, + Number: num, + BaseFee: b.g.baseFee(), }, nil } @@ -346,6 +336,10 @@ func (b *mockBackend) TransactionReceipt(ctx context.Context, txHash common.Hash func (b *mockBackend) Close() { } +func (b *mockBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { + return big.NewInt(0), nil +} + type testSendVariantsFn func(ctx context.Context, h *testHarness, tx TxCandidate) (*types.Receipt, error) func testSendVariants(t *testing.T, testFn func(t *testing.T, send testSendVariantsFn)) { @@ -511,9 +505,7 @@ func TestTxMgrConfirmsBlobTxAtHigherGasPrice(t *testing.T) { h := newTestHarness(t) - gasTipCap, gasFeeCap, excessBlobGas := h.gasPricer.sample() - // Needs to be adjusted when testing with Prague activated on L1. - blobFeeCap := eth.CalcBlobFeeCancun(excessBlobGas) + gasTipCap, gasFeeCap, blobFeeCap := h.gasPricer.sample() t.Log("Blob fee cap:", blobFeeCap, "gasFeeCap:", gasFeeCap) tx := types.NewTx(&types.BlobTx{ @@ -1016,11 +1008,10 @@ func TestManagerErrsOnZeroConfs(t *testing.T) { // first call but a success on the second call. This allows us to test that the // inner loop of WaitMined properly handles this case. type failingBackend struct { - returnSuccessBlockNumber bool - returnSuccessHeader bool - returnSuccessReceipt bool - baseFee, gasTip *big.Int - excessBlobGas *uint64 + returnSuccessBlockNumber bool + returnSuccessHeader bool + returnSuccessReceipt bool + baseFee, gasTip, blobBaseFee *big.Int } // BlockNumber for the failingBackend returns errRpcFailure on the first @@ -1057,9 +1048,8 @@ func (b *failingBackend) HeaderByNumber(ctx context.Context, _ *big.Int) (*types } return &types.Header{ - Number: big.NewInt(1), - BaseFee: b.baseFee, - ExcessBlobGas: b.excessBlobGas, + Number: big.NewInt(1), + BaseFee: b.baseFee, }, nil } @@ -1094,6 +1084,10 @@ func (b *failingBackend) ChainID(ctx context.Context) (*big.Int, error) { func (b *failingBackend) Close() { } +func (b *failingBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { + return b.blobBaseFee, nil +} + // TestWaitMinedReturnsReceiptAfterFailure asserts that WaitMined is able to // recover from failed calls to the backend. It uses the failedBackend to // simulate an rpc call failure, followed by the successful return of a receipt. @@ -1315,12 +1309,10 @@ func testIncreaseGasPriceLimit(t *testing.T, lt gasPriceLimitTest) { borkedTip := int64(10) borkedFee := int64(45) - // simulate 100 excess blobs which yields a 50 wei blob base fee - borkedExcessBlobGas := uint64(100 * params.BlobTxBlobGasPerBlob) borkedBackend := failingBackend{ gasTip: big.NewInt(borkedTip), baseFee: big.NewInt(borkedFee), - excessBlobGas: &borkedExcessBlobGas, + blobBaseFee: big.NewInt(50), returnSuccessHeader: true, } diff --git a/op-test-sequencer/sequencer/backend/work/builders/standardbuilder/config.go b/op-test-sequencer/sequencer/backend/work/builders/standardbuilder/config.go index 1da2cbb4cfcef..3804eda73a2c4 100644 --- a/op-test-sequencer/sequencer/backend/work/builders/standardbuilder/config.go +++ b/op-test-sequencer/sequencer/backend/work/builders/standardbuilder/config.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum-optimism/optimism/op-test-sequencer/sequencer/backend/work" "github.com/ethereum-optimism/optimism/op-test-sequencer/sequencer/seqtypes" + "github.com/ethereum/go-ethereum/params" ) type Config struct { @@ -23,6 +24,8 @@ type Config struct { L2EL endpoint.MustRPC `yaml:"l2EL"` // L2 consensus-layer RPC endpoint L2CL endpoint.MustRPC `yaml:"l2CL"` + + L1ChainConfig *params.ChainConfig } func (c *Config) Start(ctx context.Context, id seqtypes.BuilderID, opts *work.ServiceOpts) (work.Builder, error) { @@ -87,7 +90,7 @@ func (c *Config) Start(ctx context.Context, id seqtypes.BuilderID, opts *work.Se if err != nil { return nil, err } - fb := derive.NewFetchingAttributesBuilder(cfg, depSet, l1Cl, l2Cl) + fb := derive.NewFetchingAttributesBuilder(cfg, c.L1ChainConfig, depSet, l1Cl, l2Cl) fb.TestSkipL1OriginCheck()