diff --git a/.env.example b/.env.example index 6edb20be5b..f2ba870ed4 100644 --- a/.env.example +++ b/.env.example @@ -21,11 +21,18 @@ SCR_STANDARD_CHAIN_CANDIDATE=false # It is defined for convenience and reuse by other vars below SCR_MONOREPO_DIR=../optimism # path to local "ethereum-optimism/optimism" monorepo -# The following vars point to three input files required for adding a chain +# The following vars point to four input files required for adding a chain # Data will be scraped from these files in order to construct the required registry data +# and for genesis validation purposes. SCR_DEPLOYMENTS_DIR=${SCR_MONOREPO_DIR}/packages/contracts-bedrock/deployments/getting-started SCR_ROLLUP_CONFIG=${SCR_MONOREPO_DIR}/op-node/rollup.json SCR_GENESIS=${SCR_MONOREPO_DIR}/op-node/genesis.json +SCR_DEPLOY_CONFIG=${SCR_MONOREPO_DIR}/packages/contracts-bedrock/deploy-config/getting-started.json + +# This is the commit in the https://github.com/ethereum-optimism/optimism/ repo +# at which the chain's genesis was created. This is necessary to have our validation checks +# recreate the genesis file using identical source code +SCR_GENESIS_CREATION_COMMIT="" # Your chain's endpoint for ETHEREUM JSON-RPC requests SCR_PUBLIC_RPC="http://awe.some.rpc" # new OP Stack L2 RPC URL diff --git a/add-chain/e2e_test.go b/add-chain/e2e_test.go index 885cd1fcd5..e79d0fadbb 100644 --- a/add-chain/e2e_test.go +++ b/add-chain/e2e_test.go @@ -10,9 +10,9 @@ import ( ) const ( - addressDir = "../superchain/extra/addresses/sepolia/" - configDir = "../superchain/configs/sepolia/" - genesisSystemConfigDir = "../superchain/extra/genesis-system-configs/sepolia/" + addressDir = "../superchain/extra/addresses/sepolia/" + configDir = "../superchain/configs/sepolia/" + validationInputsDir = "../validation/genesis/validation-inputs" ) var tests = []struct { @@ -171,7 +171,6 @@ func checkConfigTOML(t *testing.T, testName, chainShortName string) { func cleanupTestFiles(t *testing.T, chainShortName string) { paths := []string{ addressDir + chainShortName + ".json", - genesisSystemConfigDir + chainShortName + ".json", configDir + chainShortName + ".toml", } diff --git a/add-chain/flags/flags.go b/add-chain/flags/flags.go index 53fc057b90..ec127405e9 100644 --- a/add-chain/flags/flags.go +++ b/add-chain/flags/flags.go @@ -70,6 +70,18 @@ var ( Usage: "Filepath to rollup.json input file", Required: true, } + DeployConfigFlag = &cli.StringFlag{ + Name: "deploy-config", + EnvVars: prefixEnvVars("DEPLOY_CONFIG"), + Usage: "Filepath to deploy-config json input file", + Required: true, + } + GenesisCreationCommit = &cli.StringFlag{ + Name: "genesis-creation-commit", + EnvVars: prefixEnvVars("GENESIS_CREATION_COMMIT"), + Usage: "Commit in the https://github.com/ethereum-optimism/optimism/ repo at which the chain's genesis was created", + Required: true, + } GenesisFlag = &cli.StringFlag{ Name: "genesis", EnvVars: prefixEnvVars("GENESIS"), diff --git a/add-chain/go.mod b/add-chain/go.mod index 27e0efb288..afd28f1d9f 100644 --- a/add-chain/go.mod +++ b/add-chain/go.mod @@ -4,12 +4,15 @@ go 1.21 replace github.com/ethereum-optimism/superchain-registry/superchain => ../superchain +replace github.com/ethereum-optimism/superchain-registry/validation => ../validation + replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101407.0-rc.1.0.20240812224053-8d99ca68bb1a require ( github.com/BurntSushi/toml v1.4.0 github.com/ethereum-optimism/optimism v1.9.1-0.20240814195148-0bb2ff57c813 github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240814192743-ea7e768a02a6 + github.com/ethereum-optimism/superchain-registry/validation v0.0.0-00010101000000-000000000000 github.com/ethereum/go-ethereum v1.14.7 github.com/google/go-cmp v0.6.0 github.com/joho/godotenv v1.5.1 diff --git a/add-chain/main.go b/add-chain/main.go index b93e9f637f..44c96d24b8 100644 --- a/add-chain/main.go +++ b/add-chain/main.go @@ -3,16 +3,19 @@ package main import ( "fmt" "os" + "path" "path/filepath" "runtime" "strconv" "strings" + "github.com/BurntSushi/toml" "github.com/ethereum-optimism/optimism/op-e2e/bindings" "github.com/ethereum-optimism/superchain-registry/add-chain/cmd" "github.com/ethereum-optimism/superchain-registry/add-chain/config" "github.com/ethereum-optimism/superchain-registry/add-chain/flags" "github.com/ethereum-optimism/superchain-registry/superchain" + "github.com/ethereum-optimism/superchain-registry/validation/genesis" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" @@ -34,6 +37,8 @@ var app = &cli.App{ flags.RollupConfigFlag, flags.DeploymentsDirFlag, flags.StandardChainCandidateFlag, + flags.GenesisCreationCommit, + flags.DeployConfigFlag, }, Action: entrypoint, Commands: []*cli.Command{ @@ -87,6 +92,8 @@ func entrypoint(ctx *cli.Context) error { chainName := ctx.String(flags.ChainNameFlag.Name) rollupConfigPath := ctx.String(flags.RollupConfigFlag.Name) + deployConfigPath := ctx.String(flags.DeployConfigFlag.Name) + genesisCreationCommit := ctx.String(flags.GenesisCreationCommit.Name) deploymentsDir := ctx.String(flags.DeploymentsDirFlag.Name) chainShortName := ctx.String(flags.ChainShortNameFlag.Name) if chainShortName == "" { @@ -107,6 +114,8 @@ func entrypoint(ctx *cli.Context) error { fmt.Printf("Monorepo dir: %s\n", monorepoDir) fmt.Printf("Deployments directory: %s\n", deploymentsDir) fmt.Printf("Rollup config filepath: %s\n", rollupConfigPath) + fmt.Printf("Deploy config filepath: %s\n", deployConfigPath) + fmt.Printf("Genesis creation commit: %s\n", genesisCreationCommit) fmt.Printf("Public RPC endpoint: %s\n", publicRPC) fmt.Printf("Sequencer RPC endpoint: %s\n", sequencerRPC) fmt.Printf("Block Explorer: %s\n", explorer) @@ -166,7 +175,29 @@ func entrypoint(ctx *cli.Context) error { return fmt.Errorf("error generating chain config .yaml file: %w", err) } - fmt.Printf("Wrote config for new chain with identifier %s", rollupConfig.Identifier()) + fmt.Printf("✅ Wrote config for new chain with identifier %s", rollupConfig.Identifier()) + + folderName := fmt.Sprintf("%d", rollupConfig.ChainID) + if runningTests := os.Getenv("SCR_RUN_TESTS"); runningTests == "true" { + folderName = folderName + "-test" + } + genesisValidationInputsDir := filepath.Join(superchainRepoRoot, "validation", "genesis", "validation-inputs", folderName) + err = os.MkdirAll(genesisValidationInputsDir, os.ModePerm) + if err != nil { + return err + } + err = copyDeployConfigFile(deployConfigPath, genesisValidationInputsDir) + if err != nil { + return fmt.Errorf("error copying deploy-config json file: %w", err) + } + fmt.Printf("✅ Copied deploy-config json file to validation module") + + err = writeGenesisValidationMetadata(genesisCreationCommit, genesisValidationInputsDir) + if err != nil { + return fmt.Errorf("error writing genesis validation metadata file: %w", err) + } + fmt.Printf("✅ Wrote genesis validation metadata file") + return nil } @@ -227,3 +258,32 @@ func getGasPayingToken(l1rpcURl string, SystemConfigAddress superchain.Address) return (*superchain.Address)(&result.Addr), nil } + +func copyDeployConfigFile(sourcePath string, targetDir string) error { + data, err := os.ReadFile(sourcePath) + if err != nil { + return err + } + return os.WriteFile(path.Join(targetDir, "deploy-config.json"), data, os.ModePerm) +} + +func writeGenesisValidationMetadata(commit string, targetDir string) error { + // Define default metadata params: + // These may not be sufficient to make the genesis validation work, + // but we address that with some manual trial-and-error intervention + // involving OPLabs engineers after the add-chain command runs. + const defaultNodeVersion = "18.12.1" + const defaultMonorepoBuildCommand = "pnpm" + const defaultGenesisCreationCommand = "opnode1" + vm := genesis.ValidationMetadata{ + GenesisCreationCommit: commit, + NodeVersion: defaultNodeVersion, + MonorepoBuildCommand: defaultMonorepoBuildCommand, + GenesisCreationCommand: defaultGenesisCreationCommand, + } + data, err := toml.Marshal(vm) + if err != nil { + return err + } + return os.WriteFile(path.Join(targetDir, "meta.toml"), data, os.ModePerm) +} diff --git a/add-chain/testdata/.env.test b/add-chain/testdata/.env.test index d5291cdde1..13c6763cfb 100644 --- a/add-chain/testdata/.env.test +++ b/add-chain/testdata/.env.test @@ -4,6 +4,8 @@ SCR_SUPERCHAIN_TARGET=sepolia # L1 chain name SCR_MONOREPO_DIR=./testdata/monorepo SCR_DEPLOYMENTS_DIR=${SCR_MONOREPO_DIR}/deployments SCR_ROLLUP_CONFIG=${SCR_MONOREPO_DIR}/op-node/rollup.json +SCR_GENESIS_CREATION_COMMIT=somecommit +SCR_DEPLOY_CONFIG=${SCR_MONOREPO_DIR}/deploy-config/sepolia.json SCR_PUBLIC_RPC="http://awe.some.rpc" # L2 RPC URL SCR_SEQUENCER_RPC="http://awe.some.seq.rpc" SCR_EXPLORER="https://awesomescan.org" # L2 block explorer URL diff --git a/add-chain/testdata/monorepo/deploy-config/sepolia.json b/add-chain/testdata/monorepo/deploy-config/sepolia.json new file mode 100644 index 0000000000..bcc13611fe --- /dev/null +++ b/add-chain/testdata/monorepo/deploy-config/sepolia.json @@ -0,0 +1,41 @@ +{ + "finalSystemOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "portalGuardian": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l1StartingBlockTag": "0x70e5634d09793b1cfaa7d0a2a5d3289a3b2308de1e82f682b4f817fc670f9797", + "l1ChainID": 11155111, + "l2ChainID": 11155420, + "l2BlockTime": 2, + "maxSequencerDrift": 600, + "sequencerWindowSize": 3600, + "channelTimeout": 300, + "p2pSequencerAddress": "0x715b7219D986641DF9eFd9C7Ef01218D528e19ec", + "batchInboxAddress": "0xff00000000000000000000000000000011155420", + "batchSenderAddress": "0x7431310e026B69BFC676C0013E12A1A11411EEc9", + "l2OutputOracleSubmissionInterval": 120, + "l2OutputOracleStartingBlockNumber": 0, + "l2OutputOracleStartingTimestamp": 0, + "l2OutputOracleProposer": "0x02b1786A85Ec3f71fBbBa46507780dB7cF9014f6", + "l2OutputOracleChallenger": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "finalizationPeriodSeconds": 12, + "proxyAdminOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "baseFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l1FeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "sequencerFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultWithdrawalNetwork": 0, + "l1FeeVaultWithdrawalNetwork": 0, + "sequencerFeeVaultWithdrawalNetwork": 0, + "gasPriceOracleOverhead": 188, + "gasPriceOracleScalar": 684000, + "enableGovernance": true, + "governanceTokenSymbol": "OP", + "governanceTokenName": "Optimism", + "governanceTokenOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l2GenesisBlockGasLimit": "0x1c9c380", + "l2GenesisBlockBaseFeePerGas": "0x3b9aca00", + "eip1559Denominator": 50, + "eip1559Elasticity": 6, + "l2GenesisRegolithTimeOffset": "0x0" +} diff --git a/justfile b/justfile index 4172e3b724..b6ff2c7b03 100644 --- a/justfile +++ b/justfile @@ -59,6 +59,7 @@ promotion-test: clean-add-chain: rm -f superchain/configs/sepolia/testchain_*.toml rm -f superchain/extra/sepolia/testchain_*.json.gz + rm -rf -- validation/genesis/validation-inputs/*-test/ # Tidying all go.mod files tidy-all: tidy-add-chain tidy-superchain tidy-validation diff --git a/validation/.gitignore b/validation/.gitignore index a658e35344..8a5e1f6911 100644 --- a/validation/.gitignore +++ b/validation/.gitignore @@ -1,2 +1,6 @@ +# diff tool artifacts generate-rollup-config/output-* generate-genesis/output-* + +# test output artifacts +genesis/validation-inputs/*-test diff --git a/validation/genesis/genesis-predeploy_test.go b/validation/genesis/genesis-predeploy_test.go index 9eab1223d4..981599dec4 100644 --- a/validation/genesis/genesis-predeploy_test.go +++ b/validation/genesis/genesis-predeploy_test.go @@ -111,9 +111,9 @@ func testGenesisPredeploys(t *testing.T, chain *ChainConfig) { gotData, err := json.MarshalIndent(g.Alloc, "", " ") require.NoError(t, err) - err = os.WriteFile(path.Join(monorepoDir, "want-alloc.json"), expectedData, 0o777) + err = os.WriteFile(path.Join(monorepoDir, "want-alloc.json"), expectedData, os.ModePerm) require.NoError(t, err) - err = os.WriteFile(path.Join(monorepoDir, "got-alloc.json"), gotData, 0o777) + err = os.WriteFile(path.Join(monorepoDir, "got-alloc.json"), gotData, os.ModePerm) require.NoError(t, err) require.Equal(t, string(expectedData), string(gotData)) @@ -135,7 +135,7 @@ func writeDeployments(chainId uint64, directory string) error { return err } - err = os.WriteFile(path.Join(directory, ".deploy"), data, 0o777) + err = os.WriteFile(path.Join(directory, ".deploy"), data, os.ModePerm) if err != nil { return err } diff --git a/validation/genesis/genesis.go b/validation/genesis/genesis.go index 6e70f73279..04f8f859a6 100644 --- a/validation/genesis/genesis.go +++ b/validation/genesis/genesis.go @@ -5,6 +5,7 @@ import ( "fmt" "path" "strconv" + "strings" "github.com/BurntSushi/toml" ) @@ -40,6 +41,10 @@ func init() { panic(fmt.Errorf("failed to decode metadata file: %w", err)) } + if strings.HasSuffix(s.Name(), "-test") { + continue + } + chainID, err := strconv.Atoi(s.Name()) if err != nil { panic(fmt.Errorf("failed to decode chain id from dir name: %w", err))