Commit c05aca35 authored by Blaine Malone's avatar Blaine Malone Committed by GitHub

opcm: exposing preimage oracle and mips singletons via DeployOPChain.s.sol (#12521)

* opcm: exposing preimage oracle and mips singletons via DeployOPChain.s.sol

* fix: new approach using opcm to grab mips singleton.

* fix: small cleanup to contracts.go.

* fix: more error checking

* fix: semgrep fix.

* fix: small rename
parent 7bd0370c
...@@ -32,8 +32,8 @@ func (c *Contract) getAddress(ctx context.Context, name string) (common.Address, ...@@ -32,8 +32,8 @@ func (c *Contract) getAddress(ctx context.Context, name string) (common.Address,
return c.callContractMethod(ctx, name, abi.Arguments{}) return c.callContractMethod(ctx, name, abi.Arguments{})
} }
// Used to call getAddress(string) on legacy ResolvedDelegateProxy contract // Used to call getAddress(string) on legacy AddressManager contract
func (c *Contract) GetAddressByName(ctx context.Context, name string) (common.Address, error) { func (c *Contract) GetAddressByNameViaAddressManager(ctx context.Context, name string) (common.Address, error) {
inputs := abi.Arguments{ inputs := abi.Arguments{
abi.Argument{ abi.Argument{
Name: "_name", Name: "_name",
...@@ -44,6 +44,61 @@ func (c *Contract) GetAddressByName(ctx context.Context, name string) (common.Ad ...@@ -44,6 +44,61 @@ func (c *Contract) GetAddressByName(ctx context.Context, name string) (common.Ad
return c.callContractMethod(ctx, "getAddress", inputs, name) return c.callContractMethod(ctx, "getAddress", inputs, name)
} }
func (c *Contract) GenericAddressGetter(ctx context.Context, functionName string) (common.Address, error) {
return c.callContractMethod(ctx, functionName, abi.Arguments{})
}
// GetImplementation retrieves the Implementation struct for a given release and contract name.
func (c *Contract) GetOPCMImplementationAddress(ctx context.Context, release, contractName string) (common.Address, error) {
methodName := "implementations"
method := abi.NewMethod(
methodName,
methodName,
abi.Function,
"view",
true,
false,
abi.Arguments{
{Name: "release", Type: mustType("string")},
{Name: "contractName", Type: mustType("string")},
},
abi.Arguments{
{Name: "logic", Type: mustType("address")},
{Name: "initializer", Type: mustType("bytes4")},
},
)
calldata, err := method.Inputs.Pack(release, contractName)
if err != nil {
return common.Address{}, fmt.Errorf("failed to pack inputs: %w", err)
}
msg := ethereum.CallMsg{
To: &c.addr,
Data: append(bytes.Clone(method.ID), calldata...),
}
result, err := c.client.CallContract(ctx, msg, nil)
if err != nil {
return common.Address{}, fmt.Errorf("failed to call contract: %w", err)
}
out, err := method.Outputs.Unpack(result)
if err != nil {
return common.Address{}, fmt.Errorf("failed to unpack result: %w", err)
}
if len(out) != 2 {
return common.Address{}, fmt.Errorf("unexpected output length: %d", len(out))
}
logic, ok := out[0].(common.Address)
if !ok {
return common.Address{}, fmt.Errorf("unexpected type for logic: %T", out[0])
}
return logic, nil
}
func (c *Contract) callContractMethod(ctx context.Context, methodName string, inputs abi.Arguments, args ...interface{}) (common.Address, error) { func (c *Contract) callContractMethod(ctx context.Context, methodName string, inputs abi.Arguments, args ...interface{}) (common.Address, error) {
method := abi.NewMethod( method := abi.NewMethod(
methodName, methodName,
......
...@@ -3,6 +3,7 @@ package pipeline ...@@ -3,6 +3,7 @@ package pipeline
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"math/big" "math/big"
...@@ -18,7 +19,7 @@ import ( ...@@ -18,7 +19,7 @@ import (
func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state2.Intent, st *state2.State, chainID common.Hash) error { func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent *state2.Intent, st *state2.State, chainID common.Hash) error {
lgr := env.Logger.New("stage", "deploy-opchain") lgr := env.Logger.New("stage", "deploy-opchain")
if !shouldDeployOPChain(intent, st, chainID) { if !shouldDeployOPChain(st, chainID) {
lgr.Info("opchain deployment not needed") lgr.Info("opchain deployment not needed")
return nil return nil
} }
...@@ -30,6 +31,8 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -30,6 +31,8 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
return fmt.Errorf("failed to get chain intent: %w", err) return fmt.Errorf("failed to get chain intent: %w", err)
} }
opcmProxyAddress := st.ImplementationsDeployment.OpcmProxyAddress
input := opcm.DeployOPChainInput{ input := opcm.DeployOPChainInput{
OpChainProxyAdminOwner: thisIntent.Roles.ProxyAdminOwner, OpChainProxyAdminOwner: thisIntent.Roles.ProxyAdminOwner,
SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, SystemConfigOwner: thisIntent.Roles.SystemConfigOwner,
...@@ -40,7 +43,7 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -40,7 +43,7 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
BasefeeScalar: 1368, BasefeeScalar: 1368,
BlobBaseFeeScalar: 801949, BlobBaseFeeScalar: 801949,
L2ChainId: chainID.Big(), L2ChainId: chainID.Big(),
OpcmProxy: st.ImplementationsDeployment.OpcmProxyAddress, OpcmProxy: opcmProxyAddress,
SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization
GasLimit: 60_000_000, GasLimit: 60_000_000,
DisputeGameType: 1, // PERMISSIONED_CANNON Game Type DisputeGameType: 1, // PERMISSIONED_CANNON Game Type
...@@ -52,7 +55,7 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -52,7 +55,7 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
} }
var dco opcm.DeployOPChainOutput var dco opcm.DeployOPChainOutput
lgr.Info("deploying using existing OPCM", "address", st.ImplementationsDeployment.OpcmProxyAddress.Hex()) lgr.Info("deploying using existing OPCM", "address", opcmProxyAddress.Hex())
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr, Logger: lgr,
ChainID: big.NewInt(int64(intent.L1ChainID)), ChainID: big.NewInt(int64(intent.L1ChainID)),
...@@ -94,7 +97,22 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -94,7 +97,22 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy, DelayedWETHPermissionlessGameProxyAddress: dco.DelayedWETHPermissionlessGameProxy,
}) })
block, err := env.L1Client.BlockByNumber(ctx, nil) err = conditionallySetImplementationAddresses(ctx, env.L1Client, intent, st, dco, opcmProxyAddress)
if err != nil {
return fmt.Errorf("failed to set implementation addresses: %w", err)
}
return nil
}
// Only try to set the implementation addresses if we reused existing implementations from a release tag.
// The reason why these addresses could be empty is because only DeployOPChain.s.sol is invoked as part of the pipeline.
func conditionallySetImplementationAddresses(ctx context.Context, client *ethclient.Client, intent *state2.Intent, st *state.State, dco opcm.DeployOPChainOutput, opcmProxyAddress common.Address) error {
if !intent.L1ContractsLocator.IsTag() {
return nil
}
block, err := client.BlockByNumber(ctx, nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to get latest block by number: %w", err) return fmt.Errorf("failed to get latest block by number: %w", err)
} }
...@@ -102,36 +120,37 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -102,36 +120,37 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
errCh := make(chan error, 8) errCh := make(chan error, 8)
// If any of the implementations addresses (excluding OpcmProxy) are empty,
// we need to set them using the implementation address read from their corresponding proxy.
// The reason these might be empty is because we're only invoking DeployOPChain.s.sol as part of the pipeline.
// TODO: Need to initialize 'mipsSingletonAddress' and 'preimageOracleSingletonAddress'
setImplementationAddressTasks := []func(){ setImplementationAddressTasks := []func(){
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.DelayedWETHPermissionedGameProxy, currentBlockHash, &st.ImplementationsDeployment.DelayedWETHImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.DelayedWETHPermissionedGameProxy, currentBlockHash, &st.ImplementationsDeployment.DelayedWETHImplAddress)
}, },
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.OptimismPortalProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismPortalImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.OptimismPortalProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismPortalImplAddress)
}, },
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.SystemConfigProxy, currentBlockHash, &st.ImplementationsDeployment.SystemConfigImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.SystemConfigProxy, currentBlockHash, &st.ImplementationsDeployment.SystemConfigImplAddress)
}, },
func() { func() {
setRDPImplementationAddress(ctx, env.L1Client, errCh, dco.AddressManager, &st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress) setRDPImplementationAddress(ctx, client, errCh, dco.AddressManager, &st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress, "OVM_L1CrossDomainMessenger")
}, },
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.L1ERC721BridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1ERC721BridgeImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.L1ERC721BridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1ERC721BridgeImplAddress)
}, },
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.L1StandardBridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1StandardBridgeImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.L1StandardBridgeProxy, currentBlockHash, &st.ImplementationsDeployment.L1StandardBridgeImplAddress)
}, },
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.OptimismMintableERC20FactoryProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.OptimismMintableERC20FactoryProxy, currentBlockHash, &st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress)
}, },
func() { func() {
setEIP1967ImplementationAddress(ctx, env.L1Client, errCh, dco.DisputeGameFactoryProxy, currentBlockHash, &st.ImplementationsDeployment.DisputeGameFactoryImplAddress) setEIP1967ImplementationAddress(ctx, client, errCh, dco.DisputeGameFactoryProxy, currentBlockHash, &st.ImplementationsDeployment.DisputeGameFactoryImplAddress)
},
func() {
setMipsSingletonAddress(ctx, client, intent.L1ContractsLocator, errCh, opcmProxyAddress, &st.ImplementationsDeployment.MipsSingletonAddress)
setPreimageOracleAddress(ctx, client, errCh, st.ImplementationsDeployment.MipsSingletonAddress, &st.ImplementationsDeployment.PreimageOracleSingletonAddress)
}, },
} }
for _, task := range setImplementationAddressTasks { for _, task := range setImplementationAddressTasks {
go task() go task()
} }
...@@ -147,17 +166,42 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent ...@@ -147,17 +166,42 @@ func DeployOPChain(ctx context.Context, env *Env, bundle ArtifactsBundle, intent
return fmt.Errorf("failed to set implementation addresses: %w", lastTaskErr) return fmt.Errorf("failed to set implementation addresses: %w", lastTaskErr)
} }
fmt.Printf("st.ImplementationsDeployment: %+v\n", st.ImplementationsDeployment)
return nil return nil
} }
func setRDPImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, addressManager common.Address, implAddress *common.Address) { func setMipsSingletonAddress(ctx context.Context, client *ethclient.Client, l1ArtifactsLocator *opcm.ArtifactsLocator, errCh chan error, opcmProxyAddress common.Address, singletonAddress *common.Address) {
if !l1ArtifactsLocator.IsTag() {
errCh <- errors.New("L1 contracts locator is not a tag, cannot set MIPS singleton address")
return
}
opcmContract := opcm.NewContract(opcmProxyAddress, client)
mipsSingletonAddress, err := opcmContract.GetOPCMImplementationAddress(ctx, l1ArtifactsLocator.Tag, "MIPS")
if err == nil {
*singletonAddress = mipsSingletonAddress
}
errCh <- err
}
func setPreimageOracleAddress(ctx context.Context, client *ethclient.Client, errCh chan error, mipsSingletonAddress common.Address, preimageOracleAddress *common.Address) {
opcmContract := opcm.NewContract(mipsSingletonAddress, client)
preimageOracle, err := opcmContract.GenericAddressGetter(ctx, "oracle")
if err == nil {
*preimageOracleAddress = preimageOracle
}
errCh <- err
}
func setRDPImplementationAddress(ctx context.Context, client *ethclient.Client, errCh chan error, addressManager common.Address, implAddress *common.Address, getNameArg string) {
if *implAddress != (common.Address{}) { if *implAddress != (common.Address{}) {
errCh <- nil errCh <- nil
return return
} }
contract := opcm.NewContract(addressManager, client) addressManagerContract := opcm.NewContract(addressManager, client)
address, err := contract.GetAddressByName(ctx, "OVM_L1CrossDomainMessenger") address, err := addressManagerContract.GetAddressByNameViaAddressManager(ctx, getNameArg)
if err == nil { if err == nil {
*implAddress = address *implAddress = address
} }
...@@ -177,7 +221,7 @@ func setEIP1967ImplementationAddress(ctx context.Context, client *ethclient.Clie ...@@ -177,7 +221,7 @@ func setEIP1967ImplementationAddress(ctx context.Context, client *ethclient.Clie
errCh <- err errCh <- err
} }
func shouldDeployOPChain(intent *state.Intent, st *state.State, chainID common.Hash) bool { func shouldDeployOPChain(st *state.State, chainID common.Hash) bool {
for _, chain := range st.Chains { for _, chain := range st.Chains {
if chain.ID == chainID { if chain.ID == chainID {
return false return false
......
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