Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions op-deployer/pkg/deployer/opcm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func (c *Contract) ProtocolVersions(ctx context.Context) (common.Address, error)
return c.getAddress(ctx, "protocolVersions")
}

func (c *Contract) OPCMStandardValidator(ctx context.Context) (common.Address, error) {
return c.getAddress(ctx, "opcmStandardValidator")
}

func (c *Contract) getAddress(ctx context.Context, name string) (common.Address, error) {
return c.callContractMethod(ctx, name, abi.Arguments{})
}
Expand Down
3 changes: 2 additions & 1 deletion op-deployer/pkg/deployer/standard/standard.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const (
ContractsV300Tag = "op-contracts/v3.0.0"
ContractsV400Tag = "op-contracts/v4.0.0-rc.7"
ContractsV410Tag = "op-contracts/v4.1.0"
ContractsV500Tag = "op-contracts/v5.0.0-rc.2"
CurrentTag = ContractsV410Tag
)

Expand Down Expand Up @@ -184,7 +185,7 @@ func DefaultHardforkScheduleForTag(tag string) *genesis.UpgradeScheduleDeployCon
return sched
case ContractsV180Tag, ContractsV200Tag, ContractsV300Tag:
sched.ActivateForkAtGenesis(forks.Holocene)
case ContractsV400Tag, ContractsV410Tag:
case ContractsV400Tag, ContractsV410Tag, ContractsV500Tag:
sched.ActivateForkAtGenesis(forks.Holocene)
sched.ActivateForkAtGenesis(forks.Isthmus)
default:
Expand Down
2 changes: 2 additions & 0 deletions op-validator/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func main() {
versionCmd(standard.ContractsV200Tag),
versionCmd(standard.ContractsV300Tag),
versionCmd(standard.ContractsV400Tag),
versionCmd(standard.ContractsV410Tag),
versionCmd(standard.ContractsV500Tag),
},
},
}
Expand Down
13 changes: 13 additions & 0 deletions op-validator/pkg/service/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ var (
Usage: "L2 chain ID",
Required: true,
}
ProposerFlag = &cli.StringFlag{
Name: "proposer",
Usage: "Proposer address as hex string (required for OPCMStandardValidator)",
Required: false,
}
FailOnErrorFlag = &cli.BoolFlag{
Name: "fail",
Usage: "Exit with non-zero code if validation errors are found",
Expand All @@ -48,6 +53,7 @@ var ValidateFlags = []cli.Flag{
ProxyAdminFlag,
SystemConfigFlag,
L2ChainIDFlag,
ProposerFlag,
FailOnErrorFlag,
}

Expand All @@ -58,6 +64,7 @@ type Config struct {
ProxyAdmin common.Address
SystemConfig common.Address
L2ChainID *big.Int
Proposer common.Address
}

// NewConfig creates a new Config from CLI context
Expand All @@ -70,11 +77,17 @@ func NewConfig(ctx *cli.Context) (*Config, error) {
return nil, fmt.Errorf("invalid L2 chain ID: %s", ctx.String(L2ChainIDFlag.Name))
}

var proposer common.Address
if proposerStr := ctx.String(ProposerFlag.Name); proposerStr != "" {
proposer = common.HexToAddress(proposerStr)
}

return &Config{
L1RPCURL: ctx.String(L1RPCURLFlag.Name),
AbsolutePrestate: absolutePrestate,
ProxyAdmin: proxyAdmin,
SystemConfig: systemConfig,
L2ChainID: l2ChainID,
Proposer: proposer,
}, nil
}
7 changes: 7 additions & 0 deletions op-validator/pkg/service/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func Validate(ctx context.Context, lgr log.Logger, release string, cfg *Config)
}

var validator validations.Validator

switch release {
case standard.ContractsV180Tag:
validator = validations.NewV180Validator(l1Client)
Expand All @@ -55,14 +56,20 @@ func Validate(ctx context.Context, lgr log.Logger, release string, cfg *Config)
validator = validations.NewV300Validator(l1Client)
case standard.ContractsV400Tag:
validator = validations.NewV400Validator(l1Client)
case standard.ContractsV410Tag:
validator = validations.NewV410Validator(l1Client)
case standard.ContractsV500Tag:
validator = validations.NewV500Validator(l1Client)
default:
return nil, fmt.Errorf("invalid release: %s", release)
}
lgr.Info("Using Validator", "version", release)

return validator.Validate(ctx, validations.BaseValidatorInput{
ProxyAdminAddress: cfg.ProxyAdmin,
SystemConfigAddress: cfg.SystemConfig,
AbsolutePrestate: cfg.AbsolutePrestate,
L2ChainID: cfg.L2ChainID,
Proposer: cfg.Proposer,
})
}
8 changes: 8 additions & 0 deletions op-validator/pkg/validations/addresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ var addresses = map[uint64]map[string]common.Address{
standard.ContractsV300Tag: common.HexToAddress("0xf989Df70FB46c581ba6157Ab335c0833bA60e1f0"),
// Bootstrapped on 06/03/2025 using OP Deployer.
standard.ContractsV400Tag: common.HexToAddress("0x3dfc5e44043DC5998928E0b8280136b7352d3F70"),
// Bootstrapped on 10/02/2025 using OP Deployer.
standard.ContractsV410Tag: common.HexToAddress("0x845FEF377Fa9C678B3eBe33B024678538f1215dD"),
// Bootstrapped on 10/27/2025 using OP Deployer (v5.0.0-rc.2).
standard.ContractsV500Tag: common.HexToAddress("0xDCE1A51A25dD5BF02ccB4264D039EDdF11A95b43"),
},
11155111: {
// Bootstrapped on 03/02/2025 using OP Deployer.
Expand All @@ -28,6 +32,10 @@ var addresses = map[uint64]map[string]common.Address{
standard.ContractsV300Tag: common.HexToAddress("0x2d56022cb84ce6b961c3b4288ca36386bcd9024c"),
// Bootstrapped on 06/03/2025 using OP Deployer.
standard.ContractsV400Tag: common.HexToAddress("0xA8a1529547306FEC7A32a001705160f2110451aE"),
// Bootstrapped on 10/02/2025 using OP Deployer.
standard.ContractsV410Tag: common.HexToAddress("0x7B4d2a02d5fa6C7C98D835d819956EBB876Ff439"),
// Bootstrapped on 10/27/2025 using OP Deployer (v5.0.0-rc.2).
standard.ContractsV500Tag: common.HexToAddress("0x757bFA3AAABcE60112Cee3239DCD05b5F6EFaE3A"),
},
}

