Commit 0ee1c09c authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: add deploy config validator

Adds a validator for the deploy configs so that we can be
automatically sure that deploy configs are valid. The problem
is that `getting-started.json` will never pass deploy config
validation because it is invalid JSON and it is the one that
we want to be sure is up to date. Will leave solving that to
future work because it requires a change to the getting started
guide for the op stack.
parent edafea3e
package main
import (
"os"
"path/filepath"
"strings"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum/go-ethereum/log"
)
func main() {
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd()))))
app := &cli.App{
Name: "check-deploy-config",
Usage: "Check that a deploy config is valid",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "path",
Required: true,
Usage: "File system path to the deploy config",
},
},
Action: entrypoint,
}
if err := app.Run(os.Args); err != nil {
log.Crit("error checking deploy config", "err", err)
}
}
func entrypoint(ctx *cli.Context) error {
path := ctx.String("path")
name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
log.Info("Checking deploy config", "name", name, "path", path)
config, err := genesis.NewDeployConfig(path)
if err != nil {
return err
}
// Check the config, no need to call `CheckAddresses()`
if err := config.Check(); err != nil {
return err
}
log.Info("Valid deploy config")
return nil
}
package genesis
import (
"bytes"
"encoding/json"
"errors"
"fmt"
......@@ -31,6 +32,7 @@ var (
)
// DeployConfig represents the deployment configuration for an OP Stack chain.
// It is used to deploy the L1 contracts as well as create the L2 genesis state.
type DeployConfig struct {
// L1StartingBlockTag is used to fill in the storage of the L1Block info predeploy. The rollup
// config script uses this to fill the L1 genesis info for the rollup. The Output oracle deploy
......@@ -172,6 +174,14 @@ type DeployConfig struct {
EIP1559Elasticity uint64 `json:"eip1559Elasticity"`
// EIP1559Denominator is the denominator of EIP1559 base fee market.
EIP1559Denominator uint64 `json:"eip1559Denominator"`
// FaultGameAbsolutePrestate
FaultGameAbsolutePrestate common.Hash `json:"faultGameAbsolutePrestate"`
// FaultGameMaxDepth
FaultGameMaxDepth uint64 `json:"faultGameMaxDepth"`
// FaultGameMaxDuration
FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"`
// SystemConfigStartBlock
SystemConfigStartBlock uint64 `json:"systemConfigStartBlock"`
// FundDevAccounts configures whether or not to fund the dev accounts. Should only be used
// during devnet deployments.
FundDevAccounts bool `json:"fundDevAccounts"`
......@@ -293,21 +303,6 @@ func (d *DeployConfig) Check() error {
if d.GasPriceOracleScalar == 0 {
return fmt.Errorf("%w: GasPriceOracleScalar cannot be 0", ErrInvalidDeployConfig)
}
if d.L1StandardBridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1StandardBridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1CrossDomainMessengerProxy == (common.Address{}) {
return fmt.Errorf("%w: L1CrossDomainMessengerProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1ERC721BridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1ERC721BridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.SystemConfigProxy == (common.Address{}) {
return fmt.Errorf("%w: SystemConfigProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.OptimismPortalProxy == (common.Address{}) {
return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.EIP1559Denominator == 0 {
return fmt.Errorf("%w: EIP1559Denominator cannot be 0", ErrInvalidDeployConfig)
}
......@@ -343,6 +338,29 @@ func (d *DeployConfig) Check() error {
return nil
}
// CheckAddresses will return an error if the addresses are not set.
// These values are required to create the L2 genesis state and are present in the deploy config
// even though the deploy config is required to deploy the contracts on L1. This creates a
// circular dependency that should be resolved in the future.
func (d *DeployConfig) CheckAddresses() error {
if d.L1StandardBridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1StandardBridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1CrossDomainMessengerProxy == (common.Address{}) {
return fmt.Errorf("%w: L1CrossDomainMessengerProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1ERC721BridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1ERC721BridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.SystemConfigProxy == (common.Address{}) {
return fmt.Errorf("%w: SystemConfigProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.OptimismPortalProxy == (common.Address{}) {
return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig)
}
return nil
}
// SetDeployments will merge a Deployments into a DeployConfig.
func (d *DeployConfig) SetDeployments(deployments *L1Deployments) {
d.L1StandardBridgeProxy = deployments.L1StandardBridgeProxy
......@@ -469,8 +487,11 @@ func NewDeployConfig(path string) (*DeployConfig, error) {
return nil, fmt.Errorf("deploy config at %s not found: %w", path, err)
}
dec := json.NewDecoder(bytes.NewReader(file))
dec.DisallowUnknownFields()
var config DeployConfig
if err := json.Unmarshal(file, &config); err != nil {
if err := dec.Decode(&config); err != nil {
return nil, fmt.Errorf("cannot unmarshal deploy config: %w", err)
}
......
......@@ -63,11 +63,15 @@ var Subcommands = cli.Commands{
config.SetDeployments(deployments)
}
// Do the check after setting the deployments
if err := config.Check(); err != nil {
return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
}
// Check the addresses after setting the deployments
if err := config.CheckAddresses(); err != nil {
return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
}
var dump *state.Dump
if l1Allocs := ctx.String("l1-allocs"); l1Allocs != "" {
dump, err = genesis.NewStateDump(l1Allocs)
......
......@@ -23,7 +23,7 @@
"gas-snapshot": "pnpm build:differential && pnpm build:fuzz && forge snapshot --no-match-test 'testDiff|testFuzz|invariant|generateArtifact'",
"storage-snapshot": "./scripts/storage-snapshot.sh",
"semver-lock": "forge script scripts/SemverLock.s.sol",
"validate-deploy-configs": "./scripts/validate-deploy-configs.sh",
"validate-deploy-configs": "./scripts/check-deploy-configs.sh",
"validate-spacers": "pnpm build && npx tsx scripts/validate-spacers.ts",
"slither": "./scripts/slither.sh",
"slither:triage": "TRIAGE_MODE=1 ./scripts/slither.sh",
......
#!/usr/bin/env bash
# This script is used to check for valid deploy configs.
# It should check all configs and return a non-zero exit code if any of them are invalid.
# getting-started.json isn't valid JSON so its skipped.
code=$?
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
CONTRACTS_BASE=$(dirname $SCRIPT_DIR)
MONOREPO_BASE=$(dirname $(dirname $CONTRACTS_BASE))
for config in $CONTRACTS_BASE/deploy-config/*.json; do
if grep -q "getting-started" <<< "$config"; then
echo "Skipping getting-started.json"
continue
fi
go run $MONOREPO_BASE/op-chain-ops/cmd/check-deploy-config/main.go --path $config
[ $? -eq 0 ] || code=$?
done
exit $code
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