Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bindings and metadata overwrite warning #8283

Merged
merged 2 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions op-bindings/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ contracts-list := ./artifacts.json
log-level := info
ETHERSCAN_APIKEY_ETH ?=
ETHERSCAN_APIKEY_OP ?=
RPC_URL_ETH ?=
RPC_URL_OP ?=

all: version mkdir bindings

Expand Down Expand Up @@ -36,7 +38,9 @@ bindgen-generate-all:
--source-maps-list MIPS,PreimageOracle \
--forge-artifacts $(contracts-dir)/forge-artifacts \
--etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \
--etherscan.apikey.op $(ETHERSCAN_APIKEY_OP)
--etherscan.apikey.op $(ETHERSCAN_APIKEY_OP) \
--rpc.url.eth $(RPC_URL_ETH) \
--rpc.url.op $(RPC_URL_OP)

bindgen-local: compile bindgen-generate-local

Expand All @@ -60,7 +64,9 @@ bindgen-remote:
--log.level $(log-level) \
remote \
--etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \
--etherscan.apikey.op $(ETHERSCAN_APIKEY_OP)
--etherscan.apikey.op $(ETHERSCAN_APIKEY_OP) \
--rpc.url.eth $(RPC_URL_ETH) \
--rpc.url.op $(RPC_URL_OP)

mkdir:
mkdir -p $(pkg)
Expand Down
5 changes: 5 additions & 0 deletions op-bindings/bindgen/generator_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)

type bindGenGeneratorRemote struct {
Expand All @@ -15,6 +16,10 @@ type bindGenGeneratorRemote struct {
eth contractDataClient
op contractDataClient
}
rpcClients struct {
eth *ethclient.Client
op *ethclient.Client
}
tempArtifactsDir string
}

Expand Down
18 changes: 18 additions & 0 deletions op-bindings/bindgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum-optimism/optimism/op-e2e/config"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -170,6 +171,13 @@ func parseConfigRemote(logger log.Logger, c *cli.Context) (bindGenGeneratorRemot

generator.contractDataClients.eth = etherscan.NewEthereumClient(c.String(EtherscanApiKeyEthFlagName))
generator.contractDataClients.op = etherscan.NewOptimismClient(c.String(EtherscanApiKeyOpFlagName))

if generator.rpcClients.eth, err = ethclient.Dial(c.String(RpcUrlEthFlagName)); err != nil {
return bindGenGeneratorRemote{}, fmt.Errorf("error initializing Ethereum client: %w", err)
}
if generator.rpcClients.op, err = ethclient.Dial(c.String(RpcUrlOpFlagName)); err != nil {
return bindGenGeneratorRemote{}, fmt.Errorf("error initializing Optimism client: %w", err)
}
return generator, nil
}

