Commit 717330e9 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Port deployers, add end-to-end contract deployments (#11922)

This PR ports over the deployers in `interopgen` into `op-deployer`, and updates `op-deployer` to support end-to end contract deployments for both the Superchain and individual OP Chains.

This PR includes a couple of bugfixes for things I discovered along the way:

1. The script host is updated to bump the nonce of the address calling the CREATE2 deployer when broadcasting. This fixes a chain/simulation mismatch that blocked contracts from being deployed.
2. The DeployImplementations contract used a fixed CREATE2 salt, which caused problems on repeated deployments. I updated the contract to pull the nonce from the environment as we do elsewhere.

Builds on https://github.com/ethereum-optimism/optimism/pull/11910.
parent 7d92c98c
...@@ -126,19 +126,32 @@ func Apply(ctx context.Context, cfg ApplyConfig) error { ...@@ -126,19 +126,32 @@ func Apply(ctx context.Context, cfg ApplyConfig) error {
return nil return nil
} }
type pipelineStage struct {
name string
stage pipeline.Stage
}
func ApplyPipeline( func ApplyPipeline(
ctx context.Context, ctx context.Context,
env *pipeline.Env, env *pipeline.Env,
intent *state.Intent, intent *state.Intent,
st *state.State, st *state.State,
) error { ) error {
pline := []struct { pline := []pipelineStage{
name string
stage pipeline.Stage
}{
{"init", pipeline.Init}, {"init", pipeline.Init},
{"deploy-superchain", pipeline.DeploySuperchain}, {"deploy-superchain", pipeline.DeploySuperchain},
{"deploy-implementations", pipeline.DeployImplementations},
}
for _, chain := range intent.Chains {
pline = append(pline, pipelineStage{
fmt.Sprintf("deploy-opchain-%s", chain.ID.Hex()),
func(ctx context.Context, env *pipeline.Env, intent *state.Intent, st *state.State) error {
return pipeline.DeployOPChain(ctx, env, intent, st, chain.ID)
},
})
} }
for _, stage := range pline { for _, stage := range pline {
if err := stage.stage(ctx, env, intent, st); err != nil { if err := stage.stage(ctx, env, intent, st); err != nil {
return fmt.Errorf("error in pipeline stage: %w", err) return fmt.Errorf("error in pipeline stage: %w", err)
......
package broadcaster
import (
"context"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type discardBroadcaster struct {
}
func DiscardBroadcaster() Broadcaster {
return &discardBroadcaster{}
}
func (d *discardBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, error) {
return nil, nil
}
func (d *discardBroadcaster) Hook(bcast script.Broadcast) {}
...@@ -20,13 +20,14 @@ import ( ...@@ -20,13 +20,14 @@ import (
) )
const ( const (
GasPadFactor = 1.5 GasPadFactor = 2.0
) )
type KeyedBroadcaster struct { type KeyedBroadcaster struct {
lgr log.Logger lgr log.Logger
mgr txmgr.TxManager mgr txmgr.TxManager
bcasts []script.Broadcast bcasts []script.Broadcast
client *ethclient.Client
} }
type KeyedBroadcasterOpts struct { type KeyedBroadcasterOpts struct {
...@@ -84,8 +85,9 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) { ...@@ -84,8 +85,9 @@ func NewKeyedBroadcaster(cfg KeyedBroadcasterOpts) (*KeyedBroadcaster, error) {
} }
return &KeyedBroadcaster{ return &KeyedBroadcaster{
lgr: cfg.Logger, lgr: cfg.Logger,
mgr: mgr, mgr: mgr,
client: cfg.Client,
}, nil }, nil
} }
...@@ -98,8 +100,13 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -98,8 +100,13 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
futures := make([]<-chan txmgr.SendResponse, len(t.bcasts)) futures := make([]<-chan txmgr.SendResponse, len(t.bcasts))
ids := make([]common.Hash, len(t.bcasts)) ids := make([]common.Hash, len(t.bcasts))
latestBlock, err := t.client.BlockByNumber(ctx, nil)
if err != nil {
return nil, fmt.Errorf("failed to get latest block: %w", err)
}
for i, bcast := range t.bcasts { for i, bcast := range t.bcasts {
futures[i], ids[i] = t.broadcast(ctx, bcast) futures[i], ids[i] = t.broadcast(ctx, bcast, latestBlock.GasLimit())
t.lgr.Info( t.lgr.Info(
"transaction broadcasted", "transaction broadcasted",
"id", ids[i], "id", ids[i],
...@@ -107,7 +114,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -107,7 +114,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
) )
} }
var err *multierror.Error var txErr *multierror.Error
var completed int var completed int
for i, fut := range futures { for i, fut := range futures {
bcastRes := <-fut bcastRes := <-fut
...@@ -122,7 +129,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -122,7 +129,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
if bcastRes.Receipt.Status == 0 { if bcastRes.Receipt.Status == 0 {
failErr := fmt.Errorf("transaction failed: %s", outRes.Receipt.TxHash.String()) failErr := fmt.Errorf("transaction failed: %s", outRes.Receipt.TxHash.String())
err = multierror.Append(err, failErr) txErr = multierror.Append(txErr, failErr)
outRes.Err = failErr outRes.Err = failErr
t.lgr.Error( t.lgr.Error(
"transaction failed on chain", "transaction failed on chain",
...@@ -144,7 +151,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -144,7 +151,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
) )
} }
} else { } else {
err = multierror.Append(err, bcastRes.Err) txErr = multierror.Append(txErr, bcastRes.Err)
outRes.Err = bcastRes.Err outRes.Err = bcastRes.Err
t.lgr.Error( t.lgr.Error(
"transaction failed", "transaction failed",
...@@ -157,12 +164,13 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ...@@ -157,12 +164,13 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er
results = append(results, outRes) results = append(results, outRes)
} }
return results, err.ErrorOrNil() return results, txErr.ErrorOrNil()
} }
func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast) (<-chan txmgr.SendResponse, common.Hash) { func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast, blockGasLimit uint64) (<-chan txmgr.SendResponse, common.Hash) {
id := bcast.ID() ch := make(chan txmgr.SendResponse, 1)
id := bcast.ID()
value := ((*uint256.Int)(bcast.Value)).ToBig() value := ((*uint256.Int)(bcast.Value)).ToBig()
var candidate txmgr.TxCandidate var candidate txmgr.TxCandidate
switch bcast.Type { switch bcast.Type {
...@@ -172,27 +180,45 @@ func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast ...@@ -172,27 +180,45 @@ func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast
TxData: bcast.Input, TxData: bcast.Input,
To: to, To: to,
Value: value, Value: value,
GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, false), GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, false, blockGasLimit),
} }
case script.BroadcastCreate: case script.BroadcastCreate:
candidate = txmgr.TxCandidate{ candidate = txmgr.TxCandidate{
TxData: bcast.Input, TxData: bcast.Input,
To: nil, To: nil,
GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, true), GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, true, blockGasLimit),
}
case script.BroadcastCreate2:
txData := make([]byte, len(bcast.Salt)+len(bcast.Input))
copy(txData, bcast.Salt[:])
copy(txData[len(bcast.Salt):], bcast.Input)
candidate = txmgr.TxCandidate{
TxData: txData,
To: &script.DeterministicDeployerAddress,
Value: value,
GasLimit: padGasLimit(bcast.Input, bcast.GasUsed, true, blockGasLimit),
} }
} }
ch := make(chan txmgr.SendResponse, 1)
t.mgr.SendAsync(ctx, candidate, ch) t.mgr.SendAsync(ctx, candidate, ch)
return ch, id return ch, id
} }
func padGasLimit(data []byte, gasUsed uint64, creation bool) uint64 { // padGasLimit calculates the gas limit for a transaction based on the intrinsic gas and the gas used by
// the underlying call. Values are multiplied by a pad factor to account for any discrepancies. The output
// is clamped to the block gas limit since Geth will reject transactions that exceed it before letting them
// into the mempool.
func padGasLimit(data []byte, gasUsed uint64, creation bool, blockGasLimit uint64) uint64 {
intrinsicGas, err := core.IntrinsicGas(data, nil, creation, true, true, false) intrinsicGas, err := core.IntrinsicGas(data, nil, creation, true, true, false)
// This method never errors - we should look into it if it does. // This method never errors - we should look into it if it does.
if err != nil { if err != nil {
panic(err) panic(err)
} }
return uint64(float64(intrinsicGas+gasUsed) * GasPadFactor) limit := uint64(float64(intrinsicGas+gasUsed) * GasPadFactor)
if limit > blockGasLimit {
return blockGasLimit
}
return limit
} }
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/holiman/uint256"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/pipeline"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
...@@ -29,6 +30,15 @@ participants: ...@@ -29,6 +30,15 @@ participants:
cl_type: lighthouse cl_type: lighthouse
network_params: network_params:
prefunded_accounts: '{ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { "balance": "1000000ETH" } }' prefunded_accounts: '{ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { "balance": "1000000ETH" } }'
additional_preloaded_contracts: '{
"0x4e59b44847b379578588920cA78FbF26c0B4956C": {
balance: "0ETH",
code: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3",
storage: {},
nonce: 0,
secretKey: "0x"
}
}'
network_id: "77799777" network_id: "77799777"
seconds_per_slot: 3 seconds_per_slot: 3
` `
...@@ -78,6 +88,8 @@ func TestEndToEndApply(t *testing.T) { ...@@ -78,6 +88,8 @@ func TestEndToEndApply(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID)) signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID))
id := uint256.NewInt(1)
addrFor := func(key devkeys.Key) common.Address { addrFor := func(key devkeys.Key) common.Address {
addr, err := dk.Address(key) addr, err := dk.Address(key)
require.NoError(t, err) require.NoError(t, err)
...@@ -100,6 +112,20 @@ func TestEndToEndApply(t *testing.T) { ...@@ -100,6 +112,20 @@ func TestEndToEndApply(t *testing.T) {
UseFaultProofs: true, UseFaultProofs: true,
FundDevAccounts: true, FundDevAccounts: true,
ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL), ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL),
Chains: []state.ChainIntent{
{
ID: id.Bytes32(),
Roles: state.ChainRoles{
ProxyAdminOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)),
SystemConfigOwner: addrFor(devkeys.SystemConfigOwner.Key(l1ChainID)),
GovernanceTokenOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)),
UnsafeBlockSigner: addrFor(devkeys.SequencerP2PRole.Key(l1ChainID)),
Batcher: addrFor(devkeys.BatcherRole.Key(l1ChainID)),
Proposer: addrFor(devkeys.ProposerRole.Key(l1ChainID)),
Challenger: addrFor(devkeys.ChallengerRole.Key(l1ChainID)),
},
},
},
} }
st := &state.State{ st := &state.State{
Version: 1, Version: 1,
...@@ -112,16 +138,63 @@ func TestEndToEndApply(t *testing.T) { ...@@ -112,16 +138,63 @@ func TestEndToEndApply(t *testing.T) {
st, st,
)) ))
addrs := []common.Address{ addrs := []struct {
st.SuperchainDeployment.ProxyAdminAddress, name string
st.SuperchainDeployment.SuperchainConfigProxyAddress, addr common.Address
st.SuperchainDeployment.SuperchainConfigImplAddress, }{
st.SuperchainDeployment.ProtocolVersionsProxyAddress, {"SuperchainProxyAdmin", st.SuperchainDeployment.ProxyAdminAddress},
st.SuperchainDeployment.ProtocolVersionsImplAddress, {"SuperchainConfigProxy", st.SuperchainDeployment.SuperchainConfigProxyAddress},
{"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress},
{"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress},
{"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress},
{"Opsm", st.ImplementationsDeployment.OpsmAddress},
{"DelayedWETHImpl", st.ImplementationsDeployment.DelayedWETHImplAddress},
{"OptimismPortalImpl", st.ImplementationsDeployment.OptimismPortalImplAddress},
{"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress},
{"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress},
{"SystemConfigImpl", st.ImplementationsDeployment.SystemConfigImplAddress},
{"L1CrossDomainMessengerImpl", st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress},
{"L1ERC721BridgeImpl", st.ImplementationsDeployment.L1ERC721BridgeImplAddress},
{"L1StandardBridgeImpl", st.ImplementationsDeployment.L1StandardBridgeImplAddress},
{"OptimismMintableERC20FactoryImpl", st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress},
{"DisputeGameFactoryImpl", st.ImplementationsDeployment.DisputeGameFactoryImplAddress},
} }
for _, addr := range addrs { for _, addr := range addrs {
code, err := l1Client.CodeAt(ctx, addr, nil) t.Run(addr.name, func(t *testing.T) {
require.NoError(t, err) code, err := l1Client.CodeAt(ctx, addr.addr, nil)
require.NotEmpty(t, code) require.NoError(t, err)
require.NotEmpty(t, code, "contracts %s at %s has no code", addr.name, addr.addr)
})
}
for _, chainState := range st.Chains {
chainAddrs := []struct {
name string
addr common.Address
}{
{"ProxyAdminAddress", chainState.ProxyAdminAddress},
{"AddressManagerAddress", chainState.AddressManagerAddress},
{"L1ERC721BridgeProxyAddress", chainState.L1ERC721BridgeProxyAddress},
{"SystemConfigProxyAddress", chainState.SystemConfigProxyAddress},
{"OptimismMintableERC20FactoryProxyAddress", chainState.OptimismMintableERC20FactoryProxyAddress},
{"L1StandardBridgeProxyAddress", chainState.L1StandardBridgeProxyAddress},
{"L1CrossDomainMessengerProxyAddress", chainState.L1CrossDomainMessengerProxyAddress},
{"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress},
{"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress},
{"DisputeGameFactoryImplAddress", chainState.DisputeGameFactoryImplAddress},
{"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress},
{"AnchorStateRegistryImplAddress", chainState.AnchorStateRegistryImplAddress},
{"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress},
{"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress},
{"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress},
{"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress},
}
for _, addr := range chainAddrs {
t.Run(fmt.Sprintf("chain %s - %s", chainState.ID, addr.name), func(t *testing.T) {
code, err := l1Client.CodeAt(ctx, addr.addr, nil)
require.NoError(t, err)
require.NotEmpty(t, code, "contracts %s at %s for chain %s has no code", addr.name, addr.addr, chainState.ID)
})
}
} }
} }
package deployers package opsm
import ( import (
"fmt" "fmt"
...@@ -19,8 +19,9 @@ type DeployImplementationsInput struct { ...@@ -19,8 +19,9 @@ type DeployImplementationsInput struct {
Release string Release string
SuperchainConfigProxy common.Address SuperchainConfigProxy common.Address
ProtocolVersionsProxy common.Address ProtocolVersionsProxy common.Address
SuperchainProxyAdmin common.Address
UseInterop bool // if true, deploy Interop implementations UseInterop bool // if true, deploy Interop implementations
SuperchainProxyAdmin common.Address
} }
func (input *DeployImplementationsInput) InputSet() bool { func (input *DeployImplementationsInput) InputSet() bool {
...@@ -49,21 +50,24 @@ type DeployImplementationsScript struct { ...@@ -49,21 +50,24 @@ type DeployImplementationsScript struct {
Run func(input, output common.Address) error Run func(input, output common.Address) error
} }
func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInput) (*DeployImplementationsOutput, error) { func DeployImplementations(
output := &DeployImplementationsOutput{} host *script.Host,
inputAddr := l1Host.NewScriptAddress() input DeployImplementationsInput,
outputAddr := l1Host.NewScriptAddress() ) (DeployImplementationsOutput, error) {
var output DeployImplementationsOutput
inputAddr := host.NewScriptAddress()
outputAddr := host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeployImplementationsInput](l1Host, inputAddr, input) cleanupInput, err := script.WithPrecompileAtAddress[*DeployImplementationsInput](host, inputAddr, &input)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to insert DeployImplementationsInput precompile: %w", err) return output, fmt.Errorf("failed to insert DeployImplementationsInput precompile: %w", err)
} }
defer cleanupInput() defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeployImplementationsOutput](l1Host, outputAddr, output, cleanupOutput, err := script.WithPrecompileAtAddress[*DeployImplementationsOutput](host, outputAddr, &output,
script.WithFieldSetter[*DeployImplementationsOutput]) script.WithFieldSetter[*DeployImplementationsOutput])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to insert DeployImplementationsOutput precompile: %w", err) return output, fmt.Errorf("failed to insert DeployImplementationsOutput precompile: %w", err)
} }
defer cleanupOutput() defer cleanupOutput()
...@@ -71,9 +75,9 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu ...@@ -71,9 +75,9 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu
if input.UseInterop { if input.UseInterop {
implContract = "DeployImplementationsInterop" implContract = "DeployImplementationsInterop"
} }
deployScript, cleanupDeploy, err := script.WithScript[DeployImplementationsScript](l1Host, "DeployImplementations.s.sol", implContract) deployScript, cleanupDeploy, err := script.WithScript[DeployImplementationsScript](host, "DeployImplementations.s.sol", implContract)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load %s script: %w", implContract, err) return output, fmt.Errorf("failed to load %s script: %w", implContract, err)
} }
defer cleanupDeploy() defer cleanupDeploy()
...@@ -81,8 +85,8 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu ...@@ -81,8 +85,8 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu
if input.UseInterop { if input.UseInterop {
opsmContract = "OPStackManagerInterop" opsmContract = "OPStackManagerInterop"
} }
if err := l1Host.RememberOnLabel("OPStackManager", opsmContract+".sol", opsmContract); err != nil { if err := host.RememberOnLabel("OPStackManager", opsmContract+".sol", opsmContract); err != nil {
return nil, fmt.Errorf("failed to link OPStackManager label: %w", err) return output, fmt.Errorf("failed to link OPStackManager label: %w", err)
} }
// So we can see in detail where the SystemConfig interop initializer fails // So we can see in detail where the SystemConfig interop initializer fails
...@@ -90,12 +94,12 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu ...@@ -90,12 +94,12 @@ func DeployImplementations(l1Host *script.Host, input *DeployImplementationsInpu
if input.UseInterop { if input.UseInterop {
sysConfig = "SystemConfigInterop" sysConfig = "SystemConfigInterop"
} }
if err := l1Host.RememberOnLabel("SystemConfigImpl", sysConfig+".sol", sysConfig); err != nil { if err := host.RememberOnLabel("SystemConfigImpl", sysConfig+".sol", sysConfig); err != nil {
return nil, fmt.Errorf("failed to link SystemConfig label: %w", err) return output, fmt.Errorf("failed to link SystemConfig label: %w", err)
} }
if err := deployScript.Run(inputAddr, outputAddr); err != nil { if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return nil, fmt.Errorf("failed to run %s script: %w", implContract, err) return output, fmt.Errorf("failed to run %s script: %w", implContract, err)
} }
return output, nil return output, nil
......
package deployers package opsm
import ( import (
"fmt" "fmt"
...@@ -35,6 +35,7 @@ type DeployOPChainOutput struct { ...@@ -35,6 +35,7 @@ type DeployOPChainOutput struct {
OptimismMintableERC20FactoryProxy common.Address OptimismMintableERC20FactoryProxy common.Address
L1StandardBridgeProxy common.Address L1StandardBridgeProxy common.Address
L1CrossDomainMessengerProxy common.Address L1CrossDomainMessengerProxy common.Address
// Fault proof contracts below. // Fault proof contracts below.
OptimismPortalProxy common.Address OptimismPortalProxy common.Address
DisputeGameFactoryProxy common.Address DisputeGameFactoryProxy common.Address
...@@ -55,33 +56,33 @@ type DeployOPChainScript struct { ...@@ -55,33 +56,33 @@ type DeployOPChainScript struct {
Run func(input, output common.Address) error Run func(input, output common.Address) error
} }
func DeployOPChain(l1Host *script.Host, input *DeployOPChainInput) (*DeployOPChainOutput, error) { func DeployOPChain(host *script.Host, input DeployOPChainInput) (DeployOPChainOutput, error) {
output := &DeployOPChainOutput{} var dco DeployOPChainOutput
inputAddr := l1Host.NewScriptAddress() inputAddr := host.NewScriptAddress()
outputAddr := l1Host.NewScriptAddress() outputAddr := host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeployOPChainInput](l1Host, inputAddr, input) cleanupInput, err := script.WithPrecompileAtAddress[*DeployOPChainInput](host, inputAddr, &input)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to insert DeployOPChainInput precompile: %w", err) return dco, fmt.Errorf("failed to insert DeployOPChainInput precompile: %w", err)
} }
defer cleanupInput() defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeployOPChainOutput](l1Host, outputAddr, output, cleanupOutput, err := script.WithPrecompileAtAddress[*DeployOPChainOutput](host, outputAddr, &dco,
script.WithFieldSetter[*DeployOPChainOutput]) script.WithFieldSetter[*DeployOPChainOutput])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to insert DeployOPChainOutput precompile: %w", err) return dco, fmt.Errorf("failed to insert DeployOPChainOutput precompile: %w", err)
} }
defer cleanupOutput() defer cleanupOutput()
deployScript, cleanupDeploy, err := script.WithScript[DeployOPChainScript](l1Host, "DeployOPChain.s.sol", "DeployOPChain") deployScript, cleanupDeploy, err := script.WithScript[DeployOPChainScript](host, "DeployOPChain.s.sol", "DeployOPChain")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load DeployOPChain script: %w", err) return dco, fmt.Errorf("failed to load DeployOPChain script: %w", err)
} }
defer cleanupDeploy() defer cleanupDeploy()
if err := deployScript.Run(inputAddr, outputAddr); err != nil { if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return nil, fmt.Errorf("failed to run DeployOPChain script: %w", err) return dco, fmt.Errorf("failed to run DeployOPChain script: %w", err)
} }
return output, nil return dco, nil
} }
package opsm package opsm
import ( import (
"context"
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
...@@ -29,11 +27,11 @@ func (dsi *DeploySuperchainInput) InputSet() bool { ...@@ -29,11 +27,11 @@ func (dsi *DeploySuperchainInput) InputSet() bool {
} }
type DeploySuperchainOutput struct { type DeploySuperchainOutput struct {
SuperchainProxyAdmin common.Address `toml:"superchainProxyAdmin"` SuperchainProxyAdmin common.Address
SuperchainConfigImpl common.Address `toml:"superchainConfigImpl"` SuperchainConfigImpl common.Address
SuperchainConfigProxy common.Address `toml:"superchainConfigProxy"` SuperchainConfigProxy common.Address
ProtocolVersionsImpl common.Address `toml:"protocolVersionsImpl"` ProtocolVersionsImpl common.Address
ProtocolVersionsProxy common.Address `toml:"protocolVersionsProxy"` ProtocolVersionsProxy common.Address
} }
func (output *DeploySuperchainOutput) CheckOutput() error { func (output *DeploySuperchainOutput) CheckOutput() error {
...@@ -54,46 +52,13 @@ type DeploySuperchainOpts struct { ...@@ -54,46 +52,13 @@ type DeploySuperchainOpts struct {
Logger log.Logger Logger log.Logger
} }
func DeploySuperchainForge(ctx context.Context, opts DeploySuperchainOpts) (DeploySuperchainOutput, error) { func DeploySuperchain(h *script.Host, input DeploySuperchainInput) (DeploySuperchainOutput, error) {
var dso DeploySuperchainOutput var dso DeploySuperchainOutput
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: opts.Logger,
ChainID: opts.ChainID,
Client: opts.Client,
Signer: opts.Signer,
From: opts.Deployer,
})
if err != nil {
return dso, fmt.Errorf("failed to create broadcaster: %w", err)
}
scriptCtx := script.DefaultContext
scriptCtx.Sender = opts.Deployer
scriptCtx.Origin = opts.Deployer
artifacts := &foundry.ArtifactsFS{FS: opts.ArtifactsFS}
h := script.NewHost(
opts.Logger,
artifacts,
nil,
scriptCtx,
script.WithBroadcastHook(bcaster.Hook),
script.WithIsolatedBroadcasts(),
)
if err := h.EnableCheats(); err != nil {
return dso, fmt.Errorf("failed to enable cheats: %w", err)
}
nonce, err := opts.Client.NonceAt(ctx, opts.Deployer, nil)
if err != nil {
return dso, fmt.Errorf("failed to get deployer nonce: %w", err)
}
inputAddr := h.NewScriptAddress() inputAddr := h.NewScriptAddress()
outputAddr := h.NewScriptAddress() outputAddr := h.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](h, inputAddr, &opts.Input) cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](h, inputAddr, &input)
if err != nil { if err != nil {
return dso, fmt.Errorf("failed to insert DeploySuperchainInput precompile: %w", err) return dso, fmt.Errorf("failed to insert DeploySuperchainInput precompile: %w", err)
} }
...@@ -116,17 +81,9 @@ func DeploySuperchainForge(ctx context.Context, opts DeploySuperchainOpts) (Depl ...@@ -116,17 +81,9 @@ func DeploySuperchainForge(ctx context.Context, opts DeploySuperchainOpts) (Depl
} }
defer cleanupDeploy() defer cleanupDeploy()
h.SetNonce(opts.Deployer, nonce)
opts.Logger.Info("deployer nonce", "nonce", nonce)
if err := deployScript.Run(inputAddr, outputAddr); err != nil { if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return dso, fmt.Errorf("failed to run DeploySuperchain script: %w", err) return dso, fmt.Errorf("failed to run DeploySuperchain script: %w", err)
} }
if _, err := bcaster.Broadcast(ctx); err != nil {
return dso, fmt.Errorf("failed to broadcast transactions: %w", err)
}
return dso, nil return dso, nil
} }
package pipeline
import (
"context"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type CallScriptBroadcastOpts struct {
L1ChainID *big.Int
Logger log.Logger
ArtifactsFS foundry.StatDirFs
Deployer common.Address
Signer opcrypto.SignerFn
Client *ethclient.Client
Handler func(host *script.Host) error
}
func CallScriptBroadcast(
ctx context.Context,
opts CallScriptBroadcastOpts,
) error {
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: opts.Logger,
ChainID: opts.L1ChainID,
Client: opts.Client,
Signer: opts.Signer,
From: opts.Deployer,
})
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
scriptCtx := script.DefaultContext
scriptCtx.Sender = opts.Deployer
scriptCtx.Origin = opts.Deployer
artifacts := &foundry.ArtifactsFS{FS: opts.ArtifactsFS}
h := script.NewHost(
opts.Logger,
artifacts,
nil,
scriptCtx,
script.WithBroadcastHook(bcaster.Hook),
script.WithIsolatedBroadcasts(),
script.WithCreate2Deployer(),
)
if err := h.EnableCheats(); err != nil {
return fmt.Errorf("failed to enable cheats: %w", err)
}
err = opts.Handler(h)
if err != nil {
return fmt.Errorf("failed to run handler: %w", err)
}
if _, err := bcaster.Broadcast(ctx); err != nil {
return fmt.Errorf("failed to broadcast: %w", err)
}
return nil
}
package pipeline
import (
"context"
"fmt"
"math/big"
"os"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
func DeployImplementations(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "deploy-implementations")
if !shouldDeployImplementations(intent, st) {
lgr.Info("implementations deployment not needed")
return nil
}
lgr.Info("deploying implementations")
var artifactsFS foundry.StatDirFs
var err error
if intent.ContractArtifactsURL.Scheme == "file" {
fs := os.DirFS(intent.ContractArtifactsURL.Path)
artifactsFS = fs.(foundry.StatDirFs)
} else {
return fmt.Errorf("only file:// artifacts URLs are supported")
}
var dump *foundry.ForgeAllocs
var dio opsm.DeployImplementationsOutput
err = CallScriptBroadcast(
ctx,
CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr,
ArtifactsFS: artifactsFS,
Deployer: env.Deployer,
Signer: env.Signer,
Client: env.L1Client,
Handler: func(host *script.Host) error {
host.SetEnvVar("IMPL_SALT", st.Create2Salt.Hex()[2:])
host.ImportState(st.SuperchainDeployment.StateDump)
dio, err = opsm.DeployImplementations(
host,
opsm.DeployImplementationsInput{
WithdrawalDelaySeconds: big.NewInt(604800),
MinProposalSizeBytes: big.NewInt(126000),
ChallengePeriodSeconds: big.NewInt(86400),
ProofMaturityDelaySeconds: big.NewInt(604800),
DisputeGameFinalityDelaySeconds: big.NewInt(302400),
Release: "op-contracts/v1.6.0",
SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress,
ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress,
SuperchainProxyAdmin: st.SuperchainDeployment.ProxyAdminAddress,
UseInterop: false,
},
)
if err != nil {
return fmt.Errorf("error deploying implementations: %w", err)
}
dump, err = host.StateDump()
if err != nil {
return fmt.Errorf("error dumping state: %w", err)
}
return nil
},
},
)
if err != nil {
return fmt.Errorf("error deploying implementations: %w", err)
}
st.ImplementationsDeployment = &state.ImplementationsDeployment{
OpsmAddress: dio.Opsm,
DelayedWETHImplAddress: dio.DelayedWETHImpl,
OptimismPortalImplAddress: dio.OptimismPortalImpl,
PreimageOracleSingletonAddress: dio.PreimageOracleSingleton,
MipsSingletonAddress: dio.MipsSingleton,
SystemConfigImplAddress: dio.SystemConfigImpl,
L1CrossDomainMessengerImplAddress: dio.L1CrossDomainMessengerImpl,
L1ERC721BridgeImplAddress: dio.L1ERC721BridgeImpl,
L1StandardBridgeImplAddress: dio.L1StandardBridgeImpl,
OptimismMintableERC20FactoryImplAddress: dio.OptimismMintableERC20FactoryImpl,
DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl,
StateDump: dump,
}
if err := env.WriteState(st); err != nil {
return err
}
return nil
}
func shouldDeployImplementations(intent *state.Intent, st *state.State) bool {
return st.ImplementationsDeployment == nil
}
...@@ -2,8 +2,11 @@ package pipeline ...@@ -2,8 +2,11 @@ package pipeline
import ( import (
"context" "context"
"crypto/rand"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
) )
...@@ -20,6 +23,13 @@ func Init(ctx context.Context, env *Env, intent *state.Intent, st *state.State) ...@@ -20,6 +23,13 @@ func Init(ctx context.Context, env *Env, intent *state.Intent, st *state.State)
return fmt.Errorf("unsupported state version: %d", st.Version) return fmt.Errorf("unsupported state version: %d", st.Version)
} }
if st.Create2Salt == (common.Hash{}) {
_, err := rand.Read(st.Create2Salt[:])
if err != nil {
return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
}
}
// If the state has never been applied, we don't need to perform // If the state has never been applied, we don't need to perform
// any additional checks. // any additional checks.
if st.AppliedIntent == nil { if st.AppliedIntent == nil {
......
package pipeline
import (
"context"
"fmt"
"math/big"
"os"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common"
)
func DeployOPChain(ctx context.Context, env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "deploy-opchain")
if !shouldDeployOPChain(intent, st, chainID) {
lgr.Info("opchain deployment not needed")
return nil
}
lgr.Info("deploying OP chain", "id", chainID.Hex())
var artifactsFS foundry.StatDirFs
var err error
if intent.ContractArtifactsURL.Scheme == "file" {
fs := os.DirFS(intent.ContractArtifactsURL.Path)
artifactsFS = fs.(foundry.StatDirFs)
} else {
return fmt.Errorf("only file:// artifacts URLs are supported")
}
thisIntent, err := intent.Chain(chainID)
if err != nil {
return fmt.Errorf("failed to get chain intent: %w", err)
}
var dco opsm.DeployOPChainOutput
err = CallScriptBroadcast(
ctx,
CallScriptBroadcastOpts{
L1ChainID: big.NewInt(int64(intent.L1ChainID)),
Logger: lgr,
ArtifactsFS: artifactsFS,
Deployer: env.Deployer,
Signer: env.Signer,
Client: env.L1Client,
Handler: func(host *script.Host) error {
host.ImportState(st.ImplementationsDeployment.StateDump)
dco, err = opsm.DeployOPChain(
host,
opsm.DeployOPChainInput{
OpChainProxyAdminOwner: thisIntent.Roles.ProxyAdminOwner,
SystemConfigOwner: thisIntent.Roles.SystemConfigOwner,
Batcher: thisIntent.Roles.Batcher,
UnsafeBlockSigner: thisIntent.Roles.UnsafeBlockSigner,
Proposer: thisIntent.Roles.Proposer,
Challenger: thisIntent.Roles.Challenger,
BasefeeScalar: 1368,
BlobBaseFeeScalar: 801949,
L2ChainId: chainID.Big(),
Opsm: st.ImplementationsDeployment.OpsmAddress,
},
)
return err
},
},
)
if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err)
}
st.Chains = append(st.Chains, state.ChainState{
ID: chainID,
ProxyAdminAddress: dco.OpChainProxyAdmin,
AddressManagerAddress: dco.AddressManager,
L1ERC721BridgeProxyAddress: dco.L1ERC721BridgeProxy,
SystemConfigProxyAddress: dco.SystemConfigProxy,
OptimismMintableERC20FactoryProxyAddress: dco.OptimismMintableERC20FactoryProxy,
L1StandardBridgeProxyAddress: dco.L1StandardBridgeProxy,
L1CrossDomainMessengerProxyAddress: dco.L1CrossDomainMessengerProxy,
OptimismPortalProxyAddress: dco.OptimismPortalProxy,
DisputeGameFactoryProxyAddress: dco.DisputeGameFactoryProxy,
DisputeGameFactoryImplAddress: dco.DisputeGameFactoryImpl,
AnchorStateRegistryProxyAddress: dco.AnchorStateRegistryProxy,
AnchorStateRegistryImplAddress: dco.AnchorStateRegistryImpl,
FaultDisputeGameAddress: dco.FaultDisputeGame,
PermissionedDisputeGameAddress: dco.PermissionedDisputeGame,
DelayedWETHPermissionedGameProxyAddress: dco.DelayedWETHPermissionedGameProxy,
DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy,
})
if err := env.WriteState(st); err != nil {
return err
}
return nil
}
func shouldDeployOPChain(intent *state.Intent, st *state.State, chainID common.Hash) bool {
for _, chain := range st.Chains {
if chain.ID == chainID {
return false
}
}
return true
}
...@@ -6,14 +6,14 @@ import ( ...@@ -6,14 +6,14 @@ import (
"math/big" "math/big"
"os" "os"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
) )
const DefaultContractsBedrockRepo = "us-docker.pkg.dev/oplabs-tools-artifacts/images/contracts-bedrock"
func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error { func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "deploy-superchain") lgr := env.Logger.New("stage", "deploy-superchain")
...@@ -33,23 +33,38 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s ...@@ -33,23 +33,38 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s
return fmt.Errorf("only file:// artifacts URLs are supported") return fmt.Errorf("only file:// artifacts URLs are supported")
} }
dso, err := opsm.DeploySuperchainForge( var dump *foundry.ForgeAllocs
var dso opsm.DeploySuperchainOutput
err = CallScriptBroadcast(
ctx, ctx,
opsm.DeploySuperchainOpts{ CallScriptBroadcastOpts{
Input: opsm.DeploySuperchainInput{ L1ChainID: big.NewInt(int64(intent.L1ChainID)),
ProxyAdminOwner: intent.SuperchainRoles.ProxyAdminOwner, Logger: lgr,
ProtocolVersionsOwner: intent.SuperchainRoles.ProtocolVersionsOwner,
Guardian: intent.SuperchainRoles.Guardian,
Paused: false,
RequiredProtocolVersion: rollup.OPStackSupport,
RecommendedProtocolVersion: rollup.OPStackSupport,
},
ArtifactsFS: artifactsFS, ArtifactsFS: artifactsFS,
ChainID: big.NewInt(int64(intent.L1ChainID)),
Client: env.L1Client,
Signer: env.Signer,
Deployer: env.Deployer, Deployer: env.Deployer,
Logger: lgr, Signer: env.Signer,
Client: env.L1Client,
Handler: func(host *script.Host) error {
dso, err = opsm.DeploySuperchain(
host,
opsm.DeploySuperchainInput{
ProxyAdminOwner: intent.SuperchainRoles.ProxyAdminOwner,
ProtocolVersionsOwner: intent.SuperchainRoles.ProtocolVersionsOwner,
Guardian: intent.SuperchainRoles.Guardian,
Paused: false,
RequiredProtocolVersion: rollup.OPStackSupport,
RecommendedProtocolVersion: rollup.OPStackSupport,
},
)
if err != nil {
return fmt.Errorf("failed to deploy superchain: %w", err)
}
dump, err = host.StateDump()
if err != nil {
return fmt.Errorf("error dumping state: %w", err)
}
return nil
},
}, },
) )
if err != nil { if err != nil {
...@@ -62,8 +77,8 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s ...@@ -62,8 +77,8 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s
SuperchainConfigImplAddress: dso.SuperchainConfigImpl, SuperchainConfigImplAddress: dso.SuperchainConfigImpl,
ProtocolVersionsProxyAddress: dso.ProtocolVersionsProxy, ProtocolVersionsProxyAddress: dso.ProtocolVersionsProxy,
ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl, ProtocolVersionsImplAddress: dso.ProtocolVersionsImpl,
StateDump: dump,
} }
if err := env.WriteState(st); err != nil { if err := env.WriteState(st); err != nil {
return err return err
} }
...@@ -72,13 +87,5 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s ...@@ -72,13 +87,5 @@ func DeploySuperchain(ctx context.Context, env *Env, intent *state.Intent, st *s
} }
func shouldDeploySuperchain(intent *state.Intent, st *state.State) bool { func shouldDeploySuperchain(intent *state.Intent, st *state.State) bool {
if st.AppliedIntent == nil { return st.SuperchainDeployment == nil
return true
}
if st.SuperchainDeployment == nil {
return true
}
return false
} }
...@@ -24,7 +24,7 @@ type Intent struct { ...@@ -24,7 +24,7 @@ type Intent struct {
ContractArtifactsURL *ArtifactsURL `json:"contractArtifactsURL" toml:"contractArtifactsURL"` ContractArtifactsURL *ArtifactsURL `json:"contractArtifactsURL" toml:"contractArtifactsURL"`
Chains []Chain `json:"chains" toml:"chains"` Chains []ChainIntent `json:"chains" toml:"chains"`
} }
func (c Intent) L1ChainIDBig() *big.Int { func (c Intent) L1ChainIDBig() *big.Int {
...@@ -62,14 +62,14 @@ func (c Intent) Check() error { ...@@ -62,14 +62,14 @@ func (c Intent) Check() error {
return nil return nil
} }
func (c Intent) Chain(id uint64) (Chain, error) { func (c Intent) Chain(id common.Hash) (ChainIntent, error) {
for i := range c.Chains { for i := range c.Chains {
if c.Chains[i].ID == id { if c.Chains[i].ID == id {
return c.Chains[i], nil return c.Chains[i], nil
} }
} }
return Chain{}, fmt.Errorf("chain %d not found", id) return ChainIntent{}, fmt.Errorf("chain %d not found", id)
} }
func (c Intent) WriteToFile(path string) error { func (c Intent) WriteToFile(path string) error {
...@@ -84,32 +84,33 @@ type SuperchainRoles struct { ...@@ -84,32 +84,33 @@ type SuperchainRoles struct {
Guardian common.Address `json:"guardian" toml:"guardian"` Guardian common.Address `json:"guardian" toml:"guardian"`
} }
type Chain struct { type ChainIntent struct {
ID uint64 `json:"id"` ID common.Hash `json:"id" toml:"id"`
Roles ChainRoles `json:"roles"` Roles ChainRoles `json:"roles" toml:"roles"`
Overrides map[string]any `json:"overrides"` Overrides map[string]any `json:"overrides" toml:"overrides"`
} }
type ChainRoles struct { type ChainRoles struct {
ProxyAdminOwner common.Address `json:"proxyAdminOwner"` ProxyAdminOwner common.Address `json:"proxyAdminOwner" toml:"proxyAdminOwner"`
SystemConfigOwner common.Address `json:"systemConfigOwner"` SystemConfigOwner common.Address `json:"systemConfigOwner" toml:"systemConfigOwner"`
GovernanceTokenOwner common.Address `json:"governanceTokenOwner"` GovernanceTokenOwner common.Address `json:"governanceTokenOwner" toml:"governanceTokenOwner"`
UnsafeBlockSigner common.Address `json:"unsafeBlockSigner"` UnsafeBlockSigner common.Address `json:"unsafeBlockSigner" toml:"unsafeBlockSigner"`
Batcher common.Address `json:"batcher"` Batcher common.Address `json:"batcher" toml:"batcher"`
Proposer common.Address `json:"proposer"` Proposer common.Address `json:"proposer" toml:"proposer"`
Challenger common.Address `json:"challenger"` Challenger common.Address `json:"challenger" toml:"challenger"`
} }
func (c *Chain) Check() error { func (c *ChainIntent) Check() error {
if c.ID == 0 { var emptyHash common.Hash
if c.ID == emptyHash {
return fmt.Errorf("id must be set") return fmt.Errorf("id must be set")
} }
......
package state package state
import ( import (
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -12,6 +13,9 @@ type State struct { ...@@ -12,6 +13,9 @@ type State struct {
// Version versions the state so we can update it later. // Version versions the state so we can update it later.
Version int `json:"version"` Version int `json:"version"`
// Create2Salt is the salt used for CREATE2 deployments.
Create2Salt common.Hash `json:"create2Salt"`
// AppliedIntent contains the chain intent that was last // AppliedIntent contains the chain intent that was last
// successfully applied. It is diffed against new intent // successfully applied. It is diffed against new intent
// in order to determine what deployment steps to take. // in order to determine what deployment steps to take.
...@@ -22,6 +26,13 @@ type State struct { ...@@ -22,6 +26,13 @@ type State struct {
// deployment. It only contains the proxies because the implementations // deployment. It only contains the proxies because the implementations
// can be looked up on chain. // can be looked up on chain.
SuperchainDeployment *SuperchainDeployment `json:"superchainDeployment"` SuperchainDeployment *SuperchainDeployment `json:"superchainDeployment"`
// ImplementationsDeployment contains the addresses of the common implementation
// contracts required for the Superchain to function.
ImplementationsDeployment *ImplementationsDeployment `json:"implementationsDeployment"`
// Chains contains data about L2 chain deployments.
Chains []ChainState `json:"opChainDeployments"`
} }
func (s State) WriteToFile(path string) error { func (s State) WriteToFile(path string) error {
...@@ -29,9 +40,46 @@ func (s State) WriteToFile(path string) error { ...@@ -29,9 +40,46 @@ func (s State) WriteToFile(path string) error {
} }
type SuperchainDeployment struct { type SuperchainDeployment struct {
ProxyAdminAddress common.Address `json:"proxyAdminAddress"` ProxyAdminAddress common.Address `json:"proxyAdminAddress"`
SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"` SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"`
SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"` SuperchainConfigImplAddress common.Address `json:"superchainConfigImplAddress"`
ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"` ProtocolVersionsProxyAddress common.Address `json:"protocolVersionsProxyAddress"`
ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"` ProtocolVersionsImplAddress common.Address `json:"protocolVersionsImplAddress"`
StateDump *foundry.ForgeAllocs `json:"stateDump"`
}
type ImplementationsDeployment struct {
OpsmAddress common.Address `json:"opsmAddress"`
DelayedWETHImplAddress common.Address `json:"delayedWETHImplAddress"`
OptimismPortalImplAddress common.Address `json:"optimismPortalImplAddress"`
PreimageOracleSingletonAddress common.Address `json:"preimageOracleSingletonAddress"`
MipsSingletonAddress common.Address `json:"mipsSingletonAddress"`
SystemConfigImplAddress common.Address `json:"systemConfigImplAddress"`
L1CrossDomainMessengerImplAddress common.Address `json:"l1CrossDomainMessengerImplAddress"`
L1ERC721BridgeImplAddress common.Address `json:"l1ERC721BridgeImplAddress"`
L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"`
OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"`
DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"`
StateDump *foundry.ForgeAllocs `json:"stateDump"`
}
type ChainState struct {
ID common.Hash `json:"id"`
ProxyAdminAddress common.Address `json:"proxyAdminAddress"`
AddressManagerAddress common.Address `json:"addressManagerAddress"`
L1ERC721BridgeProxyAddress common.Address `json:"l1ERC721BridgeProxyAddress"`
SystemConfigProxyAddress common.Address `json:"systemConfigProxyAddress"`
OptimismMintableERC20FactoryProxyAddress common.Address `json:"optimismMintableERC20FactoryProxyAddress"`
L1StandardBridgeProxyAddress common.Address `json:"l1StandardBridgeProxyAddress"`
L1CrossDomainMessengerProxyAddress common.Address `json:"l1CrossDomainMessengerProxyAddress"`
OptimismPortalProxyAddress common.Address `json:"optimismPortalProxyAddress"`
DisputeGameFactoryProxyAddress common.Address `json:"disputeGameFactoryProxyAddress"`
DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"`
AnchorStateRegistryProxyAddress common.Address `json:"anchorStateRegistryProxyAddress"`
AnchorStateRegistryImplAddress common.Address `json:"anchorStateRegistryImplAddress"`
FaultDisputeGameAddress common.Address `json:"faultDisputeGameAddress"`
PermissionedDisputeGameAddress common.Address `json:"permissionedDisputeGameAddress"`
DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"`
DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"`
} }
...@@ -5,6 +5,8 @@ import ( ...@@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -147,7 +149,7 @@ func prepareInitialL1(l1Host *script.Host, cfg *L1Config) (*L1Deployment, error) ...@@ -147,7 +149,7 @@ func prepareInitialL1(l1Host *script.Host, cfg *L1Config) (*L1Deployment, error)
func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*SuperchainDeployment, error) { func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*SuperchainDeployment, error) {
l1Host.SetTxOrigin(superCfg.Deployer) l1Host.SetTxOrigin(superCfg.Deployer)
superDeployment, err := deployers.DeploySuperchain(l1Host, &deployers.DeploySuperchainInput{ superDeployment, err := opsm.DeploySuperchain(l1Host, opsm.DeploySuperchainInput{
ProxyAdminOwner: superCfg.ProxyAdminOwner, ProxyAdminOwner: superCfg.ProxyAdminOwner,
ProtocolVersionsOwner: superCfg.ProtocolVersionsOwner, ProtocolVersionsOwner: superCfg.ProtocolVersionsOwner,
Guardian: superCfg.SuperchainConfigGuardian, Guardian: superCfg.SuperchainConfigGuardian,
...@@ -159,7 +161,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup ...@@ -159,7 +161,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup
return nil, fmt.Errorf("failed to deploy Superchain contracts: %w", err) return nil, fmt.Errorf("failed to deploy Superchain contracts: %w", err)
} }
implementationsDeployment, err := deployers.DeployImplementations(l1Host, &deployers.DeployImplementationsInput{ implementationsDeployment, err := opsm.DeployImplementations(l1Host, opsm.DeployImplementationsInput{
WithdrawalDelaySeconds: superCfg.Implementations.FaultProof.WithdrawalDelaySeconds, WithdrawalDelaySeconds: superCfg.Implementations.FaultProof.WithdrawalDelaySeconds,
MinProposalSizeBytes: superCfg.Implementations.FaultProof.MinProposalSizeBytes, MinProposalSizeBytes: superCfg.Implementations.FaultProof.MinProposalSizeBytes,
ChallengePeriodSeconds: superCfg.Implementations.FaultProof.ChallengePeriodSeconds, ChallengePeriodSeconds: superCfg.Implementations.FaultProof.ChallengePeriodSeconds,
...@@ -178,7 +180,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup ...@@ -178,7 +180,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup
// Collect deployment addresses // Collect deployment addresses
// This could all be automatic once we have better output-contract typing/scripting // This could all be automatic once we have better output-contract typing/scripting
return &SuperchainDeployment{ return &SuperchainDeployment{
Implementations: Implementations(*implementationsDeployment), Implementations: Implementations(implementationsDeployment),
ProxyAdmin: superDeployment.SuperchainProxyAdmin, ProxyAdmin: superDeployment.SuperchainProxyAdmin,
ProtocolVersions: superDeployment.ProtocolVersionsImpl, ProtocolVersions: superDeployment.ProtocolVersionsImpl,
ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy, ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy,
...@@ -194,7 +196,7 @@ func deployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme ...@@ -194,7 +196,7 @@ func deployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme
l1Host.SetTxOrigin(cfg.Deployer) l1Host.SetTxOrigin(cfg.Deployer)
output, err := deployers.DeployOPChain(l1Host, &deployers.DeployOPChainInput{ output, err := opsm.DeployOPChain(l1Host, opsm.DeployOPChainInput{
OpChainProxyAdminOwner: cfg.ProxyAdminOwner, OpChainProxyAdminOwner: cfg.ProxyAdminOwner,
SystemConfigOwner: cfg.SystemConfigOwner, SystemConfigOwner: cfg.SystemConfigOwner,
Batcher: cfg.BatchSenderAddress, Batcher: cfg.BatchSenderAddress,
...@@ -212,7 +214,7 @@ func deployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme ...@@ -212,7 +214,7 @@ func deployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme
// Collect deployment addresses // Collect deployment addresses
return &L2Deployment{ return &L2Deployment{
L2OpchainDeployment: L2OpchainDeployment(*output), L2OpchainDeployment: L2OpchainDeployment(output),
}, nil }, nil
} }
......
package deployers
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type DeploySuperchainInput struct {
ProxyAdminOwner common.Address // TODO(#11783): also used as interop-dependency-set owner
ProtocolVersionsOwner common.Address
Guardian common.Address
Paused bool
RequiredProtocolVersion params.ProtocolVersion
RecommendedProtocolVersion params.ProtocolVersion
}
func (input *DeploySuperchainInput) InputSet() bool {
return true
}
type DeploySuperchainOutput struct {
SuperchainProxyAdmin common.Address
SuperchainConfigImpl common.Address
SuperchainConfigProxy common.Address
ProtocolVersionsImpl common.Address
ProtocolVersionsProxy common.Address
}
func (output *DeploySuperchainOutput) CheckOutput() error {
return nil
}
type DeploySuperchainScript struct {
Run func(input, output common.Address) error
}
func DeploySuperchain(l1Host *script.Host, input *DeploySuperchainInput) (*DeploySuperchainOutput, error) {
output := &DeploySuperchainOutput{}
inputAddr := l1Host.NewScriptAddress()
outputAddr := l1Host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeploySuperchainInput](l1Host, inputAddr, input)
if err != nil {
return nil, fmt.Errorf("failed to insert DeploySuperchainInput precompile: %w", err)
}
defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeploySuperchainOutput](l1Host, outputAddr, output,
script.WithFieldSetter[*DeploySuperchainOutput])
if err != nil {
return nil, fmt.Errorf("failed to insert DeploySuperchainOutput precompile: %w", err)
}
defer cleanupOutput()
deployScript, cleanupDeploy, err := script.WithScript[DeploySuperchainScript](l1Host, "DeploySuperchain.s.sol", "DeploySuperchain")
if err != nil {
return nil, fmt.Errorf("failed to load DeploySuperchain script: %w", err)
}
defer cleanupDeploy()
if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return nil, fmt.Errorf("failed to run DeploySuperchain script: %w", err)
}
return output, nil
}
...@@ -386,6 +386,20 @@ func (h *Host) GetNonce(addr common.Address) uint64 { ...@@ -386,6 +386,20 @@ func (h *Host) GetNonce(addr common.Address) uint64 {
return h.state.GetNonce(addr) return h.state.GetNonce(addr)
} }
// ImportState imports a set of foundry.ForgeAllocs into the
// host's state database. It does not erase existing state
// when importing.
func (h *Host) ImportState(allocs *foundry.ForgeAllocs) {
for addr, alloc := range allocs.Accounts {
h.state.SetBalance(addr, uint256.MustFromBig(alloc.Balance), tracing.BalanceChangeUnspecified)
h.state.SetNonce(addr, alloc.Nonce)
h.state.SetCode(addr, alloc.Code)
for key, value := range alloc.Storage {
h.state.SetState(addr, key, value)
}
}
}
// getPrecompile overrides any accounts during runtime, to insert special precompiles, if activated. // getPrecompile overrides any accounts during runtime, to insert special precompiles, if activated.
func (h *Host) getPrecompile(rules params.Rules, original vm.PrecompiledContract, addr common.Address) vm.PrecompiledContract { func (h *Host) getPrecompile(rules params.Rules, original vm.PrecompiledContract, addr common.Address) vm.PrecompiledContract {
if p, ok := h.precompiles[addr]; ok { if p, ok := h.precompiles[addr]; ok {
...@@ -436,8 +450,8 @@ func (h *Host) onEnter(depth int, typ byte, from common.Address, to common.Addre ...@@ -436,8 +450,8 @@ func (h *Host) onEnter(depth int, typ byte, from common.Address, to common.Addre
return return
} }
// Bump nonce value, such that a broadcast Call appears like a tx // Bump nonce value, such that a broadcast Call or CREATE2 appears like a tx
if parentCallFrame.LastOp == vm.CALL { if parentCallFrame.LastOp == vm.CALL || parentCallFrame.LastOp == vm.CREATE2 {
sender := parentCallFrame.Ctx.Address() sender := parentCallFrame.Ctx.Address()
if parentCallFrame.Prank.Sender != nil { if parentCallFrame.Prank.Sender != nil {
sender = *parentCallFrame.Prank.Sender sender = *parentCallFrame.Prank.Sender
......
...@@ -164,7 +164,7 @@ func TestScriptBroadcast(t *testing.T) { ...@@ -164,7 +164,7 @@ func TestScriptBroadcast(t *testing.T) {
require.EqualValues(t, 0, h.GetNonce(senderAddr)) require.EqualValues(t, 0, h.GetNonce(senderAddr))
require.EqualValues(t, 3, h.GetNonce(scriptAddr)) require.EqualValues(t, 3, h.GetNonce(scriptAddr))
require.EqualValues(t, 2, h.GetNonce(coffeeAddr)) require.EqualValues(t, 2, h.GetNonce(coffeeAddr))
// This is zero because the deterministic deployer is the // This is one because we still need to bump the nonce of the
// address that actually deploys the contract using CREATE2. // address that will perform the send to the Create2Deployer.
require.EqualValues(t, 0, h.GetNonce(cafeAddr)) require.EqualValues(t, 1, h.GetNonce(cafeAddr))
} }
...@@ -33,6 +33,7 @@ import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol"; ...@@ -33,6 +33,7 @@ import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol";
import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol"; import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol";
import { Blueprint } from "src/libraries/Blueprint.sol"; import { Blueprint } from "src/libraries/Blueprint.sol";
import { Config } from "scripts/libraries/Config.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { Solarray } from "scripts/libraries/Solarray.sol"; import { Solarray } from "scripts/libraries/Solarray.sol";
...@@ -338,7 +339,7 @@ contract DeployImplementations is Script { ...@@ -338,7 +339,7 @@ contract DeployImplementations is Script {
// First we deploy the blueprints for the singletons deployed by OPSM. // First we deploy the blueprints for the singletons deployed by OPSM.
// forgefmt: disable-start // forgefmt: disable-start
bytes32 salt = bytes32(0); bytes32 salt = keccak256(bytes(Config.implSalt()));
OPStackManager.Blueprints memory blueprints; OPStackManager.Blueprints memory blueprints;
vm.startBroadcast(msg.sender); vm.startBroadcast(msg.sender);
......
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