Commit 4abef208 authored by Wyatt Barnes's avatar Wyatt Barnes Committed by GitHub

Add deployed bytecode retrieval mitigation (#8282)

parent 6d78aa09
...@@ -7,6 +7,8 @@ contracts-list := ./artifacts.json ...@@ -7,6 +7,8 @@ contracts-list := ./artifacts.json
log-level := info log-level := info
ETHERSCAN_APIKEY_ETH ?= ETHERSCAN_APIKEY_ETH ?=
ETHERSCAN_APIKEY_OP ?= ETHERSCAN_APIKEY_OP ?=
RPC_URL_ETH ?=
RPC_URL_OP ?=
all: version mkdir bindings all: version mkdir bindings
...@@ -36,7 +38,9 @@ bindgen-generate-all: ...@@ -36,7 +38,9 @@ bindgen-generate-all:
--source-maps-list MIPS,PreimageOracle \ --source-maps-list MIPS,PreimageOracle \
--forge-artifacts $(contracts-dir)/forge-artifacts \ --forge-artifacts $(contracts-dir)/forge-artifacts \
--etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \ --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 bindgen-local: compile bindgen-generate-local
...@@ -60,7 +64,9 @@ bindgen-remote: ...@@ -60,7 +64,9 @@ bindgen-remote:
--log.level $(log-level) \ --log.level $(log-level) \
remote \ remote \
--etherscan.apikey.eth $(ETHERSCAN_APIKEY_ETH) \ --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:
mkdir -p $(pkg) mkdir -p $(pkg)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/etherscan" "github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
) )
type bindGenGeneratorRemote struct { type bindGenGeneratorRemote struct {
...@@ -15,6 +16,10 @@ type bindGenGeneratorRemote struct { ...@@ -15,6 +16,10 @@ type bindGenGeneratorRemote struct {
eth contractDataClient eth contractDataClient
op contractDataClient op contractDataClient
} }
rpcClients struct {
eth *ethclient.Client
op *ethclient.Client
}
tempArtifactsDir string tempArtifactsDir string
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/etherscan" "github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum-optimism/optimism/op-e2e/config" "github.com/ethereum-optimism/optimism/op-e2e/config"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
...@@ -170,6 +171,13 @@ func parseConfigRemote(logger log.Logger, c *cli.Context) (bindGenGeneratorRemot ...@@ -170,6 +171,13 @@ func parseConfigRemote(logger log.Logger, c *cli.Context) (bindGenGeneratorRemot
generator.contractDataClients.eth = etherscan.NewEthereumClient(c.String(EtherscanApiKeyEthFlagName)) generator.contractDataClients.eth = etherscan.NewEthereumClient(c.String(EtherscanApiKeyEthFlagName))
generator.contractDataClients.op = etherscan.NewOptimismClient(c.String(EtherscanApiKeyOpFlagName)) 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 return generator, nil
} }
...@@ -221,5 +229,15 @@ func remoteFlags() []cli.Flag { ...@@ -221,5 +229,15 @@ func remoteFlags() []cli.Flag {
Usage: "API key to make queries to Etherscan for Optimism", Usage: "API key to make queries to Etherscan for Optimism",
Required: true, 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,
},
} }
} }
...@@ -10,6 +10,8 @@ import ( ...@@ -10,6 +10,8 @@ import (
"text/template" "text/template"
"github.com/ethereum-optimism/optimism/op-bindings/etherscan" "github.com/ethereum-optimism/optimism/op-bindings/etherscan"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
) )
type contractData struct { type contractData struct {
...@@ -25,6 +27,12 @@ func (generator *bindGenGeneratorRemote) standardHandler(contractMetadata *remot ...@@ -25,6 +27,12 @@ func (generator *bindGenGeneratorRemote) standardHandler(contractMetadata *remot
} }
contractMetadata.DeployedBin = fetchedData.deployedBin 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 ABI was explicitly provided by config, don't overwrite
if contractMetadata.ABI == "" { if contractMetadata.ABI == "" {
...@@ -76,6 +84,12 @@ func (generator *bindGenGeneratorRemote) multiSendHandler(contractMetadata *remo ...@@ -76,6 +84,12 @@ func (generator *bindGenGeneratorRemote) multiSendHandler(contractMetadata *remo
contractMetadata.ABI = fetchedData.abi contractMetadata.ABI = fetchedData.abi
contractMetadata.DeployedBin = fetchedData.deployedBin 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 { if contractMetadata.InitBin, err = generator.removeDeploymentSalt(fetchedData.deploymentTx.Input, contractMetadata.DeploymentSalt); err != nil {
return err return err
} }
...@@ -89,6 +103,12 @@ func (generator *bindGenGeneratorRemote) senderCreatorHandler(contractMetadata * ...@@ -89,6 +103,12 @@ func (generator *bindGenGeneratorRemote) senderCreatorHandler(contractMetadata *
if err != nil { if err != nil {
return fmt.Errorf("error fetching deployed bytecode: %w", err) 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 // The SenderCreator contract is deployed by EntryPoint, so the transaction data
// from the deployment transaction is for the entire EntryPoint deployment. // from the deployment transaction is for the entire EntryPoint deployment.
...@@ -221,6 +241,41 @@ func (generator *bindGenGeneratorRemote) compareBytecodeWithOp(contractMetadataE ...@@ -221,6 +241,41 @@ func (generator *bindGenGeneratorRemote) compareBytecodeWithOp(contractMetadataE
return nil 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 { func (generator *bindGenGeneratorRemote) writeAllOutputs(contractMetadata *remoteContractMetadata, fileTemplate string) error {
abiFilePath, bytecodeFilePath, err := writeContractArtifacts( abiFilePath, bytecodeFilePath, err := writeContractArtifacts(
generator.logger, generator.tempArtifactsDir, contractMetadata.Name, generator.logger, generator.tempArtifactsDir, contractMetadata.Name,
......
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