Commit bb5ab2e6 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-deployer: Add support for inspecting l2 semvers (#12577)

* op-deployer: Add support for inspecting l2 semvers

* goimports

* add factory targets
parent 85248727
......@@ -440,6 +440,11 @@ func (h *Host) HasPrecompileOverride(addr common.Address) bool {
return ok
}
// GetCode returns the code of an account from the state.
func (h *Host) GetCode(addr common.Address) []byte {
return h.state.GetCode(addr)
}
// onEnter is a trace-hook, which we use to apply changes to the state-DB, to simulate isolated broadcast calls,
// for better gas estimation of the exact broadcast call execution.
func (h *Host) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
......
......@@ -61,6 +61,14 @@ var Commands = []*cli.Command{
Action: DeployConfigCLI,
Flags: Flags,
},
{
Name: "l2-semvers",
Usage: "outputs the semvers for all L2 chains",
Args: true,
ArgsUsage: "<l2-chain-id>",
Action: L2SemversCLI,
Flags: Flags,
},
}
type cliConfig struct {
......
package inspect
import (
"bytes"
"context"
"fmt"
"math/big"
"regexp"
"time"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline"
"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-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/urfave/cli/v2"
)
var versionSelector = []byte{0x54, 0xfd, 0x4d, 0x50}
func L2SemversCLI(cliCtx *cli.Context) error {
cliCfg, err := readConfig(cliCtx)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(cliCtx.Context, time.Minute)
defer cancel()
logCfg := oplog.ReadCLIConfig(cliCtx)
l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg)
oplog.SetGlobalLogHandler(l.Handler())
globalState, err := pipeline.ReadState(cliCfg.Workdir)
if err != nil {
return fmt.Errorf("failed to read intent: %w", err)
}
chainState, err := globalState.Chain(cliCfg.ChainID)
if err != nil {
return fmt.Errorf("failed to find chain state: %w", err)
}
intent := globalState.AppliedIntent
if intent == nil {
return fmt.Errorf("can only run this command following a full apply")
}
if chainState.Allocs == nil {
return fmt.Errorf("chain state does not have allocs")
}
artifactsFS, cleanup, err := pipeline.DownloadArtifacts(ctx, intent.L2ContractsLocator, pipeline.LogProgressor(l))
if err != nil {
return fmt.Errorf("failed to download L2 artifacts: %w", err)
}
defer func() {
if err := cleanup(); err != nil {
l.Warn("failed to clean up L2 artifacts", "err", err)
}
}()
host, err := pipeline.DefaultScriptHost(
broadcaster.NoopBroadcaster(),
l,
common.Address{19: 0x01},
artifactsFS,
0,
)
if err != nil {
return fmt.Errorf("failed to create script host: %w", err)
}
host.ImportState(chainState.Allocs.Data)
addr := common.Address{19: 0x01}
type contractToCheck struct {
Address common.Address
Name string
}
contractsOutput := make(map[string]string)
// The gov token and the proxy admin do not have semvers.
contracts := []contractToCheck{
{predeploys.L2ToL1MessagePasserAddr, "L2ToL1MessagePasser"},
{predeploys.DeployerWhitelistAddr, "DeployerWhitelist"},
{predeploys.WETHAddr, "WETH"},
{predeploys.L2CrossDomainMessengerAddr, "L2CrossDomainMessenger"},
{predeploys.L2StandardBridgeAddr, "L2StandardBridge"},
{predeploys.SequencerFeeVaultAddr, "SequencerFeeVault"},
{predeploys.OptimismMintableERC20FactoryAddr, "OptimismMintableERC20Factory"},
{predeploys.L1BlockNumberAddr, "L1BlockNumber"},
{predeploys.GasPriceOracleAddr, "GasPriceOracle"},
{predeploys.L1BlockAddr, "L1Block"},
{predeploys.LegacyMessagePasserAddr, "LegacyMessagePasser"},
{predeploys.L2ERC721BridgeAddr, "L2ERC721Bridge"},
{predeploys.OptimismMintableERC721FactoryAddr, "OptimismMintableERC721Factory"},
{predeploys.BaseFeeVaultAddr, "BaseFeeVault"},
{predeploys.L1FeeVaultAddr, "L1FeeVault"},
{predeploys.SchemaRegistryAddr, "SchemaRegistry"},
{predeploys.EASAddr, "EAS"},
{predeploys.WETHAddr, "WETH"},
}
for _, contract := range contracts {
data, _, err := host.Call(
addr,
contract.Address,
bytes.Clone(versionSelector),
1_000_000_000,
uint256.NewInt(0),
)
if err != nil {
return fmt.Errorf("failed to call version on %s: %w", contract.Name, err)
}
// The second 32 bytes contain the length of the string
length := new(big.Int).SetBytes(data[32:64]).Int64()
// Start of the string data (after offset and length)
stringStart := 64
stringEnd := int64(stringStart) + length
// Bounds check
if stringEnd > int64(len(data)) {
return fmt.Errorf("string data out of bounds")
}
contractsOutput[contract.Name] = string(data[stringStart:stringEnd])
}
erc20Semver, err := findSemverBytecode(host, predeploys.OptimismMintableERC20FactoryAddr)
if err == nil {
contractsOutput["OptimismMintableERC20"] = erc20Semver
} else {
l.Warn("failed to find semver for OptimismMintableERC20", "err", err)
}
erc721Semver, err := findSemverBytecode(host, predeploys.OptimismMintableERC721FactoryAddr)
if err == nil {
contractsOutput["OptimismMintableERC721"] = erc721Semver
} else {
l.Warn("failed to find semver for OptimismMintableERC721", "err", err)
}
if err := jsonutil.WriteJSON(contractsOutput, ioutil.ToStdOutOrFileOrNoop(cliCfg.Outfile, 0o666)); err != nil {
return fmt.Errorf("failed to write rollup config: %w", err)
}
return nil
}
const patternLen = 24
var semverRegexp = regexp.MustCompile(`^(\d+\.\d+\.\d+([\w.+\-]*))\x00`)
var codeAddr = common.HexToAddress("0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30000")
func findSemverBytecode(host *script.Host, proxyAddr common.Address) (string, error) {
var implAddr common.Address
copy(implAddr[:], codeAddr[:])
copy(implAddr[18:], proxyAddr[18:])
bytecode := host.GetCode(implAddr)
if len(bytecode) == 0 {
return "", fmt.Errorf("failed to get bytecode for factory")
}
versionSelectorIndex := bytes.LastIndex(bytecode, versionSelector)
if versionSelectorIndex == -1 {
return "", fmt.Errorf("failed to find semver selector in factory bytecode")
}
for i := versionSelectorIndex; i < len(bytecode); i++ {
if bytecode[i] == 0 {
continue
}
if i+patternLen > len(bytecode) {
break
}
slice := bytecode[i : i+patternLen]
if slice[0] == 0x00 {
continue
}
matches := semverRegexp.FindSubmatch(slice)
if len(matches) == 0 {
continue
}
return string(matches[1]), nil
}
return "", fmt.Errorf("failed to find semver in factory bytecode")
}
......@@ -119,7 +119,7 @@ func ManagerOwnerAddrFor(chainID uint64) (common.Address, error) {
func StandardArtifactsURLForTag(tag string) (*url.URL, error) {
switch tag {
case "op-contracts/v1.6.0":
return url.Parse(standardArtifactsURL("5ff7fb7f5d3ff30d165fef6cec22c7af8eceb102dd964034762cf988a6678d51"))
return url.Parse(standardArtifactsURL("ee07c78c3d8d4cd8f7a933c050f5afeebaa281b57b226cc6f092b19de2a8d61f"))
case "op-contracts/v1.7.0-beta.1+l2-contracts":
return url.Parse(standardArtifactsURL("b0fb1f6f674519d637cff39a22187a5993d7f81a6d7b7be6507a0b50a5e38597"))
default:
......
......@@ -15,6 +15,8 @@ import (
"strings"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
......@@ -30,6 +32,12 @@ type CleanupFunc func() error
var noopCleanup = func() error { return nil }
func LogProgressor(lgr log.Logger) DownloadProgressor {
return func(curr, total int64) {
lgr.Info("artifacts download progress", "current", curr, "total", total)
}
}
func DownloadArtifacts(ctx context.Context, loc *opcm.ArtifactsLocator, progress DownloadProgressor) (foundry.StatDirFs, CleanupFunc, error) {
var u *url.URL
var err error
......
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