Commit 527bf6cd authored by Qi Zhou's avatar Qi Zhou Committed by GitHub

feat: op-program supports custom chain config (#12310)

* feat: op-program supports custom chain config

* address comments

* address comments

* update mainnet.go/sepolia.go

* Update op-program/chainconfig/chaincfg.go
Co-authored-by: default avatarAdrian Sutton <adrian@symphonious.net>

* more error info on config file not found

---------
Co-authored-by: default avatarQi Zhou <qizhou@ethstorage.io>
Co-authored-by: default avatarAdrian Sutton <adrian@symphonious.net>
parent dc125121
...@@ -51,6 +51,9 @@ test: ...@@ -51,6 +51,9 @@ test:
verify-sepolia: op-program-host op-program-client verify-sepolia: op-program-host op-program-client
env GO111MODULE=on go run ./verify/sepolia/cmd/sepolia.go --l1 $$SEPOLIA_L1URL --l1.beacon $$SEPOLIA_BEACON_URL --l2 $$SEPOLIA_L2URL --datadir /tmp/test-sepolia env GO111MODULE=on go run ./verify/sepolia/cmd/sepolia.go --l1 $$SEPOLIA_L1URL --l1.beacon $$SEPOLIA_BEACON_URL --l2 $$SEPOLIA_L2URL --datadir /tmp/test-sepolia
verify-devnet:
env GO111MODULE=on go run ./verify/devnet/cmd/devnet.go --l1 http://localhost:8545 --l1.beacon http://localhost:5052 --l2 http://localhost:9545 --datadir /tmp/test-devnet
capture-mainnet-genesis: op-program-host op-program-client capture-mainnet-genesis: op-program-host op-program-client
rm -rf "$(COMPAT_DIR)/mainnet-genesis" "$(COMPAT_DIR)/mainnet-genesis.tar.bz" rm -rf "$(COMPAT_DIR)/mainnet-genesis" "$(COMPAT_DIR)/mainnet-genesis.tar.bz"
env GO111MODULE=on go run ./verify/mainnet/cmd/mainnet.go --l1 $$MAINNET_L1URL --l1.beacon $$MAINNET_BEACON_URL --l2 $$MAINNET_L2URL --datadir "$(COMPAT_DIR)/mainnet-genesis" --l1.head "0x4903424f6cc2cfba7c2bf8c8f48ca46721c963fa64b411cfee3697b781e3e5f1" --l2.start "105235063" --l2.end "105235064" env GO111MODULE=on go run ./verify/mainnet/cmd/mainnet.go --l1 $$MAINNET_L1URL --l1.beacon $$MAINNET_BEACON_URL --l2 $$MAINNET_L2URL --datadir "$(COMPAT_DIR)/mainnet-genesis" --l1.head "0x4903424f6cc2cfba7c2bf8c8f48ca46721c963fa64b411cfee3697b781e3e5f1" --l2.start "105235063" --l2.end "105235064"
...@@ -97,6 +100,7 @@ verify-compat: verify-sepolia-delta verify-sepolia-ecotone verify-mainnet-genesi ...@@ -97,6 +100,7 @@ verify-compat: verify-sepolia-delta verify-sepolia-ecotone verify-mainnet-genesi
test \ test \
capture-goerli-verify \ capture-goerli-verify \
verify-sepolia \ verify-sepolia \
verify-devnet \
capture-mainnet-genesis \ capture-mainnet-genesis \
capture-sepolia-delta \ capture-sepolia-delta \
capture-sepolia-ecotone \ capture-sepolia-ecotone \
......
package chainconfig package chainconfig
import ( import (
"embed"
"encoding/json"
"errors"
"fmt" "fmt"
"os"
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
...@@ -13,16 +18,56 @@ func OPSepoliaChainConfig() *params.ChainConfig { ...@@ -13,16 +18,56 @@ func OPSepoliaChainConfig() *params.ChainConfig {
return mustLoadChainConfig("op-sepolia") return mustLoadChainConfig("op-sepolia")
} }
//go:embed configs/*json
var customChainConfigFS embed.FS
func RollupConfigByChainID(chainID uint64) (*rollup.Config, error) { func RollupConfigByChainID(chainID uint64) (*rollup.Config, error) {
config, err := rollup.LoadOPStackRollupConfig(chainID) config, err := rollup.LoadOPStackRollupConfig(chainID)
if err != nil { if err == nil {
return config, err
}
return rollupConfigByChainID(chainID, customChainConfigFS)
}
func rollupConfigByChainID(chainID uint64, customChainFS embed.FS) (*rollup.Config, error) {
// Load custom rollup configs from embed FS
file, err := customChainFS.Open(fmt.Sprintf("configs/%d-rollup.json", chainID))
if errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("no rollup config available for chain ID: %d", chainID)
} else if err != nil {
return nil, fmt.Errorf("failed to get rollup config for chain ID %d: %w", chainID, err) return nil, fmt.Errorf("failed to get rollup config for chain ID %d: %w", chainID, err)
} }
return config, nil dec := json.NewDecoder(file)
dec.DisallowUnknownFields()
var customRollupConfig rollup.Config
if err := dec.Decode(&customRollupConfig); err != nil {
return nil, fmt.Errorf("failed to parse rollup config for chain ID %d: %w", chainID, err)
}
return &customRollupConfig, nil
} }
func ChainConfigByChainID(chainID uint64) (*params.ChainConfig, error) { func ChainConfigByChainID(chainID uint64) (*params.ChainConfig, error) {
return params.LoadOPStackChainConfig(chainID) config, err := params.LoadOPStackChainConfig(chainID)
if err == nil {
return config, err
}
return chainConfigByChainID(chainID, customChainConfigFS)
}
func chainConfigByChainID(chainID uint64, customChainFS embed.FS) (*params.ChainConfig, error) {
// Load from custom chain configs from embed FS
data, err := customChainFS.ReadFile(fmt.Sprintf("configs/%d-genesis-l2.json", chainID))
if errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("no chain config available for chain ID: %d", chainID)
} else if err != nil {
return nil, fmt.Errorf("failed to get chain config for chain ID %d: %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 %d: %w", chainID, err)
}
return genesis.Config, nil
} }
func mustLoadChainConfig(name string) *params.ChainConfig { func mustLoadChainConfig(name string) *params.ChainConfig {
......
package chainconfig
import (
"testing"
"github.com/ethereum-optimism/optimism/op-program/chainconfig/test"
"github.com/stretchr/testify/require"
)
// TestGetCustomRollupConfig tests loading the custom rollup configs from test embed FS.
func TestGetCustomRollupConfig(t *testing.T) {
config, err := rollupConfigByChainID(901, test.TestCustomChainConfigFS)
require.NoError(t, err)
require.Equal(t, config.L1ChainID.Uint64(), uint64(900))
require.Equal(t, config.L2ChainID.Uint64(), uint64(901))
_, err = rollupConfigByChainID(900, test.TestCustomChainConfigFS)
require.Error(t, err)
}
// TestGetCustomChainConfig tests loading the custom chain configs from test embed FS.
func TestGetCustomChainConfig(t *testing.T) {
config, err := chainConfigByChainID(901, test.TestCustomChainConfigFS)
require.NoError(t, err)
require.Equal(t, config.ChainID.Uint64(), uint64(901))
_, err = chainConfigByChainID(900, test.TestCustomChainConfigFS)
require.Error(t, err)
}
This diff is collapsed.
{
"genesis": {
"l1": {
"hash": "0xf39446e09aeca67452545d06a6e6a6a11184575ecf421f9306cf3602febf93ba",
"number": 1
},
"l2": {
"hash": "0x2a92ff72dad302d39fa80ef81522f0ccb27dc903255b618dfc4feddb22a8f80d",
"number": 0
},
"l2_time": 1728358574,
"system_config": {
"batcherAddr": "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc",
"overhead": "0x0000000000000000000000000000000000000000000000000000000000000834",
"scalar": "0x00000000000000000000000000000000000000000000000000000000000f4240",
"gasLimit": 30000000
}
},
"block_time": 2,
"max_sequencer_drift": 300,
"seq_window_size": 200,
"channel_timeout": 120,
"l1_chain_id": 900,
"l2_chain_id": 901,
"regolith_time": 0,
"canyon_time": 0,
"delta_time": 0,
"ecotone_time": 0,
"fjord_time": 0,
"batch_inbox_address": "0xff00000000000000000000000000000000000901",
"deposit_contract_address": "0x55bdfb0bfef1070c457124920546359426153833",
"l1_system_config_address": "0x3649f526889a918af0a5498706db29e81bc91e0c",
"protocol_versions_address": "0x0000000000000000000000000000000000000000"
}
package test
import "embed"
//go:embed configs/*json
var TestCustomChainConfigFS embed.FS
package main
import (
"context"
"flag"
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-program/verify"
"github.com/ethereum/go-ethereum/common"
)
func main() {
var l1RpcUrl string
var l1RpcKind string
var l1BeaconUrl string
var l2RpcUrl string
var dataDir string
var l1HashStr string
var l2Start uint64
var l2End uint64
flag.StringVar(&l1RpcUrl, "l1", "", "L1 RPC URL to use")
flag.StringVar(&l1BeaconUrl, "l1.beacon", "", "L1 Beacon URL to use")
flag.StringVar(&l1RpcKind, "l1-rpckind", "", "L1 RPC kind")
flag.StringVar(&l2RpcUrl, "l2", "", "L2 RPC URL to use")
flag.StringVar(&dataDir, "datadir", "",
"Directory to use for storing pre-images. If not set a temporary directory will be used.")
flag.StringVar(&l1HashStr, "l1.head", "", "Hash of L1 block to use")
flag.Uint64Var(&l2Start, "l2.start", 0, "Block number of agreed L2 block")
flag.Uint64Var(&l2End, "l2.end", 0, "Block number of claimed L2 block")
flag.Parse()
if l1RpcUrl == "" {
_, _ = fmt.Fprintln(os.Stderr, "Must specify --l1 RPC URL")
os.Exit(2)
}
if l1BeaconUrl == "" {
_, _ = fmt.Fprintln(os.Stderr, "Must specify --l1.beacon URL")
os.Exit(2)
}
if l2RpcUrl == "" {
_, _ = fmt.Fprintln(os.Stderr, "Must specify --l2 RPC URL")
os.Exit(2)
}
// Apply the custom configs by running op-program in the same process
runner, err := verify.NewRunner(l1RpcUrl, l1RpcKind, l1BeaconUrl, l2RpcUrl, dataDir, "", 901, true)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to create runner: %v\n", err.Error())
os.Exit(1)
}
if l1HashStr == "" && l2Start == 0 && l2End == 0 {
err = runner.RunToFinalized(context.Background())
} else {
l1Hash := common.HexToHash(l1HashStr)
if l1Hash == (common.Hash{}) {
_, _ = fmt.Fprintf(os.Stderr, "Invalid --l1.head: %v\n", l1HashStr)
os.Exit(2)
}
err = runner.RunBetweenBlocks(context.Background(), l1Hash, l2Start, l2End)
}
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed: %v\n", err.Error())
os.Exit(1)
}
}
...@@ -45,7 +45,7 @@ func main() { ...@@ -45,7 +45,7 @@ func main() {
os.Exit(2) os.Exit(2)
} }
runner, err := verify.NewRunner(l1RpcUrl, l1RpcKind, l1BeaconUrl, l2RpcUrl, dataDir, "op-mainnet", opMainnetChainID) runner, err := verify.NewRunner(l1RpcUrl, l1RpcKind, l1BeaconUrl, l2RpcUrl, dataDir, "op-mainnet", opMainnetChainID, false)
if err != nil { if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to create runner: %v\n", err.Error()) _, _ = fmt.Fprintf(os.Stderr, "Failed to create runner: %v\n", err.Error())
os.Exit(1) os.Exit(1)
......
...@@ -45,7 +45,7 @@ func main() { ...@@ -45,7 +45,7 @@ func main() {
os.Exit(2) os.Exit(2)
} }
runner, err := verify.NewRunner(l1RpcUrl, l1RpcKind, l1BeaconUrl, l2RpcUrl, dataDir, "op-sepolia", opSepoliaChainID) runner, err := verify.NewRunner(l1RpcUrl, l1RpcKind, l1BeaconUrl, l2RpcUrl, dataDir, "op-sepolia", opSepoliaChainID, false)
if err != nil { if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to create runner: %v\n", err.Error()) _, _ = fmt.Fprintf(os.Stderr, "Failed to create runner: %v\n", err.Error())
os.Exit(1) os.Exit(1)
......
...@@ -28,23 +28,22 @@ import ( ...@@ -28,23 +28,22 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
const runInProcess = false
type Runner struct { type Runner struct {
l1RpcUrl string l1RpcUrl string
l1RpcKind string l1RpcKind string
l1BeaconUrl string l1BeaconUrl string
l2RpcUrl string l2RpcUrl string
dataDir string dataDir string
network string network string
chainCfg *params.ChainConfig chainCfg *params.ChainConfig
l2Client *sources.L2Client l2Client *sources.L2Client
logCfg oplog.CLIConfig logCfg oplog.CLIConfig
setupLog log.Logger setupLog log.Logger
rollupCfg *rollup.Config rollupCfg *rollup.Config
runInProcess bool
} }
func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl string, dataDir string, network string, chainID uint64) (*Runner, error) { func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl string, dataDir string, network string, chainID uint64, runInProcess bool) (*Runner, error) {
ctx := context.Background() ctx := context.Background()
logCfg := oplog.DefaultCLIConfig() logCfg := oplog.DefaultCLIConfig()
logCfg.Level = log.LevelDebug logCfg.Level = log.LevelDebug
...@@ -56,7 +55,7 @@ func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl s ...@@ -56,7 +55,7 @@ func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl s
return nil, fmt.Errorf("dial L2 client: %w", err) return nil, fmt.Errorf("dial L2 client: %w", err)
} }
rollupCfg, err := rollup.LoadOPStackRollupConfig(chainID) rollupCfg, err := chainconfig.RollupConfigByChainID(chainID)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load rollup config: %w", err) return nil, fmt.Errorf("failed to load rollup config: %w", err)
} }
...@@ -74,17 +73,18 @@ func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl s ...@@ -74,17 +73,18 @@ func NewRunner(l1RpcUrl string, l1RpcKind string, l1BeaconUrl string, l2RpcUrl s
} }
return &Runner{ return &Runner{
l1RpcUrl: l1RpcUrl, l1RpcUrl: l1RpcUrl,
l1RpcKind: l1RpcKind, l1RpcKind: l1RpcKind,
l1BeaconUrl: l1BeaconUrl, l1BeaconUrl: l1BeaconUrl,
l2RpcUrl: l2RpcUrl, l2RpcUrl: l2RpcUrl,
dataDir: dataDir, dataDir: dataDir,
network: network, network: network,
chainCfg: chainCfg, chainCfg: chainCfg,
logCfg: logCfg, logCfg: logCfg,
setupLog: setupLog, setupLog: setupLog,
l2Client: l2Client, l2Client: l2Client,
rollupCfg: rollupCfg, rollupCfg: rollupCfg,
runInProcess: runInProcess,
}, nil }, nil
} }
...@@ -208,7 +208,7 @@ func (r *Runner) run(ctx context.Context, l1Head common.Hash, agreedBlockInfo et ...@@ -208,7 +208,7 @@ func (r *Runner) run(ctx context.Context, l1Head common.Hash, agreedBlockInfo et
} }
fmt.Printf("Configuration: %s\n", argsStr) fmt.Printf("Configuration: %s\n", argsStr)
if runInProcess { if r.runInProcess {
offlineCfg := config.NewConfig( offlineCfg := config.NewConfig(
r.rollupCfg, r.chainCfg, l1Head, agreedBlockInfo.Hash(), agreedOutputRoot, claimedOutputRoot, claimedBlockInfo.NumberU64()) r.rollupCfg, r.chainCfg, l1Head, agreedBlockInfo.Hash(), agreedOutputRoot, claimedOutputRoot, claimedBlockInfo.NumberU64())
offlineCfg.DataDir = r.dataDir offlineCfg.DataDir = r.dataDir
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment