Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 8 additions & 16 deletions op-deployer/pkg/deployer/integration_test/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-core/predeploys"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -935,24 +934,12 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat

// Then test upgrade on the V2-deployed chain
t.Run("upgrade chain v2", func(t *testing.T) {
// ABI-encode game args for FaultDisputeGameConfig{absolutePrestate}
bytes32Type := deployer.Bytes32Type
addressType := deployer.AddressType

// FaultDisputeGameConfig just needs absolutePrestate (bytes32)
testPrestate := common.Hash{'P', 'R', 'E', 'S', 'T', 'A', 'T', 'E'}
cannonArgs, err := abi.Arguments{{Type: bytes32Type}}.Pack(testPrestate)
require.NoError(t, err)

// PermissionedDisputeGameConfig needs absolutePrestate, proposer, challenger
testProposer := common.Address{'P'}
testChallenger := common.Address{'C'}
permissionedArgs, err := abi.Arguments{
{Type: bytes32Type},
{Type: addressType},
{Type: addressType},
}.Pack(testPrestate, testProposer, testChallenger)
require.NoError(t, err)

upgradeConfig := embedded.UpgradeOPChainInput{
Prank: superchainProxyAdminOwner,
Expand All @@ -964,19 +951,24 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat
Enabled: true,
InitBond: big.NewInt(1000000000000000000),
GameType: embedded.GameTypeCannon,
GameArgs: cannonArgs,
FaultDisputeGameConfig: &embedded.FaultDisputeGameConfig{
AbsolutePrestate: testPrestate,
},
},
{
Enabled: true,
InitBond: big.NewInt(1000000000000000000),
GameType: embedded.GameTypePermissionedCannon,
GameArgs: permissionedArgs,
PermissionedDisputeGameConfig: &embedded.PermissionedDisputeGameConfig{
AbsolutePrestate: testPrestate,
Proposer: testProposer,
Challenger: testChallenger,
},
},
{
Enabled: false,
InitBond: big.NewInt(0),
GameType: embedded.GameTypeCannonKona,
GameArgs: []byte{}, // Disabled games don't need args
},
},
ExtraInstructions: []embedded.ExtraInstruction{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum-optimism/optimism/op-service/testutils/devnet"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -134,23 +133,12 @@ func TestManageAddGameTypeV2_Integration(t *testing.T) {
_, afactsFS := testutil.LocalArtifacts(t)
shared.RunPastUpgradesWithRPC(t, runner.l1RPC, afactsFS, lgr, 11155111, l1ProxyAdminOwner, systemConfigProxy)

bytes32Type := deployer.Bytes32Type
addressType := deployer.AddressType

// FaultDisputeGameConfig just needs absolutePrestate (bytes32)
testPrestate := common.Hash{'P', 'R', 'E', 'S', 'T', 'A', 'T', 'E'}
cannonArgs, err := abi.Arguments{{Type: bytes32Type}}.Pack(testPrestate)
require.NoError(t, err)

// PermissionedDisputeGameConfig needs absolutePrestate, proposer, challenger
testProposer := common.Address{'P'}
testChallenger := common.Address{'C'}
permissionedArgs, err := abi.Arguments{
{Type: bytes32Type},
{Type: addressType},
{Type: addressType},
}.Pack(testPrestate, testProposer, testChallenger)
require.NoError(t, err)

testConfig := embedded.UpgradeOPChainInput{
Prank: l1ProxyAdminOwner,
Expand All @@ -162,19 +150,24 @@ func TestManageAddGameTypeV2_Integration(t *testing.T) {
Enabled: true,
InitBond: big.NewInt(1000000000000000000),
GameType: embedded.GameTypeCannon,
GameArgs: cannonArgs,
FaultDisputeGameConfig: &embedded.FaultDisputeGameConfig{
AbsolutePrestate: testPrestate,
},
},
{
Enabled: true,
InitBond: big.NewInt(1000000000000000000),
GameType: embedded.GameTypePermissionedCannon,
GameArgs: permissionedArgs,
PermissionedDisputeGameConfig: &embedded.PermissionedDisputeGameConfig{
AbsolutePrestate: testPrestate,
Proposer: testProposer,
Challenger: testChallenger,
},
},
{
Enabled: false,
InitBond: big.NewInt(0),
GameType: embedded.GameTypeCannonKona,
GameArgs: []byte{}, // Disabled games don't need args
},
},
ExtraInstructions: []embedded.ExtraInstruction{
Expand Down
29 changes: 11 additions & 18 deletions op-deployer/pkg/deployer/integration_test/shared/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/opcmregistry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/upgrade/embedded"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -255,37 +253,32 @@ func buildV2OPCMUpgradeConfig(t *testing.T, prank, opcmAddr, systemConfigProxy c

// Build dispute game configs with dummy prestates
// CANNON and PERMISSIONED_CANNON are the standard game types
cannonArgs, err := abi.Arguments{{Type: deployer.Bytes32Type}}.Pack(opcmregistry.DummyCannonPrestate)
require.NoError(t, err)

permissionedArgs, err := abi.Arguments{
{Type: deployer.Bytes32Type},
{Type: deployer.AddressType},
{Type: deployer.AddressType},
}.Pack(opcmregistry.DummyCannonPrestate, common.Address{}, common.Address{})
require.NoError(t, err)

cannonKonaArgs, err := abi.Arguments{{Type: deployer.Bytes32Type}}.Pack(opcmregistry.DummyCannonKonaPrestate)
require.NoError(t, err)

disputeGameConfigs := []embedded.DisputeGameConfig{
{
Enabled: true,
InitBond: big.NewInt(0),
GameType: embedded.GameTypeCannon,
GameArgs: cannonArgs,
FaultDisputeGameConfig: &embedded.FaultDisputeGameConfig{
AbsolutePrestate: opcmregistry.DummyCannonPrestate,
},
},
{
Enabled: true,
InitBond: big.NewInt(0),
GameType: embedded.GameTypePermissionedCannon,
GameArgs: permissionedArgs,
PermissionedDisputeGameConfig: &embedded.PermissionedDisputeGameConfig{
AbsolutePrestate: opcmregistry.DummyCannonPrestate,
Proposer: common.Address{},
Challenger: common.Address{},
},
},
{
Enabled: true,
InitBond: big.NewInt(0),
GameType: embedded.GameTypeCannonKona,
GameArgs: cannonKonaArgs,
FaultDisputeGameConfig: &embedded.FaultDisputeGameConfig{
AbsolutePrestate: opcmregistry.DummyCannonKonaPrestate,
},
},
}

Expand Down
153 changes: 129 additions & 24 deletions op-deployer/pkg/deployer/upgrade/embedded/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@ import (
"github.com/lmittmann/w3"
)

// GameType represents the type of dispute game.
type GameType uint32

const (
GameTypeCannon GameType = 0
GameTypePermissionedCannon GameType = 1
GameTypeSuperCannon GameType = 4
GameTypeSuperPermCannon GameType = 5
GameTypeCannonKona GameType = 8
GameTypeSuperCannonKona GameType = 9
)

var (
// This is used to encode the fault dispute game config for the upgrade input
faultEncoder = w3.MustNewFunc("dummy((bytes32 absolutePrestate))", "")

// This is used to encode the permissioned dispute game config for the upgrade input
permEncoder = w3.MustNewFunc("dummy((bytes32 absolutePrestate,address proposer,address challenger))", "")

// This is used to encode the upgrade input for the upgrade input
upgradeInputEncoder = w3.MustNewFunc("dummy((address systemConfig,(bool enabled,uint256 initBond,uint32 gameType,bytes gameArgs)[] disputeGameConfigs,(string key,bytes data)[] extraInstructions))",
"")

// This is used to encode the OP Chain config for the upgrade input
opChainConfigEncoder = w3.MustNewFunc("dummy((address systemConfigProxy,bytes32 cannonPrestate,bytes32 cannonKonaPrestate)[])", "")
)

// ScriptInput represents the input struct that is actually passed to the script.
// It contains the prank, opcm, and upgrade input.
type ScriptInput struct {
Expand All @@ -29,6 +56,13 @@ type UpgradeOPChainInput struct {
UpgradeInputV2 *UpgradeInputV2 `json:"upgradeInput,omitempty"`
}

// OPChainConfig represents the configuration for an OP Chain upgrade on OPCM v1.
type OPChainConfig struct {
SystemConfigProxy common.Address `json:"systemConfigProxy"`
CannonPrestate common.Hash `json:"cannonPrestate"`
CannonKonaPrestate common.Hash `json:"cannonKonaPrestate"`
}

// UpgradeInputV2 represents the new upgrade input in OPCM v2.
type UpgradeInputV2 struct {
SystemConfig common.Address `json:"systemConfig"`
Expand All @@ -38,10 +72,11 @@ type UpgradeInputV2 struct {

// DisputeGameConfig represents the configuration for a dispute game.
type DisputeGameConfig struct {
Enabled bool `json:"enabled"`
InitBond *big.Int `json:"initBond"`
GameType GameType `json:"gameType"`
GameArgs []byte `json:"gameArgs"`
Enabled bool `json:"enabled"`
InitBond *big.Int `json:"initBond"`
GameType GameType `json:"gameType"`
FaultDisputeGameConfig *FaultDisputeGameConfig `json:"faultDisputeGameConfig,omitempty"`
PermissionedDisputeGameConfig *PermissionedDisputeGameConfig `json:"permissionedDisputeGameConfig,omitempty"`
}

// ExtraInstruction represents an additional upgrade instruction for the upgrade on OPCM v2.
Expand All @@ -50,30 +85,36 @@ type ExtraInstruction struct {
Data []byte `json:"data"`
}

// GameType represents the type of dispute game.
type GameType uint32

const (
GameTypeCannon GameType = 0
GameTypePermissionedCannon GameType = 1
GameTypeSuperCannon GameType = 4
GameTypeSuperPermCannon GameType = 5
GameTypeCannonKona GameType = 8
GameTypeSuperCannonKona GameType = 9
)
// FaultDisputeGameConfig represents the configuration for a fault dispute game.
// It contains the absolute prestate of the fault dispute game.
type FaultDisputeGameConfig struct {
AbsolutePrestate common.Hash `json:"absolutePrestate"`
}

// OPChainConfig represents the configuration for an OP Chain upgrade on OPCM v1.
type OPChainConfig struct {
SystemConfigProxy common.Address `json:"systemConfigProxy"`
CannonPrestate common.Hash `json:"cannonPrestate"`
CannonKonaPrestate common.Hash `json:"cannonKonaPrestate"`
// PermissionedDisputeGameConfig represents the configuration for a permissioned dispute game.
// It contains the absolute prestate, proposer, and challenger of the permissioned dispute game.
type PermissionedDisputeGameConfig struct {
AbsolutePrestate common.Hash `json:"absolutePrestate"`
Proposer common.Address `json:"proposer"`
Challenger common.Address `json:"challenger"`
}

var upgradeInputEncoder = w3.MustNewFunc("dummy((address systemConfig,(bool enabled,uint256 initBond,uint32 gameType,bytes gameArgs)[] disputeGameConfigs,(string key,bytes data)[] extraInstructions))",
"")
// EncodableUpgradeInput is an intermediate struct that matches the encoder expectation for the UpgradeInputV2 struct.
type EncodableUpgradeInput struct {
SystemConfig common.Address
DisputeGameConfigs []EncodableDisputeGameConfig
ExtraInstructions []ExtraInstruction
}

var opChainConfigEncoder = w3.MustNewFunc("dummy((address systemConfigProxy,bytes32 cannonPrestate,bytes32 cannonKonaPrestate)[])", "")
// EncodableDisputeGameConfig is an intermediate struct that matches the encoder expectation.
type EncodableDisputeGameConfig struct {
Enabled bool
InitBond *big.Int
GameType uint32
GameArgs []byte
}

// EncodedOpChainConfigs encodes the OP Chain configs for the upgrade input, assumes is not nil
func (u *UpgradeOPChainInput) EncodedOpChainConfigs() ([]byte, error) {
data, err := opChainConfigEncoder.EncodeArgs(u.ChainConfigs)
if err != nil {
Expand All @@ -82,8 +123,72 @@ func (u *UpgradeOPChainInput) EncodedOpChainConfigs() ([]byte, error) {
return data[4:], nil
}

// EncodedUpgradeInputV2 encodes the upgrade input for the upgrade input, assumes is not nil
func (u *UpgradeOPChainInput) EncodedUpgradeInputV2() ([]byte, error) {
data, err := upgradeInputEncoder.EncodeArgs(u.UpgradeInputV2)

encodableConfigs := make([]EncodableDisputeGameConfig, len(u.UpgradeInputV2.DisputeGameConfigs))

// Validate and encode each game config.
// We iterate over the game configs in the upgrade input config and encode them into the encodable configs.
// We return an error if a game config is not valid.
for i, gameConfig := range u.UpgradeInputV2.DisputeGameConfigs {
var gameArgs []byte
var err error

if gameConfig.Enabled {
if gameConfig.GameType == GameTypeCannon || gameConfig.GameType == GameTypeCannonKona {
if gameConfig.FaultDisputeGameConfig == nil {
return nil, fmt.Errorf("faultDisputeGameConfig is required for game type %d", gameConfig.GameType)
}
gameArgs, err = faultEncoder.EncodeArgs(gameConfig.FaultDisputeGameConfig)
if err != nil {
return nil, fmt.Errorf("failed to encode fault game config: %w", err)
}

// Edge case check when the encoded game args length is less than 4
if len(gameArgs) < 4 {
return nil, fmt.Errorf("encoded game args length is less than 4 for game type %d", gameConfig.GameType)
}

// Skip the selector bytes
gameArgs = gameArgs[4:]
}

if gameConfig.GameType == GameTypePermissionedCannon {
if gameConfig.PermissionedDisputeGameConfig == nil {
return nil, fmt.Errorf("permissionedDisputeGameConfig is required for game type %d", gameConfig.GameType)
}
gameArgs, err = permEncoder.EncodeArgs(gameConfig.PermissionedDisputeGameConfig)
if err != nil {
return nil, fmt.Errorf("failed to encode permissioned game config: %w", err)
}

// Edge case check when the encoded game args length is less than 4
if len(gameArgs) < 4 {
return nil, fmt.Errorf("encoded game args length is less than 4 for game type %d", gameConfig.GameType)
}

// Skip the selector bytes
gameArgs = gameArgs[4:]
}
}

encodableConfigs[i] = EncodableDisputeGameConfig{
Enabled: gameConfig.Enabled,
InitBond: gameConfig.InitBond,
GameType: uint32(gameConfig.GameType),
GameArgs: gameArgs,
}
}

// Create encodable input
encodableInput := EncodableUpgradeInput{
SystemConfig: u.UpgradeInputV2.SystemConfig,
DisputeGameConfigs: encodableConfigs,
ExtraInstructions: u.UpgradeInputV2.ExtraInstructions,
}

data, err := upgradeInputEncoder.EncodeArgs(encodableInput)
if err != nil {
return nil, fmt.Errorf("failed to encode upgrade input: %w", err)
}
Expand Down
Loading