Commit 11843b48 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-deployer: Add deploy mips script (#12835)

* op-deployer: Add deploy mips script

* op-deployer: Explicitly set the PreimageOracle to use with deployed MIPS

* op-deployer: Validate mips version in go code.
parent dd10e2e2
......@@ -38,7 +38,7 @@ type DisputeGameConfig struct {
MinProposalSizeBytes uint64
ChallengePeriodSeconds uint64
MipsVersion uint8
MipsVersion uint64
GameKind string
GameType uint32
AbsolutePrestate common.Hash
......
......@@ -28,6 +28,7 @@ const (
L2ChainIdFlagName = "l2-chain-id"
ProposerFlagName = "proposer"
ChallengerFlagName = "challenger"
PreimageOracleFlagName = "preimage-oracle"
)
var (
......@@ -140,6 +141,12 @@ var (
EnvVars: deployer.PrefixEnvVar("CHALLENGER"),
Value: common.Address{}.Hex(),
}
PreimageOracleFlag = &cli.StringFlag{
Name: PreimageOracleFlagName,
Usage: "Preimage oracle address.",
EnvVars: deployer.PrefixEnvVar("PREIMAGE_ORACLE"),
Value: common.Address{}.Hex(),
}
)
var OPCMFlags = []cli.Flag{
......@@ -181,6 +188,14 @@ var DisputeGameFlags = []cli.Flag{
ChallengerFlag,
}
var MIPSFlags = []cli.Flag{
deployer.L1RPCURLFlag,
deployer.PrivateKeyFlag,
ArtifactsLocatorFlag,
PreimageOracleFlag,
MIPSVersionFlag,
}
var Commands = []*cli.Command{
{
Name: "opcm",
......@@ -200,4 +215,10 @@ var Commands = []*cli.Command{
Flags: cliapp.ProtectFlags(DisputeGameFlags),
Action: DisputeGameCLI,
},
{
Name: "mips",
Usage: "Bootstrap an instance of MIPS.",
Flags: cliapp.ProtectFlags(MIPSFlags),
Action: MIPSCLI,
},
}
package bootstrap
import (
"context"
"crypto/ecdsa"
"fmt"
"strings"
artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
)
type MIPSConfig struct {
L1RPCUrl string
PrivateKey string
Logger log.Logger
ArtifactsLocator *artifacts2.Locator
privateKeyECDSA *ecdsa.PrivateKey
PreimageOracle common.Address
MipsVersion uint64
}
func (c *MIPSConfig) Check() error {
if c.L1RPCUrl == "" {
return fmt.Errorf("l1RPCUrl must be specified")
}
if c.PrivateKey == "" {
return fmt.Errorf("private key must be specified")
}
privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x"))
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
c.privateKeyECDSA = privECDSA
if c.Logger == nil {
return fmt.Errorf("logger must be specified")
}
if c.ArtifactsLocator == nil {
return fmt.Errorf("artifacts locator must be specified")
}
if c.PreimageOracle == (common.Address{}) {
return fmt.Errorf("preimage oracle must be specified")
}
if c.MipsVersion == 0 {
return fmt.Errorf("mips version must be specified")
}
if c.MipsVersion != 1 && c.MipsVersion != 2 {
return fmt.Errorf("mips version must be either 1 or 2")
}
return nil
}
func MIPSCLI(cliCtx *cli.Context) error {
logCfg := oplog.ReadCLIConfig(cliCtx)
l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg)
oplog.SetGlobalLogHandler(l.Handler())
l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName)
privateKey := cliCtx.String(deployer.PrivateKeyFlagName)
artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName)
artifactsLocator := new(artifacts2.Locator)
if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil {
return fmt.Errorf("failed to parse artifacts URL: %w", err)
}
mipsVersion := cliCtx.Uint64(MIPSVersionFlagName)
preimageOracle := common.HexToAddress(cliCtx.String(PreimageOracleFlagName))
ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context)
return MIPS(ctx, MIPSConfig{
L1RPCUrl: l1RPCUrl,
PrivateKey: privateKey,
Logger: l,
ArtifactsLocator: artifactsLocator,
MipsVersion: mipsVersion,
PreimageOracle: preimageOracle,
})
}
func MIPS(ctx context.Context, cfg MIPSConfig) error {
if err := cfg.Check(); err != nil {
return fmt.Errorf("invalid config for MIPS: %w", err)
}
lgr := cfg.Logger
progressor := func(curr, total int64) {
lgr.Info("artifacts download progress", "current", curr, "total", total)
}
artifactsFS, cleanup, err := artifacts2.Download(ctx, cfg.ArtifactsLocator, progressor)
if err != nil {
return fmt.Errorf("failed to download artifacts: %w", err)
}
defer func() {
if err := cleanup(); err != nil {
lgr.Warn("failed to clean up artifacts", "err", err)
}
}()
l1Client, err := ethclient.Dial(cfg.L1RPCUrl)
if err != nil {
return fmt.Errorf("failed to connect to L1 RPC: %w", err)
}
chainID, err := l1Client.ChainID(ctx)
if err != nil {
return fmt.Errorf("failed to get chain ID: %w", err)
}
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID))
chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey)
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: chainID,
Client: l1Client,
Signer: signer,
From: chainDeployer,
})
if err != nil {
return fmt.Errorf("failed to create broadcaster: %w", err)
}
nonce, err := l1Client.NonceAt(ctx, chainDeployer, nil)
if err != nil {
return fmt.Errorf("failed to get starting nonce: %w", err)
}
host, err := env.DefaultScriptHost(
bcaster,
lgr,
chainDeployer,
artifactsFS,
nonce,
)
if err != nil {
return fmt.Errorf("failed to create script host: %w", err)
}
var release string
if cfg.ArtifactsLocator.IsTag() {
release = cfg.ArtifactsLocator.Tag
} else {
release = "dev"
}
lgr.Info("deploying dispute game", "release", release)
dgo, err := opcm.DeployMIPS(
host,
opcm.DeployMIPSInput{
MipsVersion: cfg.MipsVersion,
PreimageOracle: cfg.PreimageOracle,
},
)
if err != nil {
return fmt.Errorf("error deploying dispute game: %w", err)
}
if _, err := bcaster.Broadcast(ctx); err != nil {
return fmt.Errorf("failed to broadcast: %w", err)
}
lgr.Info("deployed dispute game")
if err := jsonutil.WriteJSON(dgo, ioutil.ToStdOut()); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
return nil
}
......@@ -11,7 +11,7 @@ import (
type DeployDisputeGameInput struct {
Release string
StandardVersionsToml string
MipsVersion uint8
MipsVersion uint64
MinProposalSizeBytes uint64
ChallengePeriodSeconds uint64
GameKind string
......
package opcm
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
)
type DeployMIPSInput struct {
MipsVersion uint64
PreimageOracle common.Address
}
func (input *DeployMIPSInput) InputSet() bool {
return true
}
type DeployMIPSOutput struct {
MipsSingleton common.Address
}
func (output *DeployMIPSOutput) CheckOutput(input common.Address) error {
return nil
}
type DeployMIPSScript struct {
Run func(input, output common.Address) error
}
func DeployMIPS(
host *script.Host,
input DeployMIPSInput,
) (DeployMIPSOutput, error) {
var output DeployMIPSOutput
inputAddr := host.NewScriptAddress()
outputAddr := host.NewScriptAddress()
cleanupInput, err := script.WithPrecompileAtAddress[*DeployMIPSInput](host, inputAddr, &input)
if err != nil {
return output, fmt.Errorf("failed to insert DeployMIPSInput precompile: %w", err)
}
defer cleanupInput()
cleanupOutput, err := script.WithPrecompileAtAddress[*DeployMIPSOutput](host, outputAddr, &output,
script.WithFieldSetter[*DeployMIPSOutput])
if err != nil {
return output, fmt.Errorf("failed to insert DeployMIPSOutput precompile: %w", err)
}
defer cleanupOutput()
implContract := "DeployMIPS"
deployScript, cleanupDeploy, err := script.WithScript[DeployMIPSScript](host, "DeployMIPS.s.sol", implContract)
if err != nil {
return output, fmt.Errorf("failed to load %s script: %w", implContract, err)
}
defer cleanupDeploy()
if err := deployScript.Run(inputAddr, outputAddr); err != nil {
return output, fmt.Errorf("failed to run %s script: %w", implContract, err)
}
return output, nil
}
package opcm
import (
"testing"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
func TestDeployMIPS(t *testing.T) {
_, artifacts := testutil.LocalArtifacts(t)
host, err := env.DefaultScriptHost(
broadcaster.NoopBroadcaster(),
testlog.Logger(t, log.LevelInfo),
common.Address{'D'},
artifacts,
0,
)
require.NoError(t, err)
input := DeployMIPSInput{
MipsVersion: 1,
PreimageOracle: common.Address{0xab},
}
output, err := DeployMIPS(host, input)
require.NoError(t, err)
require.NotEmpty(t, output.MipsSingleton)
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Forge
import { Script } from "forge-std/Script.sol";
// Scripts
import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
// Interfaces
import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol";
import { IMIPS } from "src/cannon/interfaces/IMIPS.sol";
/// @title DeployMIPSInput
contract DeployMIPSInput is BaseDeployIO {
// Specify the PreimageOracle to use
address internal _preimageOracle;
// Specify which MIPS version to use.
uint256 internal _mipsVersion;
function set(bytes4 _sel, uint256 _value) public {
if (_sel == this.mipsVersion.selector) {
require(_value == 1 || _value == 2, "DeployMIPS: unknown mips version");
_mipsVersion = _value;
} else {
revert("DeployMIPS: unknown selector");
}
}
function set(bytes4 _sel, address _value) public {
if (_sel == this.preimageOracle.selector) {
require(_value != address(0), "DeployMIPS: preimageOracle cannot be empty");
_preimageOracle = _value;
} else {
revert("DeployMIPS: unknown selector");
}
}
function mipsVersion() public view returns (uint256) {
require(_mipsVersion != 0, "DeployMIPS: mipsVersion not set");
require(_mipsVersion == 1 || _mipsVersion == 2, "DeployMIPS: unknown mips version");
return _mipsVersion;
}
function preimageOracle() public view returns (address) {
require(_preimageOracle != address(0), "DeployMIPS: preimageOracle not set");
return _preimageOracle;
}
}
/// @title DeployMIPSOutput
contract DeployMIPSOutput is BaseDeployIO {
IMIPS internal _mipsSingleton;
function set(bytes4 _sel, address _value) public {
if (_sel == this.mipsSingleton.selector) {
require(_value != address(0), "DeployMIPS: mipsSingleton cannot be zero address");
_mipsSingleton = IMIPS(_value);
} else {
revert("DeployMIPS: unknown selector");
}
}
function checkOutput(DeployMIPSInput _mi) public view {
DeployUtils.assertValidContractAddress(address(_mipsSingleton));
assertValidDeploy(_mi);
}
function mipsSingleton() public view returns (IMIPS) {
DeployUtils.assertValidContractAddress(address(_mipsSingleton));
return _mipsSingleton;
}
function assertValidDeploy(DeployMIPSInput _mi) public view {
assertValidMipsSingleton(_mi);
}
function assertValidMipsSingleton(DeployMIPSInput _mi) internal view {
IMIPS mips = mipsSingleton();
require(address(mips.oracle()) == address(_mi.preimageOracle()), "MIPS-10");
}
}
/// @title DeployMIPS
contract DeployMIPS is Script {
function run(DeployMIPSInput _mi, DeployMIPSOutput _mo) public {
deployMipsSingleton(_mi, _mo);
_mo.checkOutput(_mi);
}
function deployMipsSingleton(DeployMIPSInput _mi, DeployMIPSOutput _mo) internal {
IMIPS singleton;
uint256 mipsVersion = _mi.mipsVersion();
IPreimageOracle preimageOracle = IPreimageOracle(_mi.preimageOracle());
vm.broadcast(msg.sender);
singleton = IMIPS(
DeployUtils.create1({
_name: mipsVersion == 1 ? "MIPS" : "MIPS2",
_args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle)))
})
);
vm.label(address(singleton), "MIPSSingleton");
_mo.set(_mo.mipsSingleton.selector, address(singleton));
}
}
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