forked from ethereum-optimism/optimism
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
op-chain-ops: create tool to determine which contract versions are de…
…ployed for each chain (ethereum-optimism#8859) * op-chain-ops: create op-version-check forked from op-upgrade * op-version-check: drop logic for upgrading contracts * op-version-check: add logic for outputting contract versions * op-version-check: improve support for checking multiple chains * op-version-check: replace legacy upgrade references * op-chain-ops: create README * op-version-check: improve README * op-version-check: drop unnecessary deploy-config flag * op-version-check: create op-version-check in Makefile * op-version-check: add usage section to README * op-chain-ops: add op-version-check to README * op-version-check: fix path in README * op-version-check: remove unused function toDeployConfigName * op-version-check: drop capitalization on error strings * op-node: capitalize writeJSONFile function this makes it accessible when op-node/cmd/genesis is imported as a package * op-chain-ops: use op-node/cmd/genesis for writeJSON * op-chain-ops: set name op_node_genesis for import * op-chain-ops: use op-node WriteJSONFile in op-upgrade * op-chains-ops: move to toSuperchainName to upgrades package * op-chain-ops: output all contracts for op-version-check addresses ethereum-optimism#8859 (comment) * op-chain-ops: add logging if chain ID is skipped by op-version-check * op-service: create jsonutil/write * op-chain-ops use jsonutil in op-upgrade, op-version-check * op-chain-ops: pass RPC URLs as flags in op-version-check * op-chain-ops: refactor of op-version-check * op-chain-ops: refactor of op-version-check * op-chain-ops: add more logging for op-version-check * op-chain-ops: add more logging for op-version-check
- Loading branch information
1 parent
2ce0ccf
commit aba06fc
Showing
7 changed files
with
299 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
op-version-check: | ||
go build -o ./bin/op-version-check ./cmd/op-version-check/main.go | ||
|
||
test: | ||
go test ./... | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,52 @@ | ||
# op-chain-ops | ||
|
||
This package contains utilities for working with chain state. | ||
|
||
## op-version-check | ||
|
||
A CLI tool for determining which contract versions are deployed for | ||
chains in a superchain. It will output a JSON file that contains a | ||
list of each chain's versions. It is assumed that the implementations | ||
that are being checked have already been deployed and their contract | ||
addresses exist inside of the `superchain-registry` repository. It is | ||
also assumed that the semantic version file in the `superchain-registry` | ||
has been updated. The tool will output the semantic versioning to | ||
determine which contract versions are deployed. | ||
|
||
### Configuration | ||
|
||
#### L1 RPC URL | ||
|
||
The L1 RPC URL is used to determine which superchain to target. All | ||
L2s that are not based on top of the L1 chain that corresponds to the | ||
L1 RPC URL are filtered out from being checked. It also is used to | ||
double check that the data in the `superchain-registry` is correct. | ||
|
||
#### Chain IDs | ||
|
||
A list of L2 chain IDs can be passed that will be used to filter which | ||
L2 chains will have their versions checked. Omitting this argument will | ||
result in all chains in the superchain being considered. | ||
|
||
#### Deploy Config | ||
|
||
The path to the `deploy-config` directory in the contracts package. | ||
Since multiple L2 networks may be considered in the check, the `deploy-config` | ||
directory must be passed and then the particular deploy config files will | ||
be read out of the directory as needed. | ||
|
||
#### Outfile | ||
|
||
The file that the versions should be written to. If omitted, the file | ||
will be written to stdout | ||
|
||
#### Usage | ||
|
||
It can be built and run using the [Makefile](./Makefile) `op-version-check` | ||
target. Run `make op-version-check` to create a binary in [./bin/op-version-check](./bin/op-version-check) | ||
that can be executed, optionally providing the `--l1-rpc-url`, `--chain-ids`, | ||
`--superchain-target`, and `--outfile` flags. | ||
|
||
```sh | ||
./bin/op-version-check | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# op-version-check | ||
|
||
A CLI tool for determining which contract versions are deployed for | ||
chains in a superchain. It will output a JSON file that contains a | ||
list of each chain's versions. It is assumed that the implementations | ||
that are being checked have already been deployed and their contract | ||
addresses exist inside of the `superchain-registry` repository. It is | ||
also assumed that the semantic version file in the `superchain-registry` | ||
has been updated. The tool will output the semantic versioning to | ||
determine which contract versions are deployed. | ||
|
||
### Configuration | ||
|
||
#### L1 RPC URL | ||
|
||
The L1 RPC URL is used to determine which superchain to target. All | ||
L2s that are not based on top of the L1 chain that corresponds to the | ||
L1 RPC URL are filtered out from being checked. It also is used to | ||
double check that the data in the `superchain-registry` is correct. | ||
|
||
#### Chain IDs | ||
|
||
A list of L2 chain IDs can be passed that will be used to filter which | ||
L2 chains will have their versions checked. Omitting this argument will | ||
result in all chains in the superchain being considered. | ||
|
||
#### Deploy Config | ||
|
||
The path to the `deploy-config` directory in the contracts package. | ||
Since multiple L2 networks may be considered in the check, the `deploy-config` | ||
directory must be passed and then the particular deploy config files will | ||
be read out of the directory as needed. | ||
|
||
#### Outfile | ||
|
||
The file that the versions should be written to. If omitted, the file | ||
will be written to stdout | ||
|
||
#### Usage | ||
|
||
It can be built and run using the [Makefile](../../Makefile) `op-version-check` | ||
target. Run `make op-version-check` to create a binary in [../../bin/op-version-check](../../bin/op-version-check) | ||
that can be executed, optionally providing the `--l1-rpc-url`, `--chain-ids`, | ||
`--superchain-target`, and `--outfile` flags. | ||
|
||
```sh | ||
./bin/op-version-check | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/ethereum/go-ethereum/ethclient" | ||
"github.com/ethereum/go-ethereum/log" | ||
"github.com/mattn/go-isatty" | ||
"github.com/urfave/cli/v2" | ||
"golang.org/x/exp/maps" | ||
|
||
"github.com/ethereum-optimism/optimism/op-chain-ops/upgrades" | ||
"github.com/ethereum-optimism/optimism/op-service/jsonutil" | ||
"github.com/ethereum-optimism/superchain-registry/superchain" | ||
) | ||
|
||
type Contract struct { | ||
Version string `yaml:"version"` | ||
Address superchain.Address `yaml:"address"` | ||
} | ||
|
||
type ChainVersionCheck struct { | ||
Name string `yaml:"name"` | ||
ChainID uint64 `yaml:"chain_id"` | ||
Contracts map[string]Contract `yaml:"contracts"` | ||
} | ||
|
||
func main() { | ||
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd())))) | ||
|
||
app := &cli.App{ | ||
Name: "op-version-check", | ||
Usage: "Determine which contract versions are deployed for chains in a superchain", | ||
Flags: []cli.Flag{ | ||
&cli.StringSliceFlag{ | ||
Name: "l1-rpc-urls", | ||
Usage: "L1 RPC URLs, the chain ID will be used to determine the superchain", | ||
EnvVars: []string{"L1_RPC_URL"}, | ||
}, | ||
&cli.StringSliceFlag{ | ||
Name: "l2-rpc-urls", | ||
Usage: "L2 RPC URLs, corresponding to chains to check versions for. Corresponds to all chains if empty", | ||
EnvVars: []string{"L1_RPC_URL"}, | ||
}, | ||
&cli.PathFlag{ | ||
Name: "outfile", | ||
Usage: "The file to write the output to. If not specified, output is written to stdout", | ||
EnvVars: []string{"OUTFILE"}, | ||
}, | ||
}, | ||
Action: entrypoint, | ||
} | ||
|
||
if err := app.Run(os.Args); err != nil { | ||
log.Crit("error op-version-check", "err", err) | ||
} | ||
} | ||
|
||
// entrypoint contains the main logic of the script | ||
func entrypoint(ctx *cli.Context) error { | ||
l1RPCURLs := ctx.StringSlice("l1-rpc-urls") | ||
l2RPCURLs := ctx.StringSlice("l2-rpc-urls") | ||
|
||
var l2ChainIDs []uint64 | ||
|
||
// If no L2 RPC URLs are specified, we check all chains for the L1 RPC URL | ||
if len(l2RPCURLs) == 0 { | ||
l2ChainIDs = maps.Keys(superchain.OPChains) | ||
} else { | ||
for _, l2RPCURL := range l2RPCURLs { | ||
client, err := ethclient.Dial(l2RPCURL) | ||
if err != nil { | ||
return errors.New("cannot create L2 client") | ||
} | ||
|
||
l2ChainID, err := client.ChainID(ctx.Context) | ||
if err != nil { | ||
return fmt.Errorf("cannot fetch L2 chain ID: %w", err) | ||
} | ||
|
||
l2ChainIDs = append(l2ChainIDs, l2ChainID.Uint64()) | ||
} | ||
} | ||
|
||
output := []ChainVersionCheck{} | ||
|
||
for _, l2ChainID := range l2ChainIDs { | ||
chainConfig := superchain.OPChains[l2ChainID] | ||
|
||
if chainConfig.ChainID != l2ChainID { | ||
return fmt.Errorf("mismatched chain IDs: %d != %d", chainConfig.ChainID, l2ChainID) | ||
} | ||
|
||
for _, l1RPCURL := range l1RPCURLs { | ||
client, err := ethclient.Dial(l1RPCURL) | ||
if err != nil { | ||
return errors.New("cannot create L1 client") | ||
} | ||
|
||
l1ChainID, err := client.ChainID(ctx.Context) | ||
if err != nil { | ||
return fmt.Errorf("cannot fetch L1 chain ID: %w", err) | ||
} | ||
|
||
superchainName, err := upgrades.ToSuperchainName(l1ChainID.Uint64()) | ||
if err != nil { | ||
return fmt.Errorf("error getting superchain name: %w", err) | ||
} | ||
|
||
if superchainName != chainConfig.Superchain { | ||
// L2 corresponds to a different superchain than L1, skip | ||
log.Info("Ignoring L1/L2", "l1-chain-id", l1ChainID, "l2-chain-id", l2ChainID) | ||
continue | ||
} | ||
|
||
log.Info(chainConfig.Name, "l1-chain-id", l1ChainID, "l2-chain-id", l2ChainID) | ||
|
||
log.Info("Detecting on chain contracts") | ||
// Tracking the individual addresses can be deprecated once the system is upgraded | ||
// to the new contracts where the system config has a reference to each address. | ||
addresses, ok := superchain.Addresses[l2ChainID] | ||
if !ok { | ||
return fmt.Errorf("no addresses for chain ID %d", l2ChainID) | ||
} | ||
versions, err := upgrades.GetContractVersions(ctx.Context, addresses, chainConfig, client) | ||
if err != nil { | ||
return fmt.Errorf("error getting contract versions: %w", err) | ||
} | ||
|
||
contracts := make(map[string]Contract) | ||
|
||
contracts["AddressManager"] = Contract{Version: "null", Address: addresses.AddressManager} | ||
contracts["L1CrossDomainMessenger"] = Contract{Version: versions.L1CrossDomainMessenger, Address: addresses.L1CrossDomainMessengerProxy} | ||
contracts["L1ERC721Bridge"] = Contract{Version: versions.L1ERC721Bridge, Address: addresses.L1ERC721BridgeProxy} | ||
contracts["L1StandardBridge"] = Contract{Version: versions.L1ERC721Bridge, Address: addresses.L1StandardBridgeProxy} | ||
contracts["L2OutputOracle"] = Contract{Version: versions.L2OutputOracle, Address: addresses.L2OutputOracleProxy} | ||
contracts["OptimismMintableERC20Factory"] = Contract{Version: versions.OptimismMintableERC20Factory, Address: addresses.OptimismMintableERC20FactoryProxy} | ||
contracts["OptimismPortal"] = Contract{Version: versions.OptimismPortal, Address: addresses.OptimismPortalProxy} | ||
contracts["SystemConfig"] = Contract{Version: versions.SystemConfig, Address: chainConfig.SystemConfigAddr} | ||
contracts["ProxyAdmin"] = Contract{Version: "null", Address: addresses.ProxyAdmin} | ||
|
||
output = append(output, ChainVersionCheck{Name: chainConfig.Name, ChainID: l2ChainID, Contracts: contracts}) | ||
|
||
log.Info("Successfully processed contract versions", "chain", chainConfig.Name, "l1-chain-id", l1ChainID, "l2-chain-id", l2ChainID) | ||
break | ||
} | ||
} | ||
// Write contract versions to disk or stdout | ||
if outfile := ctx.Path("outfile"); outfile != "" { | ||
if err := jsonutil.WriteJSON(outfile, output); err != nil { | ||
return err | ||
} | ||
} else { | ||
data, err := json.MarshalIndent(output, "", " ") | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Println(string(data)) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package jsonutil | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
) | ||
|
||
func WriteJSON(outfile string, input interface{}) error { | ||
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
enc := json.NewEncoder(f) | ||
enc.SetIndent("", " ") | ||
return enc.Encode(input) | ||
} |