Commit 1bdd60d1 authored by Adrian Sutton's avatar Adrian Sutton

Merge remote-tracking branch 'origin/develop' into aj/split-providers

parents 2e4de94b c3b17448
...@@ -192,9 +192,13 @@ jobs: ...@@ -192,9 +192,13 @@ jobs:
description: Run the release script description: Run the release script
type: boolean type: boolean
default: false default: false
resource_class:
description: Docker resoruce class
type: string
default: medium
machine: machine:
image: ubuntu-2204:2022.07.1 image: ubuntu-2204:2022.07.1
resource_class: medium resource_class: "<<parameters.resource_class>>"
docker_layer_caching: true # we rely on this for faster builds, and actively warm it up for builds with common stages docker_layer_caching: true # we rely on this for faster builds, and actively warm it up for builds with common stages
steps: steps:
- checkout - checkout
...@@ -1668,6 +1672,7 @@ workflows: ...@@ -1668,6 +1672,7 @@ workflows:
docker_tags: <<pipeline.git.revision>>,latest docker_tags: <<pipeline.git.revision>>,latest
publish: true publish: true
release: true release: true
resource_class: xlarge
context: context:
- oplabs-gcr - oplabs-gcr
requires: requires:
......
...@@ -7,7 +7,7 @@ toolchain go1.21.1 ...@@ -7,7 +7,7 @@ toolchain go1.21.1
require github.com/ethereum-optimism/optimism v0.0.0 require github.com/ethereum-optimism/optimism v0.0.0
require ( require (
golang.org/x/crypto v0.14.0 // indirect golang.org/x/crypto v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect golang.org/x/sys v0.14.0 // indirect
) )
......
...@@ -4,8 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb ...@@ -4,8 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
......
...@@ -33,7 +33,7 @@ flag_management: ...@@ -33,7 +33,7 @@ flag_management:
individual_flags: individual_flags:
- name: contracts-bedrock-tests - name: contracts-bedrock-tests
paths: paths:
- packages/contracts-bedrock - packages/contracts-bedrock/src
statuses: statuses:
- type: patch - type: patch
target: 100% target: 100%
......
...@@ -33,13 +33,13 @@ require ( ...@@ -33,13 +33,13 @@ require (
github.com/multiformats/go-multiaddr v0.12.0 github.com/multiformats/go-multiaddr v0.12.0
github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/gomega v1.29.0 github.com/onsi/gomega v1.30.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0 github.com/pkg/profile v1.7.0
github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_golang v1.17.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.25.7
golang.org/x/crypto v0.14.0 golang.org/x/crypto v0.15.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/sync v0.5.0 golang.org/x/sync v0.5.0
golang.org/x/term v0.14.0 golang.org/x/term v0.14.0
...@@ -200,7 +200,7 @@ require ( ...@@ -200,7 +200,7 @@ require (
golang.org/x/mod v0.13.0 // indirect golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.14.0 // indirect golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.14.0 // indirect golang.org/x/tools v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
......
...@@ -569,8 +569,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J ...@@ -569,8 +569,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
...@@ -769,8 +769,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm ...@@ -769,8 +769,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
...@@ -895,8 +895,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= ...@@ -895,8 +895,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -13,7 +13,7 @@ const StorageSetterStorageLayoutJSON = "{\"storage\":null,\"types\":{}}" ...@@ -13,7 +13,7 @@ const StorageSetterStorageLayoutJSON = "{\"storage\":null,\"types\":{}}"
var StorageSetterStorageLayout = new(solc.StorageLayout) var StorageSetterStorageLayout = new(solc.StorageLayout)
var StorageSetterDeployedBin = "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c8063a6ed563e1161005b578063a6ed563e1461011c578063bd02d0f51461011c578063ca446dd914610138578063e2a4853a146100bf57600080fd5b806321f8a721146100825780634e91db08146100bf57806354fd4d50146100d3575b600080fd5b610095610090366004610156565b610146565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100d16100cd36600461016f565b9055565b005b61010f6040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6040516100b69190610191565b61012a610090366004610156565b6040519081526020016100b6565b6100d16100cd366004610204565b6000610150825490565b92915050565b60006020828403121561016857600080fd5b5035919050565b6000806040838503121561018257600080fd5b50508035926020909101359150565b600060208083528351808285015260005b818110156101be578581018301518582016040015282016101a2565b818111156101d0576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6000806040838503121561021757600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461024257600080fd5b80915050925092905056fea164736f6c634300080f000a" var StorageSetterDeployedBin = "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063a6ed563e1161005b578063a6ed563e1461013a578063bd02d0f51461013a578063ca446dd914610156578063e2a4853a146100df57600080fd5b80630528afe21461008d57806321f8a721146100a25780634e91db08146100df57806354fd4d50146100f1575b600080fd5b6100a061009b3660046101d7565b610164565b005b6100b56100b036600461024c565b6101c7565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100a06100ed366004610265565b9055565b61012d6040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b6040516100d69190610287565b6101486100b036600461024c565b6040519081526020016100d6565b6100a06100ed3660046102fa565b8060005b818110156101c1576101af84848381811061018557610185610343565b905060400201600001358585848181106101a1576101a1610343565b905060400201602001359055565b806101b981610372565b915050610168565b50505050565b60006101d1825490565b92915050565b600080602083850312156101ea57600080fd5b823567ffffffffffffffff8082111561020257600080fd5b818501915085601f83011261021657600080fd5b81358181111561022557600080fd5b8660208260061b850101111561023a57600080fd5b60209290920196919550909350505050565b60006020828403121561025e57600080fd5b5035919050565b6000806040838503121561027857600080fd5b50508035926020909101359150565b600060208083528351808285015260005b818110156102b457858101830151858201604001528201610298565b818111156102c6576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6000806040838503121561030d57600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461033857600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036103ca577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b506001019056fea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(StorageSetterStorageLayoutJSON), StorageSetterStorageLayout); err != nil { if err := json.Unmarshal([]byte(StorageSetterStorageLayoutJSON), StorageSetterStorageLayout); err != nil {
......
...@@ -388,41 +388,28 @@ func (d *DeployConfig) SetDeployments(deployments *L1Deployments) { ...@@ -388,41 +388,28 @@ func (d *DeployConfig) SetDeployments(deployments *L1Deployments) {
} }
// GetDeployedAddresses will get the deployed addresses of deployed L1 contracts // GetDeployedAddresses will get the deployed addresses of deployed L1 contracts
// required for the L2 genesis creation. Legacy systems use the `Proxy__` prefix // required for the L2 genesis creation.
// while modern systems use the `Proxy` suffix. First check for the legacy
// deployments so that this works with upgrading a system.
func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error { func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error {
var err error
if d.L1StandardBridgeProxy == (common.Address{}) { if d.L1StandardBridgeProxy == (common.Address{}) {
var l1StandardBridgeProxyDeployment *hardhat.Deployment l1StandardBridgeProxyDeployment, err := hh.GetDeployment("L1StandardBridgeProxy")
l1StandardBridgeProxyDeployment, err = hh.GetDeployment("Proxy__OVM_L1StandardBridge") if err != nil {
if errors.Is(err, hardhat.ErrCannotFindDeployment) { return fmt.Errorf("cannot find L1StandardBridgeProxy artifact: %w", err)
l1StandardBridgeProxyDeployment, err = hh.GetDeployment("L1StandardBridgeProxy")
if err != nil {
return err
}
} }
d.L1StandardBridgeProxy = l1StandardBridgeProxyDeployment.Address d.L1StandardBridgeProxy = l1StandardBridgeProxyDeployment.Address
} }
if d.L1CrossDomainMessengerProxy == (common.Address{}) { if d.L1CrossDomainMessengerProxy == (common.Address{}) {
var l1CrossDomainMessengerProxyDeployment *hardhat.Deployment l1CrossDomainMessengerProxyDeployment, err := hh.GetDeployment("L1CrossDomainMessengerProxy")
l1CrossDomainMessengerProxyDeployment, err = hh.GetDeployment("Proxy__OVM_L1CrossDomainMessenger") if err != nil {
if errors.Is(err, hardhat.ErrCannotFindDeployment) { return fmt.Errorf("cannot find L1CrossDomainMessengerProxy artifact: %w", err)
l1CrossDomainMessengerProxyDeployment, err = hh.GetDeployment("L1CrossDomainMessengerProxy")
if err != nil {
return err
}
} }
d.L1CrossDomainMessengerProxy = l1CrossDomainMessengerProxyDeployment.Address d.L1CrossDomainMessengerProxy = l1CrossDomainMessengerProxyDeployment.Address
} }
if d.L1ERC721BridgeProxy == (common.Address{}) { if d.L1ERC721BridgeProxy == (common.Address{}) {
// There is no legacy deployment of this contract
l1ERC721BridgeProxyDeployment, err := hh.GetDeployment("L1ERC721BridgeProxy") l1ERC721BridgeProxyDeployment, err := hh.GetDeployment("L1ERC721BridgeProxy")
if err != nil { if err != nil {
return err return fmt.Errorf("cannot find L1ERC721BridgeProxy artifact: %w", err)
} }
d.L1ERC721BridgeProxy = l1ERC721BridgeProxyDeployment.Address d.L1ERC721BridgeProxy = l1ERC721BridgeProxyDeployment.Address
} }
...@@ -430,7 +417,7 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error { ...@@ -430,7 +417,7 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error {
if d.SystemConfigProxy == (common.Address{}) { if d.SystemConfigProxy == (common.Address{}) {
systemConfigProxyDeployment, err := hh.GetDeployment("SystemConfigProxy") systemConfigProxyDeployment, err := hh.GetDeployment("SystemConfigProxy")
if err != nil { if err != nil {
return err return fmt.Errorf("cannot find SystemConfigProxy artifact: %w", err)
} }
d.SystemConfigProxy = systemConfigProxyDeployment.Address d.SystemConfigProxy = systemConfigProxyDeployment.Address
} }
...@@ -438,7 +425,7 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error { ...@@ -438,7 +425,7 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error {
if d.OptimismPortalProxy == (common.Address{}) { if d.OptimismPortalProxy == (common.Address{}) {
optimismPortalProxyDeployment, err := hh.GetDeployment("OptimismPortalProxy") optimismPortalProxyDeployment, err := hh.GetDeployment("OptimismPortalProxy")
if err != nil { if err != nil {
return err return fmt.Errorf("cannot find OptimismPortalProxy artifact: %w", err)
} }
d.OptimismPortalProxy = optimismPortalProxyDeployment.Address d.OptimismPortalProxy = optimismPortalProxyDeployment.Address
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
...@@ -21,6 +22,11 @@ const ( ...@@ -21,6 +22,11 @@ const (
methodClaim = "claimData" methodClaim = "claimData"
methodL1Head = "l1Head" methodL1Head = "l1Head"
methodProposals = "proposals" methodProposals = "proposals"
methodResolve = "resolve"
methodResolveClaim = "resolveClaim"
methodAttack = "attack"
methodDefend = "defend"
methodStep = "step"
) )
type FaultDisputeGameContract struct { type FaultDisputeGameContract struct {
...@@ -138,6 +144,57 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Cl ...@@ -138,6 +144,57 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Cl
return claims, nil return claims, nil
} }
func (f *FaultDisputeGameContract) AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodStep, new(big.Int).SetUint64(claimIdx), isAttack, stateData, proof)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
call := f.resolveClaimCall(claimIdx)
_, err := f.multiCaller.SingleCall(ctx, batching.BlockLatest, call)
if err != nil {
return fmt.Errorf("failed to call resolve claim: %w", err)
}
return nil
}
func (f *FaultDisputeGameContract) ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error) {
call := f.resolveClaimCall(claimIdx)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) resolveClaimCall(claimIdx uint64) *batching.ContractCall {
return f.contract.Call(methodResolveClaim, new(big.Int).SetUint64(claimIdx))
}
func (f *FaultDisputeGameContract) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) {
call := f.resolveCall()
result, err := f.multiCaller.SingleCall(ctx, batching.BlockLatest, call)
if err != nil {
return gameTypes.GameStatusInProgress, fmt.Errorf("failed to call resolve: %w", err)
}
return gameTypes.GameStatusFromUint8(result.GetUint8(0))
}
func (f *FaultDisputeGameContract) ResolveTx() (txmgr.TxCandidate, error) {
call := f.resolveCall()
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall {
return f.contract.Call(methodResolve)
}
func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim { func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim {
parentIndex := result.GetUint32(0) parentIndex := result.GetUint32(0)
countered := result.GetBool(1) countered := result.GetBool(1)
......
...@@ -67,6 +67,13 @@ func TestSimpleGetters(t *testing.T) { ...@@ -67,6 +67,13 @@ func TestSimpleGetters(t *testing.T) {
return game.GetL1Head(context.Background()) return game.GetL1Head(context.Background())
}, },
}, },
{
method: methodResolve,
result: types.GameStatusInProgress,
call: func(game *FaultDisputeGameContract) (any, error) {
return game.CallResolve(context.Background())
},
},
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
...@@ -176,6 +183,57 @@ func TestGetAllClaims(t *testing.T) { ...@@ -176,6 +183,57 @@ func TestGetAllClaims(t *testing.T) {
require.Equal(t, expectedClaims, claims) require.Equal(t, expectedClaims, claims)
} }
func TestCallResolveClaim(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(methodResolveClaim, batching.BlockLatest, []interface{}{big.NewInt(123)}, nil)
err := game.CallResolveClaim(context.Background(), 123)
require.NoError(t, err)
}
func TestResolveClaimTx(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(methodResolveClaim, batching.BlockLatest, []interface{}{big.NewInt(123)}, nil)
tx, err := game.ResolveClaimTx(123)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func TestResolveTx(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(methodResolve, batching.BlockLatest, nil, nil)
tx, err := game.ResolveTx()
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func TestAttackTx(t *testing.T) {
stubRpc, game := setup(t)
value := common.Hash{0xaa}
stubRpc.SetResponse(methodAttack, batching.BlockLatest, []interface{}{big.NewInt(111), value}, nil)
tx, err := game.AttackTx(111, value)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func TestDefendTx(t *testing.T) {
stubRpc, game := setup(t)
value := common.Hash{0xaa}
stubRpc.SetResponse(methodDefend, batching.BlockLatest, []interface{}{big.NewInt(111), value}, nil)
tx, err := game.DefendTx(111, value)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func TestStepTx(t *testing.T) {
stubRpc, game := setup(t)
stateData := []byte{1, 2, 3}
proofData := []byte{4, 5, 6, 7, 8, 9}
stubRpc.SetResponse(methodStep, batching.BlockLatest, []interface{}{big.NewInt(111), true, stateData, proofData}, nil)
tx, err := game.StepTx(111, true, stateData, proofData)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func expectGetClaim(stubRpc *batchingTest.AbiBasedRpc, claim faultTypes.Claim) { func expectGetClaim(stubRpc *batchingTest.AbiBasedRpc, claim faultTypes.Claim) {
stubRpc.SetResponse( stubRpc.SetResponse(
methodClaim, methodClaim,
......
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
...@@ -26,6 +25,10 @@ type GameInfo interface { ...@@ -26,6 +25,10 @@ type GameInfo interface {
GetClaimCount(context.Context) (uint64, error) GetClaimCount(context.Context) (uint64, error)
} }
// gameValidator checks that the specific game instance is compatible with the configuration.
// Typically, this is done by verifying the absolute prestate of the game matches the local absolute prestate.
type gameValidator func(ctx context.Context, gameContract *contracts.FaultDisputeGameContract) error
type GamePlayer struct { type GamePlayer struct {
act actor act actor
agreeWithProposedOutput bool agreeWithProposedOutput bool
...@@ -34,7 +37,7 @@ type GamePlayer struct { ...@@ -34,7 +37,7 @@ type GamePlayer struct {
status gameTypes.GameStatus status gameTypes.GameStatus
} }
type resourceCreator func(addr common.Address, contract *contracts.FaultDisputeGameContract, gameDepth uint64, dir string) (types.TraceProvider, types.OracleUpdater, error) type resourceCreator func(addr common.Address, contract *contracts.FaultDisputeGameContract, gameDepth uint64, dir string) (types.TraceAccessor, types.OracleUpdater, gameValidator, error)
func NewGamePlayer( func NewGamePlayer(
ctx context.Context, ctx context.Context,
...@@ -77,21 +80,20 @@ func NewGamePlayer( ...@@ -77,21 +80,20 @@ func NewGamePlayer(
return nil, fmt.Errorf("failed to fetch the game depth: %w", err) return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
} }
provider, updater, err := creator(addr, loader, gameDepth, dir) accessor, updater, validator, err := creator(addr, loader, gameDepth, dir)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create trace provider: %w", err) return nil, fmt.Errorf("failed to create trace accessor: %w", err)
} }
if err := ValidateAbsolutePrestate(ctx, provider, loader); err != nil { if err := validator(ctx, loader); err != nil {
return nil, fmt.Errorf("failed to validate absolute prestate: %w", err) return nil, fmt.Errorf("failed to validate absolute prestate: %w", err)
} }
responder, err := responder.NewFaultResponder(logger, txMgr, addr) responder, err := responder.NewFaultResponder(logger, txMgr, loader)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create the responder: %w", err) return nil, fmt.Errorf("failed to create the responder: %w", err)
} }
accessor := trace.NewSimpleTraceAccessor(provider)
agent := NewAgent(m, loader, int(gameDepth), accessor, responder, updater, cfg.AgreeWithProposedOutput, logger) agent := NewAgent(m, loader, int(gameDepth), accessor, responder, updater, cfg.AgreeWithProposedOutput, logger)
return &GamePlayer{ return &GamePlayer{
act: agent.Act, act: agent.Act,
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...@@ -37,16 +38,19 @@ func RegisterGameTypes( ...@@ -37,16 +38,19 @@ func RegisterGameTypes(
client *ethclient.Client, client *ethclient.Client,
) { ) {
if cfg.TraceTypeEnabled(config.TraceTypeCannon) { if cfg.TraceTypeEnabled(config.TraceTypeCannon) {
resourceCreator := func(addr common.Address, contract *contracts.FaultDisputeGameContract, gameDepth uint64, dir string) (faultTypes.TraceProvider, faultTypes.OracleUpdater, error) { resourceCreator := func(addr common.Address, contract *contracts.FaultDisputeGameContract, gameDepth uint64, dir string) (faultTypes.TraceAccessor, faultTypes.OracleUpdater, gameValidator, error) {
provider, err := cannon.NewTraceProvider(ctx, logger, m, cfg, contract, dir, gameDepth) provider, err := cannon.NewTraceProvider(ctx, logger, m, cfg, contract, dir, gameDepth)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("create cannon trace provider: %w", err) return nil, nil, nil, fmt.Errorf("create cannon trace provider: %w", err)
} }
updater, err := cannon.NewOracleUpdater(ctx, logger, txMgr, addr, client) updater, err := cannon.NewOracleUpdater(ctx, logger, txMgr, addr, client)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to create the cannon updater: %w", err) return nil, nil, nil, fmt.Errorf("failed to create the cannon updater: %w", err)
} }
return provider, updater, nil validator := func(ctx context.Context, contract *contracts.FaultDisputeGameContract) error {
return ValidateAbsolutePrestate(ctx, provider, contract)
}
return trace.NewSimpleTraceAccessor(provider), updater, validator, nil
} }
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
return NewGamePlayer(ctx, logger, m, cfg, dir, game.Proxy, txMgr, client, resourceCreator) return NewGamePlayer(ctx, logger, m, cfg, dir, game.Proxy, txMgr, client, resourceCreator)
...@@ -54,10 +58,13 @@ func RegisterGameTypes( ...@@ -54,10 +58,13 @@ func RegisterGameTypes(
registry.RegisterGameType(cannonGameType, playerCreator) registry.RegisterGameType(cannonGameType, playerCreator)
} }
if cfg.TraceTypeEnabled(config.TraceTypeAlphabet) { if cfg.TraceTypeEnabled(config.TraceTypeAlphabet) {
resourceCreator := func(addr common.Address, contract *contracts.FaultDisputeGameContract, gameDepth uint64, dir string) (faultTypes.TraceProvider, faultTypes.OracleUpdater, error) { resourceCreator := func(addr common.Address, contract *contracts.FaultDisputeGameContract, gameDepth uint64, dir string) (faultTypes.TraceAccessor, faultTypes.OracleUpdater, gameValidator, error) {
provider := alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth) provider := alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
updater := alphabet.NewOracleUpdater(logger) updater := alphabet.NewOracleUpdater(logger)
return provider, updater, nil validator := func(ctx context.Context, contract *contracts.FaultDisputeGameContract) error {
return ValidateAbsolutePrestate(ctx, provider, contract)
}
return trace.NewSimpleTraceAccessor(provider), updater, validator, nil
} }
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
return NewGamePlayer(ctx, logger, m, cfg, dir, game.Proxy, txMgr, client, resourceCreator) return NewGamePlayer(ctx, logger, m, cfg, dir, game.Proxy, txMgr, client, resourceCreator)
......
package responder
import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
// setupFaultDisputeGame deploys the FaultDisputeGame contract to a simulated backend
func setupFaultDisputeGame() (common.Address, *bind.TransactOpts, *backends.SimulatedBackend, *bindings.FaultDisputeGame, error) {
privateKey, err := crypto.GenerateKey()
from := crypto.PubkeyToAddress(privateKey.PublicKey)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
opts, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337))
if err != nil {
return common.Address{}, nil, nil, nil, err
}
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{from: {Balance: big.NewInt(params.Ether)}},
50_000_000,
)
blockHashOracle, _, _, err := bindings.DeployBlockOracle(opts, backend)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
_, _, contract, err := bindings.DeployFaultDisputeGame(
opts,
backend,
uint8(0), // Game Type ID
[32]byte{0x01}, // Absolute Prestate Claim
big.NewInt(15), // Max Game Depth
uint64(604800), // 7 days
common.Address{0xdd}, // VM
common.Address{0xee}, // L2OutputOracle (Not used in Alphabet Game)
blockHashOracle, // Block hash oracle
)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
return from, opts, backend, contract, nil
}
// TestBuildFaultDefendData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultDefendData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
resp, _ := newTestFaultResponder(t)
data, err := resp.buildFaultDefendData(1, [32]byte{0x02, 0x03})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Defend(opts, big.NewInt(1), [32]byte{0x02, 0x03})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
// TestBuildFaultAttackData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultAttackData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
resp, _ := newTestFaultResponder(t)
data, err := resp.buildFaultAttackData(1, [32]byte{0x02, 0x03})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Attack(opts, big.NewInt(1), [32]byte{0x02, 0x03})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
// TestBuildFaultStepData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultStepData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
resp, _ := newTestFaultResponder(t)
data, err := resp.buildStepTxData(2, false, []byte{0x01}, []byte{0x02})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Step(opts, big.NewInt(2), false, []byte{0x01}, []byte{0x02})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
...@@ -2,153 +2,97 @@ package responder ...@@ -2,153 +2,97 @@ package responder
import ( import (
"context" "context"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type GameContract interface {
CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error)
CallResolveClaim(ctx context.Context, claimIdx uint64) error
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error)
}
// FaultResponder implements the [Responder] interface to send onchain transactions. // FaultResponder implements the [Responder] interface to send onchain transactions.
type FaultResponder struct { type FaultResponder struct {
log log.Logger log log.Logger
txMgr txmgr.TxManager txMgr txmgr.TxManager
contract GameContract
fdgAddr common.Address
fdgAbi *abi.ABI
} }
// NewFaultResponder returns a new [FaultResponder]. // NewFaultResponder returns a new [FaultResponder].
func NewFaultResponder(logger log.Logger, txManagr txmgr.TxManager, fdgAddr common.Address) (*FaultResponder, error) { func NewFaultResponder(logger log.Logger, txMgr txmgr.TxManager, contract GameContract) (*FaultResponder, error) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
if err != nil {
return nil, err
}
return &FaultResponder{ return &FaultResponder{
log: logger, log: logger,
txMgr: txManagr, txMgr: txMgr,
fdgAddr: fdgAddr, contract: contract,
fdgAbi: fdgAbi,
}, nil }, nil
} }
// buildFaultDefendData creates the transaction data for the Defend function.
func (r *FaultResponder) buildFaultDefendData(parentContractIndex int, pivot [32]byte) ([]byte, error) {
return r.fdgAbi.Pack(
"defend",
big.NewInt(int64(parentContractIndex)),
pivot,
)
}
// buildFaultAttackData creates the transaction data for the Attack function.
func (r *FaultResponder) buildFaultAttackData(parentContractIndex int, pivot [32]byte) ([]byte, error) {
return r.fdgAbi.Pack(
"attack",
big.NewInt(int64(parentContractIndex)),
pivot,
)
}
// buildResolveData creates the transaction data for the Resolve function.
func (r *FaultResponder) buildResolveData() ([]byte, error) {
return r.fdgAbi.Pack("resolve")
}
// CallResolve determines if the resolve function on the fault dispute game contract // CallResolve determines if the resolve function on the fault dispute game contract
// would succeed. Returns the game status if the call would succeed, errors otherwise. // would succeed. Returns the game status if the call would succeed, errors otherwise.
func (r *FaultResponder) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) { func (r *FaultResponder) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) {
txData, err := r.buildResolveData() return r.contract.CallResolve(ctx)
if err != nil {
return gameTypes.GameStatusInProgress, err
}
res, err := r.txMgr.Call(ctx, ethereum.CallMsg{
To: &r.fdgAddr,
Data: txData,
}, nil)
if err != nil {
return gameTypes.GameStatusInProgress, err
}
var status uint8
if err = r.fdgAbi.UnpackIntoInterface(&status, "resolve", res); err != nil {
return gameTypes.GameStatusInProgress, err
}
return gameTypes.GameStatusFromUint8(status)
} }
// Resolve executes a resolve transaction to resolve a fault dispute game. // Resolve executes a resolve transaction to resolve a fault dispute game.
func (r *FaultResponder) Resolve(ctx context.Context) error { func (r *FaultResponder) Resolve(ctx context.Context) error {
txData, err := r.buildResolveData() candidate, err := r.contract.ResolveTx()
if err != nil { if err != nil {
return err return err
} }
return r.sendTxAndWait(ctx, txData) return r.sendTxAndWait(ctx, candidate)
}
// buildResolveClaimData creates the transaction data for the ResolveClaim function.
func (r *FaultResponder) buildResolveClaimData(claimIdx uint64) ([]byte, error) {
return r.fdgAbi.Pack("resolveClaim", big.NewInt(int64(claimIdx)))
} }
// CallResolveClaim determines if the resolveClaim function on the fault dispute game contract // CallResolveClaim determines if the resolveClaim function on the fault dispute game contract
// would succeed. // would succeed.
func (r *FaultResponder) CallResolveClaim(ctx context.Context, claimIdx uint64) error { func (r *FaultResponder) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
txData, err := r.buildResolveClaimData(claimIdx) return r.contract.CallResolveClaim(ctx, claimIdx)
if err != nil {
return err
}
_, err = r.txMgr.Call(ctx, ethereum.CallMsg{
To: &r.fdgAddr,
Data: txData,
}, nil)
return err
} }
// ResolveClaim executes a resolveClaim transaction to resolve a fault dispute game. // ResolveClaim executes a resolveClaim transaction to resolve a fault dispute game.
func (r *FaultResponder) ResolveClaim(ctx context.Context, claimIdx uint64) error { func (r *FaultResponder) ResolveClaim(ctx context.Context, claimIdx uint64) error {
txData, err := r.buildResolveClaimData(claimIdx) candidate, err := r.contract.ResolveClaimTx(claimIdx)
if err != nil { if err != nil {
return err return err
} }
return r.sendTxAndWait(ctx, txData) return r.sendTxAndWait(ctx, candidate)
} }
func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action) error { func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action) error {
var txData []byte var candidate txmgr.TxCandidate
var err error var err error
switch action.Type { switch action.Type {
case types.ActionTypeMove: case types.ActionTypeMove:
if action.IsAttack { if action.IsAttack {
txData, err = r.buildFaultAttackData(action.ParentIdx, action.Value) candidate, err = r.contract.AttackTx(uint64(action.ParentIdx), action.Value)
} else { } else {
txData, err = r.buildFaultDefendData(action.ParentIdx, action.Value) candidate, err = r.contract.DefendTx(uint64(action.ParentIdx), action.Value)
} }
case types.ActionTypeStep: case types.ActionTypeStep:
txData, err = r.buildStepTxData(uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData) candidate, err = r.contract.StepTx(uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData)
} }
if err != nil { if err != nil {
return err return err
} }
return r.sendTxAndWait(ctx, txData) return r.sendTxAndWait(ctx, candidate)
} }
// sendTxAndWait sends a transaction through the [txmgr] and waits for a receipt. // sendTxAndWait sends a transaction through the [txmgr] and waits for a receipt.
// This sets the tx GasLimit to 0, performing gas estimation online through the [txmgr]. // This sets the tx GasLimit to 0, performing gas estimation online through the [txmgr].
func (r *FaultResponder) sendTxAndWait(ctx context.Context, txData []byte) error { func (r *FaultResponder) sendTxAndWait(ctx context.Context, candidate txmgr.TxCandidate) error {
receipt, err := r.txMgr.Send(ctx, txmgr.TxCandidate{ receipt, err := r.txMgr.Send(ctx, candidate)
To: &r.fdgAddr,
TxData: txData,
GasLimit: 0,
})
if err != nil { if err != nil {
return err return err
} }
...@@ -159,14 +103,3 @@ func (r *FaultResponder) sendTxAndWait(ctx context.Context, txData []byte) error ...@@ -159,14 +103,3 @@ func (r *FaultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
} }
return nil return nil
} }
// buildStepTxData creates the transaction data for the step function.
func (r *FaultResponder) buildStepTxData(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) ([]byte, error) {
return r.fdgAbi.Pack(
"step",
big.NewInt(int64(claimIdx)),
isAttack,
stateData,
proof,
)
}
...@@ -3,7 +3,6 @@ package cannon ...@@ -3,7 +3,6 @@ package cannon
import ( import (
"context" "context"
"errors" "errors"
"math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
...@@ -11,7 +10,6 @@ import ( ...@@ -11,7 +10,6 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -47,10 +45,6 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) ( ...@@ -47,10 +45,6 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (
), nil ), nil
} }
func (m *mockTxManager) Call(_ context.Context, _ ethereum.CallMsg, _ *big.Int) ([]byte, error) {
panic("not implemented")
}
func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) { func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) {
panic("not implemented") panic("not implemented")
} }
......
package trace
import (
"context"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
)
type translatingProvider struct {
parentDepth uint64
provider types.TraceProvider
}
func Translate(provider types.TraceProvider, parentDepth uint64) types.TraceProvider {
return &translatingProvider{
parentDepth: parentDepth,
provider: provider,
}
}
func (p translatingProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.parentDepth)
if err != nil {
return common.Hash{}, err
}
return p.provider.Get(ctx, relativePos)
}
func (p translatingProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.parentDepth)
if err != nil {
return nil, nil, nil, err
}
return p.provider.GetStepData(ctx, relativePos)
}
func (p translatingProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
return p.provider.AbsolutePreStateCommitment(ctx)
}
var _ types.TraceProvider = (*translatingProvider)(nil)
package trace
import (
"context"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/stretchr/testify/require"
)
func TestTranslate(t *testing.T) {
orig := alphabet.NewTraceProvider("abcdefghij", 4)
translated := Translate(orig, 3)
// All nodes on the first translated layer, map to GIndex 1
for i := int64(8); i <= 15; i++ {
requireSameValue(t, orig, 1, translated, i)
}
// Nodes on the second translated layer map to GIndex 2 and 3 alternately
for i := int64(16); i <= 31; i += 2 {
requireSameValue(t, orig, 2, translated, i)
requireSameValue(t, orig, 3, translated, i+1)
}
// Nodes on the third translated layer map to GIndex 4, 5, 6 and 7
for i := int64(32); i <= 61; i += 4 {
requireSameValue(t, orig, 4, translated, i)
requireSameValue(t, orig, 5, translated, i+1)
requireSameValue(t, orig, 6, translated, i+2)
requireSameValue(t, orig, 7, translated, i+3)
}
}
func requireSameValue(t *testing.T, a types.TraceProvider, aGIdx int64, b types.TraceProvider, bGIdx int64) {
// Check Get returns the same results
aValue, err := a.Get(context.Background(), types.NewPositionFromGIndex(big.NewInt(aGIdx)))
require.NoError(t, err)
bValue, err := b.Get(context.Background(), types.NewPositionFromGIndex(big.NewInt(bGIdx)))
require.NoError(t, err)
require.Equal(t, aValue, bValue)
// Check GetStepData returns the same results
aPrestate, aProofData, aPreimageData, err := a.GetStepData(context.Background(), types.NewPositionFromGIndex(big.NewInt(aGIdx)))
require.NoError(t, err)
bPrestate, bProofData, bPreimageData, err := b.GetStepData(context.Background(), types.NewPositionFromGIndex(big.NewInt(bGIdx)))
require.NoError(t, err)
require.Equal(t, aPrestate, bPrestate)
require.Equal(t, aProofData, bProofData)
require.Equal(t, aPreimageData, bPreimageData)
}
func TestTranslate_AbsolutePreStateCommitment(t *testing.T) {
orig := alphabet.NewTraceProvider("abcdefghij", 4)
translated := Translate(orig, 3)
origValue, err := orig.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
translatedValue, err := translated.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, origValue, translatedValue)
}
...@@ -48,9 +48,6 @@ type fakeTxMgr struct { ...@@ -48,9 +48,6 @@ type fakeTxMgr struct {
func (f fakeTxMgr) From() common.Address { func (f fakeTxMgr) From() common.Address {
return f.from return f.from
} }
func (f fakeTxMgr) Call(_ context.Context, _ ethereum.CallMsg, _ *big.Int) ([]byte, error) {
panic("unimplemented")
}
func (f fakeTxMgr) BlockNumber(_ context.Context) (uint64, error) { func (f fakeTxMgr) BlockNumber(_ context.Context) (uint64, error) {
panic("unimplemented") panic("unimplemented")
} }
......
...@@ -28,7 +28,7 @@ var Subcommands = cli.Commands{ ...@@ -28,7 +28,7 @@ var Subcommands = cli.Commands{
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "deploy-config", Name: "deploy-config",
Usage: "Path to hardhat deploy config file", Usage: "Path to deploy config file",
Required: true, Required: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
...@@ -91,18 +91,26 @@ var Subcommands = cli.Commands{ ...@@ -91,18 +91,26 @@ var Subcommands = cli.Commands{
{ {
Name: "l2", Name: "l2",
Usage: "Generates an L2 genesis file and rollup config suitable for a deployed network", Usage: "Generates an L2 genesis file and rollup config suitable for a deployed network",
Description: "Generating the L2 genesis depends on knowledge of L1 contract addresses for the bridge to be secure. " +
"A deploy config and either a deployment directory or an L1 deployments file are used to create the L2 genesis. " +
"The deploy directory and L1 deployments file are generated by the L1 contract deployments.",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "l1-rpc", Name: "l1-rpc",
Usage: "L1 RPC URL", Usage: "L1 RPC URL",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "deploy-config", Name: "deploy-config",
Usage: "Path to deploy config file", Usage: "Path to deploy config file",
Required: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "deployment-dir", Name: "deployment-dir",
Usage: "Path to network deployment directory", Usage: "Path to network deployment directory. Cannot be used with --l1-deployments",
},
&cli.StringFlag{
Name: "l1-deployments",
Usage: "Path to L1 deployments JSON file. Cannot be used with --deployment-dir",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "outfile.l2", Name: "outfile.l2",
...@@ -122,20 +130,36 @@ var Subcommands = cli.Commands{ ...@@ -122,20 +130,36 @@ var Subcommands = cli.Commands{
} }
deployDir := ctx.String("deployment-dir") deployDir := ctx.String("deployment-dir")
if deployDir == "" { l1Deployments := ctx.String("l1-deployments")
return errors.New("Must specify --deployment-dir")
if deployDir != "" && l1Deployments != "" {
return errors.New("cannot specify both --deployment-dir and --l1-deployments")
}
if deployDir == "" && l1Deployments == "" {
return errors.New("must specify either --deployment-dir or --l1-deployments")
} }
log.Info("Deployment directory", "path", deployDir) if deployDir != "" {
depPath, network := filepath.Split(deployDir) log.Info("Deployment directory", "path", deployDir)
hh, err := hardhat.New(network, nil, []string{depPath}) depPath, network := filepath.Split(deployDir)
if err != nil { hh, err := hardhat.New(network, nil, []string{depPath})
return err if err != nil {
return err
}
// Read the appropriate deployment addresses from disk
if err := config.GetDeployedAddresses(hh); err != nil {
return err
}
} }
// Read the appropriate deployment addresses from disk if l1Deployments != "" {
if err := config.GetDeployedAddresses(hh); err != nil { log.Info("L1 deployments", "path", l1Deployments)
return err deployments, err := genesis.NewL1Deployments(l1Deployments)
if err != nil {
return err
}
config.SetDeployments(deployments)
} }
client, err := ethclient.Dial(ctx.String("l1-rpc")) client, err := ethclient.Dial(ctx.String("l1-rpc"))
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -86,6 +87,17 @@ func toCallArg(msg ethereum.CallMsg) interface{} { ...@@ -86,6 +87,17 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
return arg return arg
} }
func (c *ContractCall) ToTxCandidate() (txmgr.TxCandidate, error) {
data, err := c.Pack()
if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to pack arguments: %w", err)
}
return txmgr.TxCandidate{
TxData: data,
To: &c.Addr,
}, nil
}
type CallResult struct { type CallResult struct {
out []interface{} out []interface{}
} }
......
...@@ -30,6 +30,22 @@ func TestContractCall_ToCallArgs(t *testing.T) { ...@@ -30,6 +30,22 @@ func TestContractCall_ToCallArgs(t *testing.T) {
require.NotContains(t, argMap, "gasPrice") require.NotContains(t, argMap, "gasPrice")
} }
func TestContractCall_ToTxCandidate(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
call := NewContractCall(testAbi, addr, "approve", common.Address{0xcc}, big.NewInt(1234444))
candidate, err := call.ToTxCandidate()
require.NoError(t, err)
require.Equal(t, candidate.To, &addr)
expectedData, err := call.Pack()
require.NoError(t, err)
require.Equal(t, candidate.TxData, expectedData)
require.Nil(t, candidate.Value)
require.Zero(t, candidate.GasLimit)
}
func TestContractCall_Pack(t *testing.T) { func TestContractCall_Pack(t *testing.T) {
addr := common.Address{0xbd} addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi() testAbi, err := bindings.ERC20MetaData.GetAbi()
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -73,6 +74,11 @@ func (l *AbiBasedRpc) BatchCallContext(ctx context.Context, b []rpc.BatchElem) e ...@@ -73,6 +74,11 @@ func (l *AbiBasedRpc) BatchCallContext(ctx context.Context, b []rpc.BatchElem) e
return errors.Join(errs...) return errors.Join(errs...)
} }
func (l *AbiBasedRpc) VerifyTxCandidate(candidate txmgr.TxCandidate) {
require.EqualValues(l.t, &l.addr, candidate.To, "Incorrect To address")
l.findExpectedCall(candidate.TxData, batching.BlockLatest.ArgValue())
}
func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method string, args ...interface{}) error { func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method string, args ...interface{}) error {
require.Equal(l.t, "eth_call", method) require.Equal(l.t, "eth_call", method)
require.Len(l.t, args, 2) require.Len(l.t, args, 2)
...@@ -82,11 +88,27 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str ...@@ -82,11 +88,27 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str
require.Equal(l.t, &l.addr, callOpts["to"]) require.Equal(l.t, &l.addr, callOpts["to"])
data, ok := callOpts["input"].(hexutil.Bytes) data, ok := callOpts["input"].(hexutil.Bytes)
require.True(l.t, ok) require.True(l.t, ok)
call, abiMethod := l.findExpectedCall(data, actualBlockRef)
output, err := abiMethod.Outputs.Pack(call.outputs...)
require.NoErrorf(l.t, err, "Invalid outputs for method %v: %v", abiMethod.Name, call.outputs)
// I admit I do not understand Go reflection.
// So leverage json.Unmarshal to set the out value correctly.
j, err := json.Marshal(hexutil.Bytes(output))
require.NoError(l.t, err)
require.NoError(l.t, json.Unmarshal(j, out))
return nil
}
func (l *AbiBasedRpc) findExpectedCall(data []byte, actualBlockRef interface{}) (*expectedCall, *abi.Method) {
abiMethod, err := l.abi.MethodById(data[0:4]) abiMethod, err := l.abi.MethodById(data[0:4])
require.NoError(l.t, err) require.NoError(l.t, err)
argData := data[4:] argData := data[4:]
args, err = abiMethod.Inputs.Unpack(argData) args, err := abiMethod.Inputs.Unpack(argData)
require.NoError(l.t, err) require.NoError(l.t, err)
require.Len(l.t, args, len(abiMethod.Inputs)) require.Len(l.t, args, len(abiMethod.Inputs))
...@@ -100,14 +122,5 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str ...@@ -100,14 +122,5 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str
} }
} }
require.NotNilf(l.t, call, "No expected calls to %v at block %v with arguments: %v\nExpected calls: %v", abiMethod.Name, actualBlockRef, args, expectedCalls) require.NotNilf(l.t, call, "No expected calls to %v at block %v with arguments: %v\nExpected calls: %v", abiMethod.Name, actualBlockRef, args, expectedCalls)
return call, abiMethod
output, err := abiMethod.Outputs.Pack(call.outputs...)
require.NoErrorf(l.t, err, "Invalid outputs for method %v: %v", abiMethod.Name, call.outputs)
// I admit I do not understand Go reflection.
// So leverage json.Unmarshal to set the out value correctly.
j, err := json.Marshal(hexutil.Bytes(output))
require.NoError(l.t, err)
require.NoError(l.t, json.Unmarshal(j, out))
return nil
} }
...@@ -51,20 +51,21 @@ func NewEngineClient(client client.RPC, log log.Logger, metrics caching.Metrics, ...@@ -51,20 +51,21 @@ func NewEngineClient(client client.RPC, log log.Logger, metrics caching.Metrics,
// 2. `error` as eth.InputError: the forkchoice state or attributes are not valid. // 2. `error` as eth.InputError: the forkchoice state or attributes are not valid.
// 3. Other types of `error`: temporary RPC errors, like timeouts. // 3. Other types of `error`: temporary RPC errors, like timeouts.
func (s *EngineClient) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState, attributes *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) { func (s *EngineClient) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState, attributes *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) {
e := s.log.New("state", fc, "attr", attributes) llog := s.log.New("state", fc) // local logger
e.Trace("Sharing forkchoice-updated signal") tlog := llog.New("attr", attributes) // trace logger
tlog.Trace("Sharing forkchoice-updated signal")
fcCtx, cancel := context.WithTimeout(ctx, time.Second*5) fcCtx, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel() defer cancel()
var result eth.ForkchoiceUpdatedResult var result eth.ForkchoiceUpdatedResult
err := s.client.CallContext(fcCtx, &result, "engine_forkchoiceUpdatedV2", fc, attributes) err := s.client.CallContext(fcCtx, &result, "engine_forkchoiceUpdatedV2", fc, attributes)
if err == nil { if err == nil {
e.Trace("Shared forkchoice-updated signal") tlog.Trace("Shared forkchoice-updated signal")
if attributes != nil { // block building is optional, we only get a payload ID if we are building a block if attributes != nil { // block building is optional, we only get a payload ID if we are building a block
e.Trace("Received payload id", "payloadId", result.PayloadID) tlog.Trace("Received payload id", "payloadId", result.PayloadID)
} }
return &result, nil return &result, nil
} else { } else {
e.Warn("Failed to share forkchoice-updated signal", "err", err) llog.Warn("Failed to share forkchoice-updated signal", "err", err)
if rpcErr, ok := err.(rpc.Error); ok { if rpcErr, ok := err.(rpc.Error); ok {
code := eth.ErrorCode(rpcErr.ErrorCode()) code := eth.ErrorCode(rpcErr.ErrorCode())
switch code { switch code {
......
{"name":"post-shanghai-bad-receipts-hash","fail":true, "reason":"failed to verify block hash"}
{"name":"post-shanghai-bad-transactions-hash","fail":true, "reason":"failed to verify block hash"}
{"name":"post-shanghai-bad-transactions-nil","fail":true, "reason": "block tx 0 is nil"}
{"name":"post-shanghai-bad-transactions","fail":true} {"name":"post-shanghai-bad-transactions","fail":true, "reason": "failed to verify transactions list"}
{"name":"post-shanghai-bad-withdrawals-hash","fail":true, "reason":"failed to verify block hash"}
{"name":"post-shanghai-bad-withdrawals-nil","fail":true, "reason": "block withdrawal 0 is null"}
{"name":"post-shanghai-bad-withdrawals","fail":true} {"name":"post-shanghai-bad-withdrawals","fail":true,"reason": "failed to verify withdrawals list"}
{"name":"pre-shanghai-bad-receipts-hash","fail":true, "reason":"failed to verify block hash"}
{"name":"pre-shanghai-bad-transactions-hash","fail":true, "reason":"failed to verify block hash"}
{"name":"pre-shanghai-bad-transactions","fail":true} {"name":"pre-shanghai-bad-transactions","fail":true,"reason": "failed to verify transactions list"}
...@@ -62,18 +62,8 @@ generate_test_vector() { ...@@ -62,18 +62,8 @@ generate_test_vector() {
$mutation_func "$data_file" $metadata_file $mutation_func "$data_file" $metadata_file
} }
mkdir -p data/blocks
mkdir -p data/headers mkdir -p data/headers
# Blocks
generate_test_vector "pre-shanghai-success" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" true success_case
generate_test_vector "pre-shanghai-bad-transactions" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" true bad_transactions_root
generate_test_vector "pre-shanghai-bad-receipts" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" true bad_receipts_root
generate_test_vector "post-shanghai-success" "0xa16c6bcda4fdca88b5761965c4d724f7afc6a6900d9051a204e544870adb3452" true success_case
generate_test_vector "post-shanghai-bad-withdrawals" "0xa16c6bcda4fdca88b5761965c4d724f7afc6a6900d9051a204e544870adb3452" true bad_withdrawals_root
generate_test_vector "post-shanghai-bad-transactions" "0xa16c6bcda4fdca88b5761965c4d724f7afc6a6900d9051a204e544870adb3452" true bad_transactions_root
generate_test_vector "post-shanghai-bad-receipts" "0xa16c6bcda4fdca88b5761965c4d724f7afc6a6900d9051a204e544870adb3452" true bad_receipts_root
# Headers # Headers
generate_test_vector "pre-shanghai-success" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" false success_case generate_test_vector "pre-shanghai-success" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" false success_case
generate_test_vector "pre-shanghai-bad-transactions" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" false bad_transactions_root generate_test_vector "pre-shanghai-bad-transactions" "0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663" false bad_transactions_root
......
...@@ -13,8 +13,9 @@ import ( ...@@ -13,8 +13,9 @@ import (
var blocksTestdata embed.FS var blocksTestdata embed.FS
type testMetadata struct { type testMetadata struct {
Name string `json:"name"` Name string `json:"name"`
Fail bool `json:"fail,omitempty"` Fail bool `json:"fail,omitempty"`
Reason string `json:"reason,omitempty"`
} }
func readJsonTestdata(t *testing.T, name string, dest any) { func readJsonTestdata(t *testing.T, name string, dest any) {
...@@ -67,6 +68,7 @@ func TestBlockJSON(t *testing.T) { ...@@ -67,6 +68,7 @@ func TestBlockJSON(t *testing.T) {
err := block.verify() err := block.verify()
if metadata.Fail { if metadata.Fail {
require.NotNil(t, err, "expecting verification error") require.NotNil(t, err, "expecting verification error")
require.ErrorContains(t, err, metadata.Reason, "validation failed for incorrect reason")
} else { } else {
require.NoError(t, err, "verification should pass") require.NoError(t, err, "verification should pass")
} }
......
...@@ -4,12 +4,9 @@ package mocks ...@@ -4,12 +4,9 @@ package mocks
import ( import (
context "context" context "context"
big "math/big"
common "github.com/ethereum/go-ethereum/common" common "github.com/ethereum/go-ethereum/common"
ethereum "github.com/ethereum/go-ethereum"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
txmgr "github.com/ethereum-optimism/optimism/op-service/txmgr" txmgr "github.com/ethereum-optimism/optimism/op-service/txmgr"
...@@ -46,32 +43,6 @@ func (_m *TxManager) BlockNumber(ctx context.Context) (uint64, error) { ...@@ -46,32 +43,6 @@ func (_m *TxManager) BlockNumber(ctx context.Context) (uint64, error) {
return r0, r1 return r0, r1
} }
// Call provides a mock function with given fields: ctx, msg, blockNumber
func (_m *TxManager) Call(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
ret := _m.Called(ctx, msg, blockNumber)
var r0 []byte
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error)); ok {
return rf(ctx, msg, blockNumber)
}
if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) []byte); ok {
r0 = rf(ctx, msg, blockNumber)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg, *big.Int) error); ok {
r1 = rf(ctx, msg, blockNumber)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// From provides a mock function with given fields: // From provides a mock function with given fields:
func (_m *TxManager) From() common.Address { func (_m *TxManager) From() common.Address {
ret := _m.Called() ret := _m.Called()
......
...@@ -43,10 +43,6 @@ type TxManager interface { ...@@ -43,10 +43,6 @@ type TxManager interface {
// NOTE: Send can be called concurrently, the nonce will be managed internally. // NOTE: Send can be called concurrently, the nonce will be managed internally.
Send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error) Send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error)
// Call is used to call a contract.
// Internally, it uses the [ethclient.Client.CallContract] method.
Call(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
// From returns the sending address associated with the instance of the transaction manager. // From returns the sending address associated with the instance of the transaction manager.
// It is static for a single instance of a TxManager. // It is static for a single instance of a TxManager.
From() common.Address From() common.Address
...@@ -169,12 +165,6 @@ func (m *SimpleTxManager) Send(ctx context.Context, candidate TxCandidate) (*typ ...@@ -169,12 +165,6 @@ func (m *SimpleTxManager) Send(ctx context.Context, candidate TxCandidate) (*typ
return receipt, err return receipt, err
} }
// Call is used to call a contract.
// Internally, it uses the [ethclient.Client.CallContract] method.
func (m *SimpleTxManager) Call(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
return m.backend.CallContract(ctx, msg, blockNumber)
}
// send performs the actual transaction creation and sending. // send performs the actual transaction creation and sending.
func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error) { func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error) {
if m.cfg.TxSendTimeout != 0 { if m.cfg.TxSendTimeout != 0 {
......
This diff is collapsed.
...@@ -6,8 +6,9 @@ import { ...@@ -6,8 +6,9 @@ import {
validators, validators,
} from '@eth-optimism/common-ts' } from '@eth-optimism/common-ts'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { ethers } from 'ethers' import { BigNumber, ethers } from 'ethers'
import Safe from '../abi/IGnosisSafe.0.8.19.json'
import { version } from '../../package.json' import { version } from '../../package.json'
type BalanceMonOptions = { type BalanceMonOptions = {
...@@ -17,11 +18,12 @@ type BalanceMonOptions = { ...@@ -17,11 +18,12 @@ type BalanceMonOptions = {
type BalanceMonMetrics = { type BalanceMonMetrics = {
balances: Gauge balances: Gauge
safeNonces: Gauge
unexpectedRpcErrors: Counter unexpectedRpcErrors: Counter
} }
type BalanceMonState = { type BalanceMonState = {
accounts: Array<{ address: string; nickname: string }> accounts: Array<{ address: string; nickname: string; safe: boolean }>
} }
export class BalanceMonService extends BaseServiceV2< export class BalanceMonService extends BaseServiceV2<
...@@ -45,7 +47,7 @@ export class BalanceMonService extends BaseServiceV2< ...@@ -45,7 +47,7 @@ export class BalanceMonService extends BaseServiceV2<
}, },
accounts: { accounts: {
validator: validators.str, validator: validators.str,
desc: 'JSON array of [{ address, nickname }] to monitor balances of', desc: 'JSON array of [{ address, nickname, safe }] to monitor balances and nonces of',
public: true, public: true,
}, },
}, },
...@@ -55,6 +57,11 @@ export class BalanceMonService extends BaseServiceV2< ...@@ -55,6 +57,11 @@ export class BalanceMonService extends BaseServiceV2<
desc: 'Balances of addresses', desc: 'Balances of addresses',
labels: ['address', 'nickname'], labels: ['address', 'nickname'],
}, },
safeNonces: {
type: Gauge,
desc: 'Safe nonce',
labels: ['address', 'nickname'],
},
unexpectedRpcErrors: { unexpectedRpcErrors: {
type: Counter, type: Counter,
desc: 'Number of unexpected RPC errors', desc: 'Number of unexpected RPC errors',
...@@ -70,9 +77,21 @@ export class BalanceMonService extends BaseServiceV2< ...@@ -70,9 +77,21 @@ export class BalanceMonService extends BaseServiceV2<
protected async main(): Promise<void> { protected async main(): Promise<void> {
for (const account of this.state.accounts) { for (const account of this.state.accounts) {
let balance: ethers.BigNumber
try { try {
balance = await this.options.rpc.getBalance(account.address) const balance = await this.options.rpc.getBalance(account.address)
this.logger.info(`got balance`, {
address: account.address,
nickname: account.nickname,
balance: balance.toString(),
})
// Parse the balance as an integer instead of via toNumber() to avoid ethers throwing an
// an error. We might get rounding errors but we don't need perfect precision here, just a
// generally accurate sense for what the current balance is.
this.metrics.balances.set(
{ address: account.address, nickname: account.nickname },
parseInt(balance.toString(), 10)
)
} catch (err) { } catch (err) {
this.logger.info(`got unexpected RPC error`, { this.logger.info(`got unexpected RPC error`, {
section: 'balances', section: 'balances',
...@@ -83,22 +102,39 @@ export class BalanceMonService extends BaseServiceV2< ...@@ -83,22 +102,39 @@ export class BalanceMonService extends BaseServiceV2<
section: 'balances', section: 'balances',
name: 'getBalance', name: 'getBalance',
}) })
continue
} }
this.logger.info(`got balance`, { // Get the safe nonce to report
address: account.address, if (account.safe) {
nickname: account.nickname, try {
balance: balance.toString(), const safeContract = new ethers.Contract(
}) account.address,
Safe.abi,
this.options.rpc
)
const safeNonce = BigNumber.from(await safeContract.nonce())
this.logger.info(`got nonce`, {
address: account.address,
nickname: account.nickname,
nonce: safeNonce.toString(),
})
// Parse the balance as an integer instead of via toNumber() to avoid ethers throwing an this.metrics.safeNonces.set(
// an error. We might get rounding errors but we don't need perfect precision here, just a { address: account.address, nickname: account.nickname },
// generally accurate sense for what the current balance is. parseInt(safeNonce.toString(), 10)
this.metrics.balances.set( )
{ address: account.address, nickname: account.nickname }, } catch (err) {
parseInt(balance.toString(), 10) this.logger.info(`got unexpected RPC error`, {
) section: 'safeNonce',
name: 'getSafeNonce',
err,
})
this.metrics.unexpectedRpcErrors.inc({
section: 'safeNonce',
name: 'getSafeNonce',
})
}
}
} }
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
}, },
"include": [ "include": [
"package.json", "package.json",
"src/abi/IGnosisSafe.0.8.19.json",
"src/**/*" "src/**/*"
] ]
} }
...@@ -672,8 +672,8 @@ SequencerFeeVault_Test:test_withdraw_toL1_succeeds() (gas: 618343) ...@@ -672,8 +672,8 @@ SequencerFeeVault_Test:test_withdraw_toL1_succeeds() (gas: 618343)
SetPrevBaseFee_Test:test_setPrevBaseFee_succeeds() (gas: 11595) SetPrevBaseFee_Test:test_setPrevBaseFee_succeeds() (gas: 11595)
StandardBridge_Stateless_Test:test_isCorrectTokenPair_succeeds() (gas: 50149) StandardBridge_Stateless_Test:test_isCorrectTokenPair_succeeds() (gas: 50149)
StandardBridge_Stateless_Test:test_isOptimismMintableERC20_succeeds() (gas: 33142) StandardBridge_Stateless_Test:test_isOptimismMintableERC20_succeeds() (gas: 33142)
Storage_Roundtrip_Test:test_setGetAddress_succeeds(bytes32,address) (runs: 64, μ: 31532, ~: 31843) Storage_Roundtrip_Test:test_setGetAddress_succeeds(bytes32,address) (runs: 64, μ: 31221, ~: 31843)
Storage_Roundtrip_Test:test_setGetBytes32_succeeds(bytes32,bytes32) (runs: 64, μ: 31620, ~: 31620) Storage_Roundtrip_Test:test_setGetBytes32_succeeds(bytes32,bytes32) (runs: 64, μ: 31654, ~: 31654)
Storage_Roundtrip_Test:test_setGetUint_succeeds(bytes32,uint256) (runs: 64, μ: 30731, ~: 31664) Storage_Roundtrip_Test:test_setGetUint_succeeds(bytes32,uint256) (runs: 64, μ: 30731, ~: 31664)
SystemConfig_Initialize_Test:test_initialize_events_succeeds() (gas: 88186) SystemConfig_Initialize_Test:test_initialize_events_succeeds() (gas: 88186)
SystemConfig_Initialize_Test:test_initialize_startBlockNoop_reverts() (gas: 77216) SystemConfig_Initialize_Test:test_initialize_startBlockNoop_reverts() (gas: 77216)
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { DeployConfig } from "scripts/DeployConfig.s.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { Constants } from "src/libraries/Constants.sol";
import { L1StandardBridge } from "src/L1/L1StandardBridge.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { ProtocolVersion, ProtocolVersions } from "src/L1/ProtocolVersions.sol";
import { OptimismPortal } from "src/L1/OptimismPortal.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol";
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Types } from "scripts/Types.sol";
import { Vm } from "forge-std/Vm.sol";
import { ISystemConfigV0 } from "scripts/interfaces/ISystemConfigV0.sol";
library ChainAssertions {
/// @notice Asserts the correctness of an L1 deployment
function postDeployAssertions(
Types.ContractSet memory prox,
DeployConfig cfg,
uint256 l2OutputOracleStartingTimestamp,
Vm vm
)
internal
view
{
ResourceMetering.ResourceConfig memory rcfg = SystemConfig(prox.SystemConfig).resourceConfig();
ResourceMetering.ResourceConfig memory dflt = Constants.DEFAULT_RESOURCE_CONFIG();
require(keccak256(abi.encode(rcfg)) == keccak256(abi.encode(dflt)));
checkSystemConfig(prox, cfg);
checkL1CrossDomainMessenger(prox, vm);
checkL1StandardBridge(prox, vm);
checkL2OutputOracle(prox, cfg, l2OutputOracleStartingTimestamp);
checkOptimismMintableERC20Factory(prox);
checkL1ERC721Bridge(prox);
checkOptimismPortal(prox, cfg);
checkProtocolVersions(prox, cfg);
}
/// @notice Asserts that the SystemConfig is setup correctly
function checkSystemConfig(Types.ContractSet memory proxies, DeployConfig cfg) internal view {
ISystemConfigV0 config = ISystemConfigV0(proxies.SystemConfig);
require(config.owner() == cfg.finalSystemOwner());
require(config.overhead() == cfg.gasPriceOracleOverhead());
require(config.scalar() == cfg.gasPriceOracleScalar());
require(config.batcherHash() == bytes32(uint256(uint160(cfg.batchSenderAddress()))));
require(config.unsafeBlockSigner() == cfg.p2pSequencerAddress());
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit);
require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier);
require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator);
require(resourceConfig.systemTxMaxGas == rconfig.systemTxMaxGas);
require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee);
require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee);
}
/// @notice Asserts that the L1CrossDomainMessenger is setup correctly
function checkL1CrossDomainMessenger(Types.ContractSet memory proxies, Vm vm) internal view {
L1CrossDomainMessenger messenger = L1CrossDomainMessenger(proxies.L1CrossDomainMessenger);
require(address(messenger.portal()) == proxies.OptimismPortal);
require(address(messenger.PORTAL()) == proxies.OptimismPortal);
bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204)));
require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER);
}
/// @notice Asserts that the L1StandardBridge is setup correctly
function checkL1StandardBridge(Types.ContractSet memory proxies, Vm vm) internal view {
L1StandardBridge bridge = L1StandardBridge(payable(proxies.L1StandardBridge));
require(address(bridge.MESSENGER()) == proxies.L1CrossDomainMessenger);
require(address(bridge.messenger()) == proxies.L1CrossDomainMessenger);
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE);
// Ensures that the legacy slot is modified correctly. This will fail
// during predeployment simulation on OP Mainnet if there is a bug.
bytes32 slot0 = vm.load(address(bridge), bytes32(uint256(0)));
require(slot0 == bytes32(uint256(Constants.INITIALIZER)));
}
/// @notice Asserts that the L2OutputOracle is setup correctly
function checkL2OutputOracle(
Types.ContractSet memory proxies,
DeployConfig cfg,
uint256 l2OutputOracleStartingTimestamp
)
internal
view
{
L2OutputOracle oracle = L2OutputOracle(proxies.L2OutputOracle);
require(oracle.SUBMISSION_INTERVAL() == cfg.l2OutputOracleSubmissionInterval());
require(oracle.submissionInterval() == cfg.l2OutputOracleSubmissionInterval());
require(oracle.L2_BLOCK_TIME() == cfg.l2BlockTime());
require(oracle.l2BlockTime() == cfg.l2BlockTime());
require(oracle.PROPOSER() == cfg.l2OutputOracleProposer());
require(oracle.proposer() == cfg.l2OutputOracleProposer());
require(oracle.CHALLENGER() == cfg.l2OutputOracleChallenger());
require(oracle.challenger() == cfg.l2OutputOracleChallenger());
require(oracle.FINALIZATION_PERIOD_SECONDS() == cfg.finalizationPeriodSeconds());
require(oracle.finalizationPeriodSeconds() == cfg.finalizationPeriodSeconds());
require(oracle.startingBlockNumber() == cfg.l2OutputOracleStartingBlockNumber());
require(oracle.startingTimestamp() == l2OutputOracleStartingTimestamp);
}
/// @notice Asserts that the OptimismMintableERC20Factory is setup correctly
function checkOptimismMintableERC20Factory(Types.ContractSet memory proxies) internal view {
OptimismMintableERC20Factory factory = OptimismMintableERC20Factory(proxies.OptimismMintableERC20Factory);
require(factory.BRIDGE() == proxies.L1StandardBridge);
require(factory.bridge() == proxies.L1StandardBridge);
}
/// @notice Asserts that the L1ERC721Bridge is setup correctly
function checkL1ERC721Bridge(Types.ContractSet memory proxies) internal view {
L1ERC721Bridge bridge = L1ERC721Bridge(proxies.L1ERC721Bridge);
require(address(bridge.MESSENGER()) == proxies.L1CrossDomainMessenger);
require(bridge.OTHER_BRIDGE() == Predeploys.L2_ERC721_BRIDGE);
}
/// @notice Asserts the OptimismPortal is setup correctly
function checkOptimismPortal(Types.ContractSet memory proxies, DeployConfig cfg) internal view {
OptimismPortal portal = OptimismPortal(payable(proxies.OptimismPortal));
require(address(portal.L2_ORACLE()) == proxies.L2OutputOracle);
require(portal.GUARDIAN() == cfg.portalGuardian());
require(address(portal.SYSTEM_CONFIG()) == proxies.SystemConfig);
require(portal.paused() == false);
}
/// @notice Asserts that the ProtocolVersions is setup correctly
function checkProtocolVersions(Types.ContractSet memory proxies, DeployConfig cfg) internal view {
ProtocolVersions versions = ProtocolVersions(proxies.ProtocolVersions);
require(versions.owner() == cfg.finalSystemOwner());
require(ProtocolVersion.unwrap(versions.required()) == cfg.requiredProtocolVersion());
require(ProtocolVersion.unwrap(versions.recommended()) == cfg.recommendedProtocolVersion());
}
}
...@@ -43,6 +43,8 @@ import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; ...@@ -43,6 +43,8 @@ import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol";
import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol";
import { AlphabetVM } from "test/mocks/AlphabetVM.sol"; import { AlphabetVM } from "test/mocks/AlphabetVM.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
import { ChainAssertions } from "scripts/ChainAssertions.sol";
import { Types } from "scripts/Types.sol";
/// @title Deploy /// @title Deploy
/// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function. /// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function.
...@@ -642,33 +644,7 @@ contract Deploy is Deployer { ...@@ -642,33 +644,7 @@ contract Deploy is Deployer {
string memory version = config.version(); string memory version = config.version();
console.log("SystemConfig version: %s", version); console.log("SystemConfig version: %s", version);
require(config.owner() == cfg.finalSystemOwner()); ChainAssertions.checkSystemConfig(_proxies(), cfg);
require(config.overhead() == cfg.gasPriceOracleOverhead());
require(config.scalar() == cfg.gasPriceOracleScalar());
require(config.unsafeBlockSigner() == cfg.p2pSequencerAddress());
require(config.batcherHash() == batcherHash);
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit);
require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier);
require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator);
require(resourceConfig.systemTxMaxGas == rconfig.systemTxMaxGas);
require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee);
require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee);
require(config.l1ERC721Bridge() == mustGetAddress("L1ERC721BridgeProxy"));
require(config.l1StandardBridge() == mustGetAddress("L1StandardBridgeProxy"));
require(config.l2OutputOracle() == mustGetAddress("L2OutputOracleProxy"));
require(config.optimismPortal() == mustGetAddress("OptimismPortalProxy"));
require(config.l1CrossDomainMessenger() == mustGetAddress("L1CrossDomainMessengerProxy"));
// A non zero start block is an override
if (startBlock != 0) {
require(config.startBlock() == startBlock);
} else {
require(config.startBlock() == block.number);
}
} }
/// @notice Initialize the L1StandardBridge /// @notice Initialize the L1StandardBridge
...@@ -698,11 +674,7 @@ contract Deploy is Deployer { ...@@ -698,11 +674,7 @@ contract Deploy is Deployer {
string memory version = L1StandardBridge(payable(l1StandardBridgeProxy)).version(); string memory version = L1StandardBridge(payable(l1StandardBridgeProxy)).version();
console.log("L1StandardBridge version: %s", version); console.log("L1StandardBridge version: %s", version);
L1StandardBridge bridge = L1StandardBridge(payable(l1StandardBridgeProxy)); ChainAssertions.checkL1StandardBridge(_proxies(), vm);
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(address(bridge.messenger()) == l1CrossDomainMessengerProxy);
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE);
} }
/// @notice Initialize the L1ERC721Bridge /// @notice Initialize the L1ERC721Bridge
...@@ -721,8 +693,7 @@ contract Deploy is Deployer { ...@@ -721,8 +693,7 @@ contract Deploy is Deployer {
string memory version = bridge.version(); string memory version = bridge.version();
console.log("L1ERC721Bridge version: %s", version); console.log("L1ERC721Bridge version: %s", version);
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy); ChainAssertions.checkL1ERC721Bridge(_proxies());
require(bridge.OTHER_BRIDGE() == Predeploys.L2_ERC721_BRIDGE);
} }
/// @notice Ininitialize the OptimismMintableERC20Factory /// @notice Ininitialize the OptimismMintableERC20Factory
...@@ -741,8 +712,7 @@ contract Deploy is Deployer { ...@@ -741,8 +712,7 @@ contract Deploy is Deployer {
string memory version = factory.version(); string memory version = factory.version();
console.log("OptimismMintableERC20Factory version: %s", version); console.log("OptimismMintableERC20Factory version: %s", version);
require(factory.BRIDGE() == l1StandardBridgeProxy); ChainAssertions.checkOptimismMintableERC20Factory(_proxies());
require(factory.bridge() == l1StandardBridgeProxy);
} }
/// @notice initializeL1CrossDomainMessenger /// @notice initializeL1CrossDomainMessenger
...@@ -786,10 +756,7 @@ contract Deploy is Deployer { ...@@ -786,10 +756,7 @@ contract Deploy is Deployer {
string memory version = messenger.version(); string memory version = messenger.version();
console.log("L1CrossDomainMessenger version: %s", version); console.log("L1CrossDomainMessenger version: %s", version);
require(address(messenger.PORTAL()) == optimismPortalProxy); ChainAssertions.checkL1CrossDomainMessenger(_proxies(), vm);
require(address(messenger.portal()) == optimismPortalProxy);
bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204)));
require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER);
} }
/// @notice Initialize the L2OutputOracle /// @notice Initialize the L2OutputOracle
...@@ -815,18 +782,7 @@ contract Deploy is Deployer { ...@@ -815,18 +782,7 @@ contract Deploy is Deployer {
string memory version = oracle.version(); string memory version = oracle.version();
console.log("L2OutputOracle version: %s", version); console.log("L2OutputOracle version: %s", version);
require(oracle.SUBMISSION_INTERVAL() == cfg.l2OutputOracleSubmissionInterval()); ChainAssertions.checkL2OutputOracle(_proxies(), cfg, cfg.l2OutputOracleStartingTimestamp());
require(oracle.submissionInterval() == cfg.l2OutputOracleSubmissionInterval());
require(oracle.L2_BLOCK_TIME() == cfg.l2BlockTime());
require(oracle.l2BlockTime() == cfg.l2BlockTime());
require(oracle.PROPOSER() == cfg.l2OutputOracleProposer());
require(oracle.proposer() == cfg.l2OutputOracleProposer());
require(oracle.CHALLENGER() == cfg.l2OutputOracleChallenger());
require(oracle.challenger() == cfg.l2OutputOracleChallenger());
require(oracle.FINALIZATION_PERIOD_SECONDS() == cfg.finalizationPeriodSeconds());
require(oracle.finalizationPeriodSeconds() == cfg.finalizationPeriodSeconds());
require(oracle.startingBlockNumber() == cfg.l2OutputOracleStartingBlockNumber());
require(oracle.startingTimestamp() == cfg.l2OutputOracleStartingTimestamp());
} }
/// @notice Initialize the OptimismPortal /// @notice Initialize the OptimismPortal
...@@ -854,10 +810,7 @@ contract Deploy is Deployer { ...@@ -854,10 +810,7 @@ contract Deploy is Deployer {
string memory version = portal.version(); string memory version = portal.version();
console.log("OptimismPortal version: %s", version); console.log("OptimismPortal version: %s", version);
require(address(portal.L2_ORACLE()) == l2OutputOracleProxy); ChainAssertions.checkOptimismPortal(_proxies(), cfg);
require(portal.GUARDIAN() == cfg.portalGuardian());
require(address(portal.SYSTEM_CONFIG()) == systemConfigProxy);
require(portal.paused() == false);
} }
function initializeProtocolVersions() public broadcast { function initializeProtocolVersions() public broadcast {
...@@ -885,9 +838,7 @@ contract Deploy is Deployer { ...@@ -885,9 +838,7 @@ contract Deploy is Deployer {
string memory version = versions.version(); string memory version = versions.version();
console.log("ProtocolVersions version: %s", version); console.log("ProtocolVersions version: %s", version);
require(versions.owner() == finalSystemOwner); ChainAssertions.checkProtocolVersions(_proxies(), cfg);
require(ProtocolVersion.unwrap(versions.required()) == requiredProtocolVersion);
require(ProtocolVersion.unwrap(versions.recommended()) == recommendedProtocolVersion);
} }
/// @notice Transfer ownership of the ProxyAdmin contract to the final system owner /// @notice Transfer ownership of the ProxyAdmin contract to the final system owner
...@@ -1008,4 +959,18 @@ contract Deploy is Deployer { ...@@ -1008,4 +959,18 @@ contract Deploy is Deployer {
console.log("StorageSetter version: %s", version); console.log("StorageSetter version: %s", version);
addr_ = address(setter); addr_ = address(setter);
} }
/// @notice Returns the proxy addresses
function _proxies() private view returns (Types.ContractSet memory proxies_) {
proxies_ = Types.ContractSet({
L1CrossDomainMessenger: mustGetAddress("L1CrossDomainMessengerProxy"),
L1StandardBridge: mustGetAddress("L1StandardBridgeProxy"),
L2OutputOracle: mustGetAddress("L2OutputOracleProxy"),
OptimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: mustGetAddress("OptimismPortalProxy"),
SystemConfig: mustGetAddress("SystemConfigProxy"),
L1ERC721Bridge: mustGetAddress("L1ERC721BridgeProxy"),
ProtocolVersions: mustGetAddress("ProtocolVersionsProxy")
});
}
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Types {
/// @notice Represents a set of L1 contracts. Used to represent a set of proxies.
struct ContractSet {
address L1CrossDomainMessenger;
address L1StandardBridge;
address L2OutputOracle;
address OptimismMintableERC20Factory;
address OptimismPortal;
address SystemConfig;
address L1ERC721Bridge;
address ProtocolVersions;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
/// @title ISystemConfigV0
/// @notice Minimal interface of the Legacy SystemConfig containing only getters.
/// Based on
/// https://github.com/ethereum-optimism/optimism/blob/f54a2234f2f350795552011f35f704a3feb56a08/packages/contracts-bedrock/src/L1/SystemConfig.sol
interface ISystemConfigV0 {
function owner() external view returns (address);
function VERSION() external view returns (uint256);
function overhead() external view returns (uint256);
function scalar() external view returns (uint256);
function batcherHash() external view returns (bytes32);
function gasLimit() external view returns (uint64);
function resourceConfig() external view returns (ResourceMetering.ResourceConfig memory);
function unsafeBlockSigner() external view returns (address);
}
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
"src/universal/OptimismMintableERC20Factory.sol": "0x8d4cbf4cc30a0bb72925b5d2e0386b8f91559f00933a9c7cf3dcc118e34fe61b", "src/universal/OptimismMintableERC20Factory.sol": "0x8d4cbf4cc30a0bb72925b5d2e0386b8f91559f00933a9c7cf3dcc118e34fe61b",
"src/universal/OptimismMintableERC721.sol": "0x4c73bf8474fa7eb091796a4db7e57bc5f26d50a3d1cfcb78d5efa47ced5ced2b", "src/universal/OptimismMintableERC721.sol": "0x4c73bf8474fa7eb091796a4db7e57bc5f26d50a3d1cfcb78d5efa47ced5ced2b",
"src/universal/OptimismMintableERC721Factory.sol": "0x935fd97018b6ef10fa813d9d43ab7a77c80885f7a8d7feb430097645cb2abd2c", "src/universal/OptimismMintableERC721Factory.sol": "0x935fd97018b6ef10fa813d9d43ab7a77c80885f7a8d7feb430097645cb2abd2c",
"src/universal/StorageSetter.sol": "0x6372647d8a67d243bc2fb40d2c4bf5807022d94d52d9423cfed27a7d57918635" "src/universal/StorageSetter.sol": "0x394ec39ef24b44f54549deec6183cace8eea2e5313cde8d5a6e0411a481c5953"
} }
\ No newline at end of file
...@@ -9,9 +9,15 @@ import { Storage } from "src/libraries/Storage.sol"; ...@@ -9,9 +9,15 @@ import { Storage } from "src/libraries/Storage.sol";
/// WARNING: this contract is not safe to be called by untrusted parties. /// WARNING: this contract is not safe to be called by untrusted parties.
/// It is only meant as an intermediate step during upgrades. /// It is only meant as an intermediate step during upgrades.
contract StorageSetter is ISemver { contract StorageSetter is ISemver {
/// @notice Represents a storage slot key value pair.
struct Slot {
bytes32 key;
bytes32 value;
}
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0 /// @custom:semver 1.1.0
string public constant version = "1.0.0"; string public constant version = "1.1.0";
/// @notice Stores a bytes32 `_value` at `_slot`. Any storage slots that /// @notice Stores a bytes32 `_value` at `_slot`. Any storage slots that
/// are packed should be set through this interface. /// are packed should be set through this interface.
...@@ -19,6 +25,14 @@ contract StorageSetter is ISemver { ...@@ -19,6 +25,14 @@ contract StorageSetter is ISemver {
Storage.setBytes32(_slot, _value); Storage.setBytes32(_slot, _value);
} }
/// @notice Stores a bytes32 value at each key in `_slots`.
function setBytes32(Slot[] calldata slots) public {
uint256 length = slots.length;
for (uint256 i; i < length; i++) {
Storage.setBytes32(slots[i].key, slots[i].value);
}
}
/// @notice Retrieves a bytes32 value from `_slot`. /// @notice Retrieves a bytes32 value from `_slot`.
function getBytes32(bytes32 _slot) external view returns (bytes32) { function getBytes32(bytes32 _slot) external view returns (bytes32) {
return Storage.getBytes32(_slot); return Storage.getBytes32(_slot);
......
...@@ -13,6 +13,11 @@ import { Test } from "forge-std/Test.sol"; ...@@ -13,6 +13,11 @@ import { Test } from "forge-std/Test.sol";
contract Storage_Roundtrip_Test is Test { contract Storage_Roundtrip_Test is Test {
StorageSetter setter; StorageSetter setter;
/// @notice A set of storage slots to pass to `setBytes32`.
StorageSetter.Slot[] slots;
/// @notice Used to deduplicate slots passed to `setBytes32`.
mapping(bytes32 => bool) keys;
function setUp() external { function setUp() external {
setter = new StorageSetter(); setter = new StorageSetter();
} }
...@@ -34,4 +39,21 @@ contract Storage_Roundtrip_Test is Test { ...@@ -34,4 +39,21 @@ contract Storage_Roundtrip_Test is Test {
assertEq(setter.getBytes32(slot), hash); assertEq(setter.getBytes32(slot), hash);
assertEq(hash, vm.load(address(setter), slot)); assertEq(hash, vm.load(address(setter), slot));
} }
/// @dev All keys must be unique in the input so deduplication is required.
function testFuzz_setGetBytes32Multi_succeeds(StorageSetter.Slot[] calldata _slots) external {
for (uint256 i; i < _slots.length; i++) {
if (keys[_slots[i].key]) {
continue;
}
slots.push(_slots[i]);
keys[_slots[i].key] = true;
}
setter.setBytes32(slots);
for (uint256 i; i < slots.length; i++) {
assertEq(setter.getBytes32(slots[i].key), slots[i].value);
assertEq(slots[i].value, vm.load(address(setter), slots[i].key));
}
}
} }
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
"@nomiclabs/hardhat-waffle": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.3.8", "@types/chai": "^4.3.8",
"@types/chai-as-promised": "^7.1.5", "@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^10.0.3", "@types/mocha": "^10.0.4",
"@types/node": "^20.8.9", "@types/node": "^20.8.9",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"ethereum-waffle": "^4.0.10", "ethereum-waffle": "^4.0.10",
......
...@@ -28,8 +28,8 @@ importers: ...@@ -28,8 +28,8 @@ importers:
specifier: ^7.1.4 specifier: ^7.1.4
version: 7.1.5 version: 7.1.5
'@types/mocha': '@types/mocha':
specifier: ^10.0.3 specifier: ^10.0.4
version: 10.0.3 version: 10.0.4
'@types/node': '@types/node':
specifier: ^20.8.9 specifier: ^20.8.9
version: 20.8.9 version: 20.8.9
...@@ -269,10 +269,10 @@ importers: ...@@ -269,10 +269,10 @@ importers:
devDependencies: devDependencies:
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^6.9.1 specifier: ^6.9.1
version: 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) version: 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.53.0)(typescript@5.2.2)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^6.9.1 specifier: ^6.9.1
version: 6.9.1(eslint@8.52.0)(typescript@5.2.2) version: 6.9.1(eslint@8.53.0)(typescript@5.2.2)
tsx: tsx:
specifier: ^3.14.0 specifier: ^3.14.0
version: 3.14.0 version: 3.14.0
...@@ -490,8 +490,8 @@ importers: ...@@ -490,8 +490,8 @@ importers:
specifier: ^7.1.5 specifier: ^7.1.5
version: 7.1.5 version: 7.1.5
'@types/mocha': '@types/mocha':
specifier: ^10.0.3 specifier: ^10.0.4
version: 10.0.3 version: 10.0.4
'@types/node': '@types/node':
specifier: ^20.8.9 specifier: ^20.8.9
version: 20.8.9 version: 20.8.9
...@@ -1801,16 +1801,6 @@ packages: ...@@ -1801,16 +1801,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.52.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
eslint: 8.52.0
eslint-visitor-keys: 3.4.3
dev: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
...@@ -1826,23 +1816,6 @@ packages: ...@@ -1826,23 +1816,6 @@ packages:
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
dev: true dev: true
/@eslint/eslintrc@2.1.2:
resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4(supports-color@8.1.1)
espree: 9.6.1
globals: 13.21.0
ignore: 5.2.4
import-fresh: 3.3.0
js-yaml: 4.1.0
minimatch: 3.1.2
strip-json-comments: 3.1.1
transitivePeerDependencies:
- supports-color
dev: true
/@eslint/eslintrc@2.1.3: /@eslint/eslintrc@2.1.3:
resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
...@@ -1860,11 +1833,6 @@ packages: ...@@ -1860,11 +1833,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@eslint/js@8.52.0:
resolution: {integrity: sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@eslint/js@8.53.0: /@eslint/js@8.53.0:
resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
...@@ -4033,8 +4001,8 @@ packages: ...@@ -4033,8 +4001,8 @@ packages:
'@types/node': 20.8.9 '@types/node': 20.8.9
dev: true dev: true
/@types/mocha@10.0.3: /@types/mocha@10.0.4:
resolution: {integrity: sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==} resolution: {integrity: sha512-xKU7bUjiFTIttpWaIZ9qvgg+22O1nmbA+HRxdlR+u6TWsGfmFdXrheJoK4fFxrHNVIOBDvDNKZG+LYBpMHpX3w==}
dev: true dev: true
/@types/morgan@1.9.7: /@types/morgan@1.9.7:
...@@ -4233,35 +4201,6 @@ packages: ...@@ -4233,35 +4201,6 @@ packages:
'@types/node': 20.8.9 '@types/node': 20.8.9
dev: false dev: false
/@typescript-eslint/eslint-plugin@6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2):
resolution: {integrity: sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@eslint-community/regexpp': 4.6.2
'@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2)
'@typescript-eslint/scope-manager': 6.9.1
'@typescript-eslint/type-utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2)
'@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2)
'@typescript-eslint/visitor-keys': 6.9.1
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.52.0
graphemer: 1.4.0
ignore: 5.2.4
natural-compare: 1.4.0
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.2.2)
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/eslint-plugin@6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.53.0)(typescript@5.2.2): /@typescript-eslint/eslint-plugin@6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==} resolution: {integrity: sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==}
engines: {node: ^16.0.0 || >=18.0.0} engines: {node: ^16.0.0 || >=18.0.0}
...@@ -4291,27 +4230,6 @@ packages: ...@@ -4291,27 +4230,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@typescript-eslint/parser@6.9.1(eslint@8.52.0)(typescript@5.2.2):
resolution: {integrity: sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 6.9.1
'@typescript-eslint/types': 6.9.1
'@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2)
'@typescript-eslint/visitor-keys': 6.9.1
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.52.0
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/parser@6.9.1(eslint@8.53.0)(typescript@5.2.2): /@typescript-eslint/parser@6.9.1(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==} resolution: {integrity: sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==}
engines: {node: ^16.0.0 || >=18.0.0} engines: {node: ^16.0.0 || >=18.0.0}
...@@ -4341,26 +4259,6 @@ packages: ...@@ -4341,26 +4259,6 @@ packages:
'@typescript-eslint/visitor-keys': 6.9.1 '@typescript-eslint/visitor-keys': 6.9.1
dev: true dev: true
/@typescript-eslint/type-utils@6.9.1(eslint@8.52.0)(typescript@5.2.2):
resolution: {integrity: sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2)
'@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.52.0
ts-api-utils: 1.0.1(typescript@5.2.2)
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/type-utils@6.9.1(eslint@8.53.0)(typescript@5.2.2): /@typescript-eslint/type-utils@6.9.1(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==} resolution: {integrity: sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==}
engines: {node: ^16.0.0 || >=18.0.0} engines: {node: ^16.0.0 || >=18.0.0}
...@@ -4407,25 +4305,6 @@ packages: ...@@ -4407,25 +4305,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@typescript-eslint/utils@6.9.1(eslint@8.52.0)(typescript@5.2.2):
resolution: {integrity: sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0)
'@types/json-schema': 7.0.12
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 6.9.1
'@typescript-eslint/types': 6.9.1
'@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2)
eslint: 8.52.0
semver: 7.5.4
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/utils@6.9.1(eslint@8.53.0)(typescript@5.2.2): /@typescript-eslint/utils@6.9.1(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==} resolution: {integrity: sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==}
engines: {node: ^16.0.0 || >=18.0.0} engines: {node: ^16.0.0 || >=18.0.0}
...@@ -7841,53 +7720,6 @@ packages: ...@@ -7841,53 +7720,6 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true dev: true
/eslint@8.52.0:
resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0)
'@eslint-community/regexpp': 4.6.2
'@eslint/eslintrc': 2.1.2
'@eslint/js': 8.52.0
'@humanwhocodes/config-array': 0.11.13
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
'@ungap/structured-clone': 1.2.0
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.4(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3
espree: 9.6.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
find-up: 5.0.0
glob-parent: 6.0.2
globals: 13.21.0
graphemer: 1.4.0
ignore: 5.2.4
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
js-yaml: 4.1.0
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.3
strip-ansi: 6.0.1
text-table: 0.2.0
transitivePeerDependencies:
- supports-color
dev: true
/eslint@8.53.0: /eslint@8.53.0:
resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
......
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