Expand Down
196 changes: 125 additions & 71 deletions op-validator/pkg/validations/addresses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (

"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"

op_e2e "github.com/ethereum-optimism/optimism/op-e2e"

"github.com/ethereum-optimism/superchain-registry/validation"
"github.com/ethereum/go-ethereum/rpc"
"github.com/lmittmann/w3"
Expand Down Expand Up @@ -74,11 +72,11 @@ func TestValidatorAddress(t *testing.T) {
}

func TestAddressValidDeployment(t *testing.T) {
op_e2e.InitParallel(t)
t.Parallel()

for _, network := range []string{"mainnet", "sepolia"} {
t.Run(network, func(t *testing.T) {
op_e2e.InitParallel(t)
t.Parallel()
testStandardVersionNetwork(t, network)
})
}
Expand All @@ -90,114 +88,170 @@ func testStandardVersionNetwork(t *testing.T, network string) {
var chainID uint64
if network == "mainnet" {
rpcURL = os.Getenv("MAINNET_RPC_URL")
if rpcURL == "" {
rpcURL = "https://ethereum.publicnode.com"
}
stdVersDefs = validation.StandardVersionsMainnet
chainID = 1
} else if network == "sepolia" {
rpcURL = os.Getenv("SEPOLIA_RPC_URL")
if rpcURL == "" {
rpcURL = "https://ethereum-sepolia-rpc.publicnode.com"
}
stdVersDefs = validation.StandardVersionsSepolia
chainID = 11155111
} else {
t.Fatalf("Invalid network: %s", network)
}

require.NotEmpty(t, rpcURL, "RPC URL is empty")

contractVersions := []string{
standard.ContractsV180Tag,
standard.ContractsV200Tag,
standard.ContractsV300Tag,
standard.ContractsV400Tag,
standard.ContractsV410Tag,
// Enable whenever we upgrade the superchain registry
//standard.ContractsV500Tag,
}

for _, semver := range contractVersions {
version := stdVersDefs[validation.Semver(semver)]
version, ok := stdVersDefs[validation.Semver(semver)]
require.True(t, ok, "version %s not found in registry", semver)

address, err := ValidatorAddress(chainID, semver)
require.NoError(t, err)
require.NoError(t, err, "failed to get validator address for %s", semver)
require.NotEqual(t, common.Address{}, address, "validator address is zero for %s", semver)

rpcClient, err := rpc.Dial(rpcURL)
require.NoError(t, err)

t.Run(semver, func(t *testing.T) {
testStandardVersion(t, address, rpcClient, version)
testStandardVersion(t, address, rpcClient, version, semver)
})
}
}

func testStandardVersion(t *testing.T, address common.Address, rpcClient *rpc.Client, version validation.VersionConfig) {
type fieldDef struct {
getter string
semver string
}
fields := []fieldDef{
{
"systemConfigVersion",
version.SystemConfig.Version,
},
{
"mipsVersion",
version.Mips.Version,
},
{
"optimismPortalVersion",
version.OptimismPortal.Version,
},
{
"anchorStateRegistryVersion",
version.AnchorStateRegistry.Version,
},
{
"delayedWETHVersion",
version.DelayedWeth.Version,
},
{
"disputeGameFactoryVersion",
version.DisputeGameFactory.Version,
},
{
"preimageOracleVersion",
version.PreimageOracle.Version,
},
{
"l1CrossDomainMessengerVersion",
version.L1CrossDomainMessenger.Version,
},
{
"l1ERC721BridgeVersion",
version.L1ERC721Bridge.Version,
},
{
"l1StandardBridgeVersion",
version.L1StandardBridge.Version,
},
{
"optimismMintableERC20FactoryVersion",
version.OptimismMintableERC20Factory.Version,
},
}

func testStandardVersion(t *testing.T, address common.Address, rpcClient *rpc.Client, version validation.VersionConfig, semverTag string) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

w3c := w3.NewClient(rpcClient)
for _, field := range fields {
fn := w3.MustNewFunc(fmt.Sprintf("%s()", field.getter), "string")
var outBytes []byte

if semverTag >= standard.ContractsV500Tag {
Comment thread
serpixel marked this conversation as resolved.
Outdated
// For v5.0.0+
type implFieldDef struct {
implGetter string
semver string
}
implFields := []implFieldDef{
{"systemConfigImpl", version.SystemConfig.Version},
{"mipsImpl", version.Mips.Version},
{"optimismPortalImpl", version.OptimismPortal.Version},
{"anchorStateRegistryImpl", version.AnchorStateRegistry.Version},
{"delayedWETHImpl", version.DelayedWeth.Version},
{"disputeGameFactoryImpl", version.DisputeGameFactory.Version},
{"l1CrossDomainMessengerImpl", version.L1CrossDomainMessenger.Version},
{"l1ERC721BridgeImpl", version.L1ERC721Bridge.Version},
{"l1StandardBridgeImpl", version.L1StandardBridge.Version},
{"optimismMintableERC20FactoryImpl", version.OptimismMintableERC20Factory.Version},
}

versionFn := w3.MustNewFunc("version()", "string")
for _, field := range implFields {
implGetterFn := w3.MustNewFunc(fmt.Sprintf("%s()", field.implGetter), "address")
var implAddrBytes []byte
require.NoError(
t,
w3c.CallCtx(
ctx,
eth.Call(&w3types.Message{
To: &address,
Func: implGetterFn,
}, nil, nil).Returns(&implAddrBytes),
),
"failed to call %s",
field.implGetter,
)

var implAddr common.Address
require.NoError(t, implGetterFn.DecodeReturns(implAddrBytes, &implAddr), "failed to decode %s", field.implGetter)
require.NotEqual(t, common.Address{}, implAddr, "implementation address is zero for %s", field.implGetter)

var versionBytes []byte
require.NoError(
t,
w3c.CallCtx(
ctx,
eth.Call(&w3types.Message{
To: &implAddr,
Func: versionFn,
}, nil, nil).Returns(&versionBytes),
),
"failed to call version() on %s implementation",
field.implGetter,
)

var outVersion string
require.NoError(t, versionFn.DecodeReturns(versionBytes, &outVersion), "failed to decode version for %s", field.implGetter)
require.Equal(t, field.semver, outVersion, "version mismatch for %s", field.implGetter)
}

preimageOracleVersionFn := w3.MustNewFunc("preimageOracleVersion()", "string")
var preimageOracleVersionBytes []byte
require.NoError(
t,
w3c.CallCtx(
ctx,
eth.Call(&w3types.Message{
To: &address,
Func: fn,
}, nil, nil).Returns(&outBytes),
Func: preimageOracleVersionFn,
}, nil, nil).Returns(&preimageOracleVersionBytes),
),
"failed to call %s",
field.getter,
"failed to call preimageOracleVersion",
)

var outVersion string
require.NoError(t, fn.DecodeReturns(outBytes, &outVersion))
require.Equal(t, field.semver, outVersion)
var preimageOracleVersion string
require.NoError(t, preimageOracleVersionFn.DecodeReturns(preimageOracleVersionBytes, &preimageOracleVersion), "failed to decode preimageOracleVersion")
require.Equal(t, version.PreimageOracle.Version, preimageOracleVersion, "version mismatch for preimageOracleVersion")
} else {
// Older versions < v5.0.0
type fieldDef struct {
getter string
semver string
}
fields := []fieldDef{
{"systemConfigVersion", version.SystemConfig.Version},
{"mipsVersion", version.Mips.Version},
{"optimismPortalVersion", version.OptimismPortal.Version},
{"anchorStateRegistryVersion", version.AnchorStateRegistry.Version},
{"delayedWETHVersion", version.DelayedWeth.Version},
{"disputeGameFactoryVersion", version.DisputeGameFactory.Version},
{"preimageOracleVersion", version.PreimageOracle.Version},
{"l1CrossDomainMessengerVersion", version.L1CrossDomainMessenger.Version},
{"l1ERC721BridgeVersion", version.L1ERC721Bridge.Version},
{"l1StandardBridgeVersion", version.L1StandardBridge.Version},
{"optimismMintableERC20FactoryVersion", version.OptimismMintableERC20Factory.Version},
}

for _, field := range fields {
fn := w3.MustNewFunc(fmt.Sprintf("%s()", field.getter), "string")
var outBytes []byte
require.NoError(
t,
w3c.CallCtx(
ctx,
eth.Call(&w3types.Message{
To: &address,
Func: fn,
}, nil, nil).Returns(&outBytes),
),
"failed to call %s",
field.getter,
)

var outVersion string
require.NoError(t, fn.DecodeReturns(outBytes, &outVersion), "failed to decode response for %s", field.getter)
require.Equal(t, field.semver, outVersion, "version mismatch for %s", field.getter)
}
}
}
Loading