Skip to content

Commit 544c42b

Browse files
authored
op-program: Update cli flags to support multiple chains (#13740)
* op-program: Validate that we have L2 chain configs for each rollup * op-program: Update CLI flags to support specifying multiple L2 chains * op-program: Allow custom l2 chain ID flag
1 parent 5a65359 commit 544c42b

File tree

4 files changed

+218
-78
lines changed

4 files changed

+218
-78
lines changed

op-program/host/cmd/main_test.go

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package main
33
import (
44
"encoding/json"
55
"fmt"
6+
"math/big"
67
"os"
78
"strconv"
9+
"strings"
810
"testing"
911

1012
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
13+
"github.com/ethereum-optimism/optimism/op-node/rollup"
1114
"github.com/ethereum-optimism/optimism/op-program/chainconfig"
1215
"github.com/ethereum-optimism/optimism/op-program/client/boot"
1316
"github.com/ethereum-optimism/optimism/op-program/host/config"
@@ -90,12 +93,10 @@ func TestNetwork(t *testing.T) {
9093
verifyArgsInvalid(t, "invalid network: \"bar\"", replaceRequiredArg("--network", "bar"))
9194
})
9295

93-
t.Run("Required", func(t *testing.T) {
94-
verifyArgsInvalid(t, "flag rollup.config or network is required", addRequiredArgsExcept("--network"))
95-
})
96-
97-
t.Run("DisallowNetworkAndRollupConfig", func(t *testing.T) {
98-
verifyArgsInvalid(t, "cannot specify both rollup.config and network", addRequiredArgs("--rollup.config=foo"))
96+
t.Run("AllowNetworkAndRollupConfig", func(t *testing.T) {
97+
configFile, rollupCfg := writeRollupConfigWithChainID(t, 4297842)
98+
cfg := configForArgs(t, addRequiredArgs("--rollup.config", configFile))
99+
require.Equal(t, []*rollup.Config{chaincfg.OPSepolia(), rollupCfg}, cfg.Rollups)
99100
})
100101

101102
t.Run("RollupConfig", func(t *testing.T) {
@@ -107,6 +108,15 @@ func TestNetwork(t *testing.T) {
107108
require.Equal(t, *chaincfg.OPSepolia(), *cfg.Rollups[0])
108109
})
109110

111+
t.Run("Multiple", func(t *testing.T) {
112+
cfg := configForArgs(t, addRequiredArgsExcept("--network", "--network=op-mainnet,op-sepolia"))
113+
require.Len(t, cfg.Rollups, 2)
114+
opMainnetCfg, err := chaincfg.GetRollupConfig("op-mainnet")
115+
require.NoError(t, err)
116+
require.Equal(t, *opMainnetCfg, *cfg.Rollups[0])
117+
require.Equal(t, *chaincfg.OPSepolia(), *cfg.Rollups[1])
118+
})
119+
110120
for _, name := range chaincfg.AvailableNetworks() {
111121
name := name
112122
expected, err := chaincfg.GetRollupConfig(name)
@@ -141,17 +151,20 @@ func TestDataFormat(t *testing.T) {
141151
}
142152

143153
func TestL2(t *testing.T) {
144-
expected := "https://example.com:8545"
145-
cfg := configForArgs(t, addRequiredArgs("--l2", expected))
146-
require.Equal(t, []string{expected}, cfg.L2URLs)
147-
}
154+
t.Run("Single", func(t *testing.T) {
155+
expected := "https://example.com:8545"
156+
cfg := configForArgs(t, addRequiredArgs("--l2", expected))
157+
require.Equal(t, []string{expected}, cfg.L2URLs)
158+
})
148159

149-
func TestL2Genesis(t *testing.T) {
150-
t.Run("RequiredWithCustomNetwork", func(t *testing.T) {
151-
rollupCfgFile := writeValidRollupConfig(t)
152-
verifyArgsInvalid(t, "flag l2.genesis is required", addRequiredArgsExcept("--network", "--rollup.config", rollupCfgFile))
160+
t.Run("Multiple", func(t *testing.T) {
161+
expected := []string{"https://example.com:8545", "https://example.com:9000"}
162+
cfg := configForArgs(t, addRequiredArgs("--l2", strings.Join(expected, ",")))
163+
require.Equal(t, expected, cfg.L2URLs)
153164
})
165+
}
154166

167+
func TestL2Genesis(t *testing.T) {
155168
t.Run("Valid", func(t *testing.T) {
156169
rollupCfgFile := writeValidRollupConfig(t)
157170
genesisFile := writeValidGenesis(t)
@@ -165,6 +178,31 @@ func TestL2Genesis(t *testing.T) {
165178
})
166179
}
167180

181+
func TestMultipleNetworkConfigs(t *testing.T) {
182+
t.Run("MultipleCustomChains", func(t *testing.T) {
183+
rollupFile1, rollupCfg1 := writeRollupConfigWithChainID(t, 1)
184+
rollupFile2, rollupCfg2 := writeRollupConfigWithChainID(t, 2)
185+
genesisFile1, chainCfg1 := writeGenesisFileWithChainID(t, 1)
186+
genesisFile2, chainCfg2 := writeGenesisFileWithChainID(t, 2)
187+
cfg := configForArgs(t, addRequiredArgsExcept("--network",
188+
"--rollup.config", rollupFile1+","+rollupFile2,
189+
"--l2.genesis", genesisFile1+","+genesisFile2))
190+
require.Equal(t, []*rollup.Config{rollupCfg1, rollupCfg2}, cfg.Rollups)
191+
require.Equal(t, []*params.ChainConfig{chainCfg1, chainCfg2}, cfg.L2ChainConfigs)
192+
})
193+
194+
t.Run("MixNetworkAndCustomChains", func(t *testing.T) {
195+
rollupFile, rollupCfg := writeRollupConfigWithChainID(t, 1)
196+
genesisFile, chainCfg := writeGenesisFileWithChainID(t, 1)
197+
cfg := configForArgs(t, addRequiredArgsExcept("--network",
198+
"--network", "op-sepolia",
199+
"--rollup.config", rollupFile,
200+
"--l2.genesis", genesisFile))
201+
require.Equal(t, []*rollup.Config{chaincfg.OPSepolia(), rollupCfg}, cfg.Rollups)
202+
require.Equal(t, []*params.ChainConfig{chainconfig.OPSepoliaChainConfig(), chainCfg}, cfg.L2ChainConfigs)
203+
})
204+
}
205+
168206
func TestL2ChainID(t *testing.T) {
169207
t.Run("DefaultToNetworkChainID", func(t *testing.T) {
170208
cfg := configForArgs(t, replaceRequiredArg("--network", "op-mainnet"))
@@ -187,11 +225,24 @@ func TestL2ChainID(t *testing.T) {
187225
"--l2.custom"))
188226
require.Equal(t, boot.CustomChainIDIndicator, cfg.L2ChainID)
189227
})
228+
229+
t.Run("ZeroWhenMultipleL2ChainsSpecified", func(t *testing.T) {
230+
cfg := configForArgs(t, addRequiredArgsExcept("--network", "--network", "op-sepolia,op-mainnet"))
231+
require.Zero(t, cfg.L2ChainID)
232+
})
190233
}
191234

192235
func TestL2Head(t *testing.T) {
193-
t.Run("Required", func(t *testing.T) {
194-
verifyArgsInvalid(t, "flag l2.head is required", addRequiredArgsExcept("--l2.head"))
236+
t.Run("RequiredWithOutputRoot", func(t *testing.T) {
237+
verifyArgsInvalid(t, "flag l2.head is required when l2.outputroot is specified", addRequiredArgsExcept("--l2.head"))
238+
})
239+
240+
t.Run("NotAllowedWithAgreedPrestate", func(t *testing.T) {
241+
req := requiredArgs()
242+
delete(req, "--l2.head")
243+
delete(req, "--l2.outputroot")
244+
args := append(toArgList(req), "--l2.head", l2HeadValue, "--l2.agreed-prestate", "0x1234")
245+
verifyArgsInvalid(t, "flag l2.head and l2.agreed-prestate must not be specified together", args)
195246
})
196247

197248
t.Run("Valid", func(t *testing.T) {
@@ -210,7 +261,7 @@ func TestL2OutputRoot(t *testing.T) {
210261
})
211262

212263
t.Run("NotRequiredWhenAgreedPrestateProvided", func(t *testing.T) {
213-
configForArgs(t, addRequiredArgsExcept("--l2.outputroot", "--l2.agreed-prestate", "0x1234"))
264+
configForArgs(t, addRequiredArgsExceptMultiple([]string{"--l2.outputroot", "--l2.head"}, "--l2.agreed-prestate", "0x1234"))
214265
})
215266

216267
t.Run("Valid", func(t *testing.T) {
@@ -225,14 +276,14 @@ func TestL2OutputRoot(t *testing.T) {
225276

226277
func TestL2AgreedPrestate(t *testing.T) {
227278
t.Run("NotRequiredWhenL2OutputRootProvided", func(t *testing.T) {
228-
configForArgs(t, addRequiredArgsExcept("--l2.outputroot", "--l2.outputroot", "0x1234"))
279+
configForArgs(t, addRequiredArgsExceptMultiple([]string{"--l2.outputroot", "--l2.head"}, "--l2.agreed-prestate", "0x1234"))
229280
})
230281

231282
t.Run("Valid", func(t *testing.T) {
232283
prestate := "0x1234"
233284
prestateBytes := common.FromHex(prestate)
234285
expectedOutputRoot := crypto.Keccak256Hash(prestateBytes)
235-
cfg := configForArgs(t, addRequiredArgsExcept("--l2.outputroot", "--l2.agreed-prestate", prestate))
286+
cfg := configForArgs(t, addRequiredArgsExceptMultiple([]string{"--l2.outputroot", "--l2.head"}, "--l2.agreed-prestate", prestate))
236287
require.Equal(t, expectedOutputRoot, cfg.L2OutputRoot)
237288
require.Equal(t, prestateBytes, cfg.AgreedPrestate)
238289
})
@@ -242,11 +293,11 @@ func TestL2AgreedPrestate(t *testing.T) {
242293
})
243294

244295
t.Run("Invalid", func(t *testing.T) {
245-
verifyArgsInvalid(t, config.ErrInvalidAgreedPrestate.Error(), addRequiredArgsExcept("--l2.outputroot", "--l2.agreed-prestate", "something"))
296+
verifyArgsInvalid(t, config.ErrInvalidAgreedPrestate.Error(), addRequiredArgsExceptMultiple([]string{"--l2.outputroot", "--l2.head"}, "--l2.agreed-prestate", "something"))
246297
})
247298

248299
t.Run("ZeroLength", func(t *testing.T) {
249-
verifyArgsInvalid(t, config.ErrInvalidAgreedPrestate.Error(), addRequiredArgsExcept("--l2.outputroot", "--l2.agreed-prestate", "0x"))
300+
verifyArgsInvalid(t, config.ErrInvalidAgreedPrestate.Error(), addRequiredArgsExceptMultiple([]string{"--l2.outputroot", "--l2.head"}, "--l2.agreed-prestate", "0x"))
250301
})
251302
}
252303

@@ -345,6 +396,12 @@ func TestL2Experimental(t *testing.T) {
345396
cfg := configForArgs(t, addRequiredArgs("--l2.experimental", expected))
346397
require.EqualValues(t, []string{expected}, cfg.L2ExperimentalURLs)
347398
})
399+
400+
t.Run("Multiple", func(t *testing.T) {
401+
expected := []string{"https://example.com:8545", "https://example.com:9000"}
402+
cfg := configForArgs(t, addRequiredArgs("--l2.experimental", strings.Join(expected, ",")))
403+
require.EqualValues(t, expected, cfg.L2ExperimentalURLs)
404+
})
348405
}
349406

350407
func TestL2BlockNumber(t *testing.T) {
@@ -431,6 +488,14 @@ func addRequiredArgsExcept(name string, optionalArgs ...string) []string {
431488
return append(toArgList(req), optionalArgs...)
432489
}
433490

491+
func addRequiredArgsExceptMultiple(remove []string, optionalArgs ...string) []string {
492+
req := requiredArgs()
493+
for _, name := range remove {
494+
delete(req, name)
495+
}
496+
return append(toArgList(req), optionalArgs...)
497+
}
498+
434499
func replaceRequiredArg(name string, value string) []string {
435500
req := requiredArgs()
436501
req[name] = value
@@ -451,17 +516,40 @@ func requiredArgs() map[string]string {
451516
}
452517

453518
func writeValidGenesis(t *testing.T) string {
519+
genesis := l2Genesis
520+
return writeGenesis(t, genesis)
521+
}
522+
523+
func writeGenesisFileWithChainID(t *testing.T, chainID uint64) (string, *params.ChainConfig) {
524+
genesis := *l2Genesis
525+
chainCfg := *genesis.Config
526+
chainCfg.ChainID = new(big.Int).SetUint64(chainID)
527+
genesis.Config = &chainCfg
528+
return writeGenesis(t, &genesis), &chainCfg
529+
}
530+
531+
func writeGenesis(t *testing.T, genesis *core.Genesis) string {
454532
dir := t.TempDir()
455-
j, err := json.Marshal(l2Genesis)
533+
j, err := json.Marshal(genesis)
456534
require.NoError(t, err)
457535
genesisFile := dir + "/genesis.json"
458536
require.NoError(t, os.WriteFile(genesisFile, j, 0666))
459537
return genesisFile
460538
}
461539

462540
func writeValidRollupConfig(t *testing.T) string {
541+
return writeRollupConfig(t, chaincfg.OPSepolia())
542+
}
543+
544+
func writeRollupConfigWithChainID(t *testing.T, chainID uint64) (string, *rollup.Config) {
545+
rollupCfg := *chaincfg.OPSepolia()
546+
rollupCfg.L2ChainID = new(big.Int).SetUint64(chainID)
547+
return writeRollupConfig(t, &rollupCfg), &rollupCfg
548+
}
549+
550+
func writeRollupConfig(t *testing.T, rollupCfg *rollup.Config) string {
463551
dir := t.TempDir()
464-
j, err := json.Marshal(chaincfg.OPSepolia())
552+
j, err := json.Marshal(&rollupCfg)
465553
require.NoError(t, err)
466554
cfgFile := dir + "/rollup.json"
467555
require.NoError(t, os.WriteFile(cfgFile, j, 0666))

0 commit comments

Comments
 (0)