Expand Down Expand Up @@ -221,5 +229,15 @@ func remoteFlags() []cli.Flag {
Usage: "API key to make queries to Etherscan for Optimism",
Required: true,
},
&cli.StringFlag{
Name: RpcUrlEthFlagName,
Usage: "RPC URL (with API key if required) to query Ethereum",
Required: true,
},
&cli.StringFlag{
Name: RpcUrlOpFlagName,
Usage: "RPC URL (with API key if required) to query Optimism",
Required: true,
},
}
}
81 changes: 81 additions & 0 deletions op-bindings/bindgen/remote_handlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"context"
"fmt"
"os"
Expand All @@ -10,6 +11,8 @@ import (
"text/template"

"github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)

type contractData struct {
Expand All @@ -25,6 +28,12 @@ func (generator *bindGenGeneratorRemote) standardHandler(contractMetadata *remot
}

contractMetadata.DeployedBin = fetchedData.deployedBin
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
return err
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
return err
}

// If ABI was explicitly provided by config, don't overwrite
if contractMetadata.ABI == "" {
Expand Down Expand Up @@ -76,6 +85,12 @@ func (generator *bindGenGeneratorRemote) multiSendHandler(contractMetadata *remo

contractMetadata.ABI = fetchedData.abi
contractMetadata.DeployedBin = fetchedData.deployedBin
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
return err
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
return err
}
if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err
}
Expand All @@ -89,6 +104,12 @@ func (generator *bindGenGeneratorRemote) senderCreatorHandler(contractMetadata *
if err != nil {
return fmt.Errorf("error fetching deployed bytecode: %w", err)
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "eth"); err != nil {
return err
}
if err = generator.compareDeployedBytecodeWithRpc(contractMetadata, "op"); err != nil {
return err
}

// The SenderCreator contract is deployed by EntryPoint, so the transaction data
// from the deployment transaction is for the entire EntryPoint deployment.
Expand Down Expand Up @@ -221,6 +242,41 @@ func (generator *bindGenGeneratorRemote) compareBytecodeWithOp(contractMetadataE
return nil
}

func (generator *bindGenGeneratorRemote) compareDeployedBytecodeWithRpc(contractMetadata *remoteContractMetadata, chain string) error {
var client *ethclient.Client
switch chain {
case "eth":
client = generator.rpcClients.eth
case "op":
client = generator.rpcClients.op
default:
return fmt.Errorf("unknown chain: %s, unable to retrieve a RPC client", chain)
}

var deployment common.Address
switch chain {
case "eth":
deployment = contractMetadata.Deployments.Eth
case "op":
deployment = contractMetadata.Deployments.Op
default:
generator.logger.Warn("Unable to compare bytecode from Etherscan against RPC client, no deployment address provided for chain", "chain", chain)
}

if deployment != (common.Address{}) {
bytecode, err := client.CodeAt(context.Background(), common.HexToAddress(deployment.Hex()), nil)
if err != nil {
return fmt.Errorf("error getting deployed bytecode from RPC on chain: %s err: %w", chain, err)
}
bytecodeHex := common.Bytes2Hex(bytecode)
if !strings.EqualFold(strings.TrimPrefix(contractMetadata.DeployedBin, "0x"), bytecodeHex) {
return fmt.Errorf("%s deployment bytecode from RPC doesn't match bytecode from Etherscan. rpcBytecode: %s etherscanBytecode: %s", contractMetadata.Name, bytecodeHex, contractMetadata.DeployedBin)
}
}

return nil
}

func (generator *bindGenGeneratorRemote) writeAllOutputs(contractMetadata *remoteContractMetadata, fileTemplate string) error {
abiFilePath, bytecodeFilePath, err := writeContractArtifacts(
generator.logger, generator.tempArtifactsDir, contractMetadata.Name,
Expand All @@ -243,6 +299,15 @@ func (generator *bindGenGeneratorRemote) writeAllOutputs(contractMetadata *remot

func (generator *bindGenGeneratorRemote) writeContractMetadata(contractMetadata *remoteContractMetadata, fileTemplate *template.Template) error {
metadataFilePath := filepath.Join(generator.metadataOut, strings.ToLower(contractMetadata.Name)+"_more.go")

var existingOutput []byte
if _, err := os.Stat(metadataFilePath); err == nil {
existingOutput, err = os.ReadFile(metadataFilePath)
if err != nil {
return fmt.Errorf("error reading existing metadata output file, metadataFilePath: %s err: %w", metadataFilePath, err)
}
}

metadataFile, err := os.OpenFile(
metadataFilePath,
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
Expand All @@ -257,6 +322,22 @@ func (generator *bindGenGeneratorRemote) writeContractMetadata(contractMetadata
return fmt.Errorf("error writing %s's contract metadata at %s: %w", contractMetadata.Name, metadataFilePath, err)
}

if len(existingOutput) != 0 {
var newOutput []byte
newOutput, err = os.ReadFile(metadataFilePath)
if err != nil {
return fmt.Errorf("error reading new file: %w", err)
}

if bytes.Equal(existingOutput, newOutput) {
generator.logger.Debug("No changes detected in the contract metadata", "contract", contractMetadata.Name)
} else {
generator.logger.Warn("Changes detected in the contract metadata, old metadata has been overwritten", "contract", contractMetadata.Name)
}
} else {
generator.logger.Debug("No existing contract metadata found, skipping comparison", "contract", contractMetadata.Name)
}

generator.logger.Debug("Successfully wrote contract metadata", "contract", contractMetadata.Name, "path", metadataFilePath)
return nil
}
Expand Down
27 changes: 25 additions & 2 deletions op-bindings/bindgen/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -119,14 +120,36 @@ func genContractBindings(logger log.Logger, abiFilePath, bytecodeFilePath, goPac
}

outFilePath := path.Join(cwd, goPackageName, strings.ToLower(contractName)+".go")
logger.Debug("Generating contract bindings", "contractName", contractName, "outFilePath", outFilePath)

var existingOutput []byte
if _, err := os.Stat(outFilePath); err == nil {
existingOutput, err = os.ReadFile(outFilePath)
if err != nil {
return fmt.Errorf("error reading existing bindings output file, outFilePath: %s err: %w", outFilePath, err)
}
}

logger.Debug("Generating contract bindings", "contractName", contractName, "outFilePath", outFilePath)
cmd := exec.Command("abigen", "--abi", abiFilePath, "--bin", bytecodeFilePath, "--pkg", goPackageName, "--type", contractName, "--out", outFilePath)
cmd.Stdout = os.Stdout

if err := cmd.Run(); err != nil {
return fmt.Errorf("error running abigen for %s: %w", contractName, err)
}

if len(existingOutput) != 0 {
newOutput, err := os.ReadFile(outFilePath)
if err != nil {
return fmt.Errorf("error reading new file: %w", err)
}

if bytes.Equal(existingOutput, newOutput) {
logger.Debug("No changes detected in the contract bindings", "contractName", contractName)
} else {
logger.Warn("Changes detected in the contract bindings, old bindings have been overwritten", "contractName", contractName)
}
} else {
logger.Debug("No existing contract bindings found, skipping comparison", "contractName", contractName)
}

return nil
}