Commit 4d30eea5 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-deployer: Add support for additional dispute games (#13346)

Users can now specify additional dispute games with custom VM types and preimage oracles. This will help with removing allocs files in the future.
parent 8398e25f
......@@ -175,7 +175,11 @@ func ApplyPipeline(
L2: l2ArtifactsFS,
}
var deployer common.Address
deployer := common.Address{0x01}
if opts.DeployerPrivateKey != nil {
deployer = crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey)
}
var bcaster broadcaster.Broadcaster
var l1Client *ethclient.Client
var l1Host *script.Host
......@@ -193,7 +197,6 @@ func ApplyPipeline(
}
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(opts.DeployerPrivateKey, chainID))
deployer = crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey)
bcaster, err = broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: opts.Logger,
......@@ -235,7 +238,6 @@ func ApplyPipeline(
return fmt.Errorf("failed to select fork: %w", err)
}
} else {
deployer = common.Address{0x01}
bcaster = broadcaster.NoopBroadcaster()
l1Host, err = env.DefaultScriptHost(
bcaster,
......@@ -286,6 +288,11 @@ func ApplyPipeline(
func() error {
return pipeline.DeployAltDA(pEnv, intent, st, chainID)
},
}, pipelineStage{
fmt.Sprintf("deploy-additional-dispute-games-%s", chainID.Hex()),
func() error {
return pipeline.DeployAdditionalDisputeGames(pEnv, intent, st, chainID)
},
}, pipelineStage{
fmt.Sprintf("generate-l2-genesis-%s", chainID.Hex()),
func() error {
......
......@@ -4,6 +4,7 @@ import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"strings"
artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
......@@ -224,7 +225,7 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) (opcm.DeployDispute
MaxClockDuration: cfg.MaxClockDuration,
DelayedWethProxy: cfg.DelayedWethProxy,
AnchorStateRegistryProxy: cfg.AnchorStateRegistryProxy,
L2ChainId: cfg.L2ChainId,
L2ChainId: common.BigToHash(new(big.Int).SetUint64(cfg.L2ChainId)),
Proposer: cfg.Proposer,
Challenger: cfg.Challenger,
},
......
......@@ -695,6 +695,47 @@ func TestInvalidL2Genesis(t *testing.T) {
}
}
func TestAdditionalDisputeGames(t *testing.T) {
op_e2e.InitParallel(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
opts, intent, st := setupGenesisChain(t, defaultL1ChainID)
(&intent.Chains[0].Roles).L1ProxyAdminOwner = crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey)
intent.GlobalDeployOverrides = map[string]any{
"challengePeriodSeconds": 1,
}
intent.Chains[0].AdditionalDisputeGames = []state.AdditionalDisputeGame{
{
ChainProofParams: state.ChainProofParams{
DisputeGameType: 255,
DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate,
DisputeMaxGameDepth: 50,
DisputeSplitDepth: 14,
DisputeClockExtension: 0,
DisputeMaxClockDuration: 1200,
DangerouslyAllowCustomDisputeParameters: true,
},
UseCustomOracle: true,
OracleMinProposalSize: 10000,
OracleChallengePeriodSeconds: 120,
VMType: state.VMTypeAlphabet,
},
}
require.NoError(t, deployer.ApplyPipeline(ctx, opts))
chainState := st.Chains[0]
require.Equal(t, 1, len(chainState.AdditionalDisputeGames))
gameInfo := chainState.AdditionalDisputeGames[0]
require.NotEmpty(t, gameInfo.VMAddress)
require.NotEmpty(t, gameInfo.GameAddress)
require.NotEmpty(t, gameInfo.OracleAddress)
require.NotEqual(t, st.ImplementationsDeployment.PreimageOracleSingletonAddress, gameInfo.OracleAddress)
}
func setupGenesisChain(t *testing.T, l1ChainID uint64) (deployer.ApplyPipelineOpts, *state.Intent, *state.State) {
lgr := testlog.Logger(t, slog.LevelDebug)
......@@ -711,7 +752,6 @@ func setupGenesisChain(t *testing.T, l1ChainID uint64) (deployer.ApplyPipelineOp
loc, _ := testutil.LocalArtifacts(t)
intent, st := newIntent(t, l1ChainIDBig, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainIDBig, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
opts := deployer.ApplyPipelineOpts{
......
......@@ -19,7 +19,7 @@ type DeployDisputeGameInput struct {
MaxClockDuration uint64
DelayedWethProxy common.Address
AnchorStateRegistryProxy common.Address
L2ChainId uint64
L2ChainId common.Hash
Proposer common.Address
Challenger common.Address
}
......
package opcm
import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
......@@ -47,7 +48,7 @@ func TestDeployDisputeGame(t *testing.T) {
MaxClockDuration: standard.DisputeMaxClockDuration,
DelayedWethProxy: common.Address{'D'},
AnchorStateRegistryProxy: common.Address{'A'},
L2ChainId: 69,
L2ChainId: common.BigToHash(big.NewInt(69)),
Proposer: common.Address{'P'},
Challenger: common.Address{'C'},
}
......
package pipeline
import (
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum/go-ethereum/common"
)
func DeployAdditionalDisputeGames(
env *Env,
intent *state.Intent,
st *state.State,
chainID common.Hash,
) error {
lgr := env.Logger.New("stage", "deploy-additional-dispute-games")
thisIntent, err := intent.Chain(chainID)
if err != nil {
return fmt.Errorf("failed to get chain intent: %w", err)
}
thisState, err := st.Chain(chainID)
if err != nil {
return fmt.Errorf("failed to get chain state: %w", err)
}
if !shouldDeployAdditionalDisputeGames(thisIntent, thisState) {
lgr.Info("additional dispute games deployment not needed")
return nil
}
if thisIntent.Roles.L1ProxyAdminOwner != env.Deployer {
return fmt.Errorf("cannot deploy additional dispute games when deployer is not L1PAO")
}
for _, game := range thisIntent.AdditionalDisputeGames {
if err := deployDisputeGame(env, st, thisIntent, thisState, game); err != nil {
return fmt.Errorf("failed to deploy additional dispute game: %w", err)
}
}
return nil
}
func deployDisputeGame(
env *Env,
st *state.State,
thisIntent *state.ChainIntent,
thisState *state.ChainState,
game state.AdditionalDisputeGame,
) error {
lgr := env.Logger.New("gameType", game.DisputeGameType)
var oracleAddr common.Address
if game.UseCustomOracle {
lgr.Info("deploying custom oracle")
out, err := opcm.DeployPreimageOracle(env.L1ScriptHost, opcm.DeployPreimageOracleInput{
MinProposalSize: new(big.Int).SetUint64(game.OracleMinProposalSize),
ChallengePeriod: new(big.Int).SetUint64(game.OracleChallengePeriodSeconds),
})
if err != nil {
return fmt.Errorf("failed to deploy preimage oracle: %w", err)
}
oracleAddr = out.PreimageOracle
} else {
lgr.Debug("using existing preimage oracle")
}
lgr.Info("deploying VM", "vmType", game.VMType)
var vmAddr common.Address
switch game.VMType {
case state.VMTypeAlphabet:
out, err := opcm.DeployAlphabetVM(env.L1ScriptHost, opcm.DeployAlphabetVMInput{
AbsolutePrestate: game.DisputeAbsolutePrestate,
PreimageOracle: st.ImplementationsDeployment.PreimageOracleSingletonAddress,
})
if err != nil {
return fmt.Errorf("failed to deploy Alphabet VM: %w", err)
}
vmAddr = out.AlphabetVM
default:
return fmt.Errorf("unsupported VM type: %v", game.VMType)
}
lgr.Info("vm deployed", "vmAddr", vmAddr)
lgr.Info("deploying dispute game")
out, err := opcm.DeployDisputeGame(env.L1ScriptHost, opcm.DeployDisputeGameInput{
Release: "dev",
VmAddress: vmAddr,
GameKind: "FaultDisputeGame",
GameType: game.DisputeGameType,
AbsolutePrestate: standard.DisputeAbsolutePrestate,
MaxGameDepth: game.DisputeMaxGameDepth,
SplitDepth: game.DisputeSplitDepth,
ClockExtension: game.DisputeClockExtension,
MaxClockDuration: game.DisputeMaxClockDuration,
DelayedWethProxy: thisState.DelayedWETHPermissionedGameProxyAddress,
AnchorStateRegistryProxy: thisState.AnchorStateRegistryProxyAddress,
L2ChainId: thisIntent.ID,
Proposer: thisIntent.Roles.Proposer,
Challenger: thisIntent.Roles.Challenger,
})
if err != nil {
return fmt.Errorf("failed to deploy dispute game: %w", err)
}
lgr.Info("dispute game deployed", "impl", out.DisputeGameImpl)
lgr.Info("setting dispute game impl on factory")
if err := opcm.SetDisputeGameImpl(
env.L1ScriptHost,
opcm.SetDisputeGameImplInput{
Factory: thisState.DisputeGameFactoryProxyAddress,
Impl: out.DisputeGameImpl,
},
); err != nil {
return fmt.Errorf("failed to set dispute game impl: %w", err)
}
thisState.AdditionalDisputeGames = append(thisState.AdditionalDisputeGames, state.AdditionalDisputeGameState{
GameType: game.DisputeGameType,
VMType: game.VMType,
GameAddress: out.DisputeGameImpl,
OracleAddress: oracleAddr,
VMAddress: vmAddr,
})
return nil
}
func shouldDeployAdditionalDisputeGames(thisIntent *state.ChainIntent, thisState *state.ChainState) bool {
if len(thisIntent.AdditionalDisputeGames) == 0 {
return false
}
if len(thisState.AdditionalDisputeGames) > 0 {
return false
}
return true
}
......@@ -4,22 +4,13 @@ import (
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
)
type SuperchainProofParams struct {
WithdrawalDelaySeconds uint64 `json:"withdrawalDelaySeconds" toml:"withdrawalDelaySeconds"`
MinProposalSizeBytes uint64 `json:"minProposalSizeBytes" toml:"minProposalSizeBytes"`
ChallengePeriodSeconds uint64 `json:"challengePeriodSeconds" toml:"challengePeriodSeconds"`
ProofMaturityDelaySeconds uint64 `json:"proofMaturityDelaySeconds" toml:"proofMaturityDelaySeconds"`
DisputeGameFinalityDelaySeconds uint64 `json:"disputeGameFinalityDelaySeconds" toml:"disputeGameFinalityDelaySeconds"`
MIPSVersion uint64 `json:"mipsVersion" toml:"mipsVersion"`
}
func DeployImplementations(env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "deploy-implementations")
......@@ -46,7 +37,7 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro
}
proofParams, err := jsonutil.MergeJSON(
SuperchainProofParams{
state.SuperchainProofParams{
WithdrawalDelaySeconds: standard.WithdrawalDelaySeconds,
MinProposalSizeBytes: standard.MinProposalSizeBytes,
ChallengePeriodSeconds: standard.ChallengePeriodSeconds,
......
......@@ -3,10 +3,10 @@ package pipeline
import (
"fmt"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum/go-ethereum/common"
)
......@@ -89,19 +89,9 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm
return nil
}
type ChainProofParams struct {
DisputeGameType uint32 `json:"disputeGameType" toml:"disputeGameType"`
DisputeAbsolutePrestate common.Hash `json:"disputeAbsolutePrestate" toml:"disputeAbsolutePrestate"`
DisputeMaxGameDepth uint64 `json:"disputeMaxGameDepth" toml:"disputeMaxGameDepth"`
DisputeSplitDepth uint64 `json:"disputeSplitDepth" toml:"disputeSplitDepth"`
DisputeClockExtension uint64 `json:"disputeClockExtension" toml:"disputeClockExtension"`
DisputeMaxClockDuration uint64 `json:"disputeMaxClockDuration" toml:"disputeMaxClockDuration"`
DangerouslyAllowCustomDisputeParameters bool `json:"dangerouslyAllowCustomDisputeParameters" toml:"dangerouslyAllowCustomDisputeParameters"`
}
func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInputV160, error) {
proofParams, err := jsonutil.MergeJSON(
ChainProofParams{
state.ChainProofParams{
DisputeGameType: standard.DisputeGameType,
DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate,
DisputeMaxGameDepth: standard.DisputeMaxGameDepth,
......
......@@ -8,6 +8,30 @@ import (
"github.com/ethereum/go-ethereum/common"
)
type VMType string
const (
VMTypeAlphabet = "ALPHABET"
)
type ChainProofParams struct {
DisputeGameType uint32 `json:"disputeGameType" toml:"disputeGameType"`
DisputeAbsolutePrestate common.Hash `json:"disputeAbsolutePrestate" toml:"disputeAbsolutePrestate"`
DisputeMaxGameDepth uint64 `json:"disputeMaxGameDepth" toml:"disputeMaxGameDepth"`
DisputeSplitDepth uint64 `json:"disputeSplitDepth" toml:"disputeSplitDepth"`
DisputeClockExtension uint64 `json:"disputeClockExtension" toml:"disputeClockExtension"`
DisputeMaxClockDuration uint64 `json:"disputeMaxClockDuration" toml:"disputeMaxClockDuration"`
DangerouslyAllowCustomDisputeParameters bool `json:"dangerouslyAllowCustomDisputeParameters" toml:"dangerouslyAllowCustomDisputeParameters"`
}
type AdditionalDisputeGame struct {
ChainProofParams
VMType VMType
UseCustomOracle bool
OracleMinProposalSize uint64
OracleChallengePeriodSeconds uint64
}
type ChainIntent struct {
ID common.Hash `json:"id" toml:"id"`
BaseFeeVaultRecipient common.Address `json:"baseFeeVaultRecipient" toml:"baseFeeVaultRecipient"`
......@@ -19,6 +43,7 @@ type ChainIntent struct {
Roles ChainRoles `json:"roles" toml:"roles"`
DeployOverrides map[string]any `json:"deployOverrides" toml:"deployOverrides"`
DangerousAltDAConfig genesis.AltDADeployConfig `json:"dangerousAltDAConfig,omitempty" toml:"dangerousAltDAConfig,omitempty"`
AdditionalDisputeGames []AdditionalDisputeGame `json:"dangerousAdditionalDisputeGames" toml:"dangerousAdditionalDisputeGames,omitempty"`
}
type ChainRoles struct {
......
......@@ -45,6 +45,15 @@ const (
var emptyAddress common.Address
var emptyHash common.Hash
type SuperchainProofParams struct {
WithdrawalDelaySeconds uint64 `json:"withdrawalDelaySeconds" toml:"withdrawalDelaySeconds"`
MinProposalSizeBytes uint64 `json:"minProposalSizeBytes" toml:"minProposalSizeBytes"`
ChallengePeriodSeconds uint64 `json:"challengePeriodSeconds" toml:"challengePeriodSeconds"`
ProofMaturityDelaySeconds uint64 `json:"proofMaturityDelaySeconds" toml:"proofMaturityDelaySeconds"`
DisputeGameFinalityDelaySeconds uint64 `json:"disputeGameFinalityDelaySeconds" toml:"disputeGameFinalityDelaySeconds"`
MIPSVersion uint64 `json:"mipsVersion" toml:"mipsVersion"`
}
type Intent struct {
DeploymentStrategy DeploymentStrategy `json:"deploymentStrategy" toml:"deploymentStrategy"`
ConfigType IntentConfigType `json:"configType" toml:"configType"`
......@@ -165,6 +174,9 @@ func (c *Intent) validateStandardValues() error {
chain.Eip1559Elasticity != standard.Eip1559Elasticity {
return fmt.Errorf("%w: chainId=%s", ErrNonStandardValue, chain.ID)
}
if len(chain.AdditionalDisputeGames) > 0 {
return fmt.Errorf("%w: chainId=%s additionalDisputeGames must be nil", ErrNonStandardValue, chain.ID)
}
}
return nil
......
......@@ -24,10 +24,58 @@ func TestValidateStandardValues(t *testing.T) {
err = intent.Check()
require.NoError(t, err)
intent.Chains[0].Eip1559Denominator = 3 // set to non-standard value
tests := []struct {
name string
mutator func(intent *Intent)
err error
}{
{
"EIP1559Denominator",
func(intent *Intent) {
intent.Chains[0].Eip1559Denominator = 3
},
ErrNonStandardValue,
},
{
"EIP1559DenominatorCanyon",
func(intent *Intent) {
intent.Chains[0].Eip1559DenominatorCanyon = 3
},
ErrNonStandardValue,
},
{
"EIP1559Elasticity",
func(intent *Intent) {
intent.Chains[0].Eip1559Elasticity = 999
},
ErrNonStandardValue,
},
{
"AdditionalDisputeGames",
func(intent *Intent) {
intent.Chains[0].AdditionalDisputeGames = []AdditionalDisputeGame{
{
VMType: VMTypeAlphabet,
},
}
},
ErrNonStandardValue,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
intent, err := NewIntentStandard(DeploymentStrategyLive, 1, []common.Hash{common.HexToHash("0x336")})
require.NoError(t, err)
setChainRoles(&intent)
setFeeAddresses(&intent)
tt.mutator(&intent)
err = intent.Check()
require.Error(t, err)
require.ErrorIs(t, err, ErrNonStandardValue)
require.ErrorIs(t, err, tt.err)
})
}
}
func TestValidateCustomValues(t *testing.T) {
......
......@@ -77,6 +77,14 @@ type ImplementationsDeployment struct {
DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"`
}
type AdditionalDisputeGameState struct {
GameType uint32
GameAddress common.Address
VMAddress common.Address
OracleAddress common.Address
VMType VMType
}
type ChainState struct {
ID common.Hash `json:"id"`
......@@ -97,6 +105,7 @@ type ChainState struct {
DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"`
DataAvailabilityChallengeProxyAddress common.Address `json:"dataAvailabilityChallengeProxyAddress"`
DataAvailabilityChallengeImplAddress common.Address `json:"dataAvailabilityChallengeImplAddress"`
AdditionalDisputeGames []AdditionalDisputeGameState `json:"additionalDisputeGames"`
Allocs *GzipData[foundry.ForgeAllocs] `json:"allocs"`
......
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