Commit 9dc2386a authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into ctb/portal-immutables

parents 3c35aaeb d003f600
...@@ -5,8 +5,8 @@ go 1.20 ...@@ -5,8 +5,8 @@ go 1.20
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.8.0 // indirect golang.org/x/crypto v0.12.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.11.0 // indirect
) )
replace github.com/ethereum-optimism/optimism v0.0.0 => ../../.. replace github.com/ethereum-optimism/optimism v0.0.0 => ../../..
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
module hello module hello
go 1.20
...@@ -23,7 +23,7 @@ require ( ...@@ -23,7 +23,7 @@ require (
github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-leveldb v0.5.0 github.com/ipfs/go-ds-leveldb v0.5.0
github.com/jackc/pgtype v1.14.0 github.com/jackc/pgtype v1.14.0
github.com/jackc/pgx/v5 v5.3.1 github.com/jackc/pgx/v5 v5.4.3
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
github.com/libp2p/go-libp2p v0.25.1 github.com/libp2p/go-libp2p v0.25.1
github.com/libp2p/go-libp2p-pubsub v0.9.3 github.com/libp2p/go-libp2p-pubsub v0.9.3
...@@ -40,10 +40,10 @@ require ( ...@@ -40,10 +40,10 @@ require (
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/urfave/cli v1.22.14 github.com/urfave/cli v1.22.14
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.25.7
golang.org/x/crypto v0.8.0 golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
golang.org/x/sync v0.3.0 golang.org/x/sync v0.3.0
golang.org/x/term v0.7.0 golang.org/x/term v0.11.0
golang.org/x/time v0.3.0 golang.org/x/time v0.3.0
gorm.io/driver/postgres v1.5.2 gorm.io/driver/postgres v1.5.2
gorm.io/gorm v1.25.2 gorm.io/gorm v1.25.2
...@@ -185,9 +185,9 @@ require ( ...@@ -185,9 +185,9 @@ require (
go.uber.org/multierr v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect go.uber.org/zap v1.24.0 // indirect
golang.org/x/mod v0.11.0 // indirect golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.12.0 // indirect
golang.org/x/tools v0.7.0 // indirect golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
......
...@@ -405,8 +405,8 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9 ...@@ -405,8 +405,8 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c h1:Dznn52SgVIVst9UyOT9brctYUgxs+CvVfPaC3jKrA50= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c h1:Dznn52SgVIVst9UyOT9brctYUgxs+CvVfPaC3jKrA50=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
...@@ -860,8 +860,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm ...@@ -860,8 +860,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.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
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-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
...@@ -911,8 +911,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx ...@@ -911,8 +911,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
...@@ -982,13 +982,13 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc ...@@ -982,13 +982,13 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
...@@ -997,8 +997,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= ...@@ -997,8 +997,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
......
...@@ -16,17 +16,15 @@ type PaginationResponse struct { ...@@ -16,17 +16,15 @@ type PaginationResponse struct {
HasNextPage bool `json:"hasNextPage"` HasNextPage bool `json:"hasNextPage"`
} }
func (a *Api) DepositsHandler(w http.ResponseWriter, r *http.Request) { func (a *Api) L1DepositsHandler(w http.ResponseWriter, r *http.Request) {
bv := a.bridgeView bv := a.BridgeTransfersView
address := common.HexToAddress(chi.URLParam(r, "address")) address := common.HexToAddress(chi.URLParam(r, "address"))
// limit := getIntFromQuery(r, "limit", 10) // limit := getIntFromQuery(r, "limit", 10)
// cursor := r.URL.Query().Get("cursor") // cursor := r.URL.Query().Get("cursor")
// sortDirection := r.URL.Query().Get("sortDirection") // sortDirection := r.URL.Query().Get("sortDirection")
deposits, err := bv.DepositsByAddress(address) deposits, err := bv.L1BridgeDepositsByAddress(address)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
...@@ -43,17 +41,15 @@ func (a *Api) DepositsHandler(w http.ResponseWriter, r *http.Request) { ...@@ -43,17 +41,15 @@ func (a *Api) DepositsHandler(w http.ResponseWriter, r *http.Request) {
jsonResponse(w, response, http.StatusOK) jsonResponse(w, response, http.StatusOK)
} }
func (a *Api) WithdrawalsHandler(w http.ResponseWriter, r *http.Request) { func (a *Api) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
bv := a.bridgeView bv := a.BridgeTransfersView
address := common.HexToAddress(chi.URLParam(r, "address")) address := common.HexToAddress(chi.URLParam(r, "address"))
// limit := getIntFromQuery(r, "limit", 10) // limit := getIntFromQuery(r, "limit", 10)
// cursor := r.URL.Query().Get("cursor") // cursor := r.URL.Query().Get("cursor")
// sortDirection := r.URL.Query().Get("sortDirection") // sortDirection := r.URL.Query().Get("sortDirection")
withdrawals, err := bv.WithdrawalsByAddress(address) withdrawals, err := bv.L2BridgeWithdrawalsByAddress(address)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
...@@ -83,22 +79,20 @@ func jsonResponse(w http.ResponseWriter, data interface{}, statusCode int) { ...@@ -83,22 +79,20 @@ func jsonResponse(w http.ResponseWriter, data interface{}, statusCode int) {
} }
type Api struct { type Api struct {
Router *chi.Mux Router *chi.Mux
bridgeView database.BridgeView BridgeTransfersView database.BridgeTransfersView
} }
func NewApi(bv database.BridgeView) *Api { func NewApi(bv database.BridgeTransfersView) *Api {
r := chi.NewRouter() r := chi.NewRouter()
api := &Api{ api := &Api{Router: r, BridgeTransfersView: bv}
Router: r,
bridgeView: bv,
}
// these regex are .+ because I wasn't sure what they should be // these regex are .+ because I wasn't sure what they should be
// don't want a regex for addresses because would prefer to validate the address // don't want a regex for addresses because would prefer to validate the address
// with go-ethereum and throw a friendly error message // with go-ethereum and throw a friendly error message
r.Get("/api/v0/deposits/{address:.+}", api.DepositsHandler) r.Get("/api/v0/deposits/{address:.+}", api.L1DepositsHandler)
r.Get("/api/v0/withdrawals/{address:.+}", api.WithdrawalsHandler) r.Get("/api/v0/withdrawals/{address:.+}", api.L2WithdrawalsHandler)
r.Get("/healthz", api.HealthzHandler) r.Get("/healthz", api.HealthzHandler)
return api return api
......
...@@ -12,89 +12,67 @@ import ( ...@@ -12,89 +12,67 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// MockBridgeView mocks the BridgeView interface // MockBridgeTransfersView mocks the BridgeTransfersView interface
type MockBridgeView struct{} type MockBridgeTransfersView struct{}
const ( const (
guid1 = "8408b6d2-7c90-4cfc-8604-b2204116cb6a" guid1 = "8408b6d2-7c90-4cfc-8604-b2204116cb6a"
guid2 = "8408b6d2-7c90-4cfc-8604-b2204116cb6b" guid2 = "8408b6d2-7c90-4cfc-8604-b2204116cb6b"
) )
// DepositsByAddress mocks returning deposits by an address var (
func (mbv *MockBridgeView) DepositsByAddress(address common.Address) ([]*database.DepositWithTransactionHashes, error) { deposit = database.L1BridgeDeposit{
return []*database.DepositWithTransactionHashes{
{
Deposit: database.Deposit{
GUID: uuid.MustParse(guid1),
InitiatedL1EventGUID: uuid.MustParse(guid2),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
},
L1TransactionHash: common.HexToHash("0x123"),
},
}, nil
}
// DepositsByAddress mocks returning deposits by an address
func (mbv *MockBridgeView) DepositByMessageNonce(nonce *big.Int) (*database.Deposit, error) {
return &database.Deposit{
GUID: uuid.MustParse(guid1), GUID: uuid.MustParse(guid1),
InitiatedL1EventGUID: uuid.MustParse(guid2), InitiatedL1EventGUID: uuid.MustParse(guid2),
Tx: database.Transaction{}, Tx: database.Transaction{},
TokenPair: database.TokenPair{}, TokenPair: database.TokenPair{},
}, nil }
withdrawal = database.L2BridgeWithdrawal{
GUID: uuid.MustParse(guid2),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}
)
func (mbv *MockBridgeTransfersView) L1BridgeDeposit(hash common.Hash) (*database.L1BridgeDeposit, error) {
return &deposit, nil
} }
// LatestDepositMessageNonce mocks returning the latest cross domain message nonce for a deposit func (mbv *MockBridgeTransfersView) L1BridgeDepositByCrossDomainMessengerNonce(nonce *big.Int) (*database.L1BridgeDeposit, error) {
func (mbv *MockBridgeView) LatestDepositMessageNonce() (*big.Int, error) { return &deposit, nil
return big.NewInt(0), nil
} }
// WithdrawalsByAddress mocks returning withdrawals by an address func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Address) ([]*database.L1BridgeDepositWithTransactionHashes, error) {
func (mbv *MockBridgeView) WithdrawalsByAddress(address common.Address) ([]*database.WithdrawalWithTransactionHashes, error) { return []*database.L1BridgeDepositWithTransactionHashes{
return []*database.WithdrawalWithTransactionHashes{
{ {
Withdrawal: database.Withdrawal{ L1BridgeDeposit: deposit,
GUID: uuid.MustParse(guid2), L1TransactionHash: common.HexToHash("0x123"),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
},
L2TransactionHash: common.HexToHash("0x789"),
}, },
}, nil }, nil
} }
// WithdrawalsByMessageNonce mocks returning withdrawals by a withdrawal hash func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalByWithdrawalHash(address common.Hash) (*database.L2BridgeWithdrawal, error) {
func (mbv *MockBridgeView) WithdrawalByMessageNonce(nonce *big.Int) (*database.Withdrawal, error) { return &withdrawal, nil
return &database.Withdrawal{
GUID: uuid.MustParse(guid2),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}, nil
} }
// WithdrawalsByHash mocks returning withdrawals by a withdrawal hash func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalByCrossDomainMessengerNonce(nonce *big.Int) (*database.L2BridgeWithdrawal, error) {
func (mbv *MockBridgeView) WithdrawalByHash(address common.Hash) (*database.Withdrawal, error) { return &withdrawal, nil
return &database.Withdrawal{
GUID: uuid.MustParse(guid2),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}, nil
} }
// LatestWithdrawalMessageNonce mocks returning the latest cross domain message nonce for a withdrawal func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.Address) ([]*database.L2BridgeWithdrawalWithTransactionHashes, error) {
func (mbv *MockBridgeView) LatestWithdrawalMessageNonce() (*big.Int, error) { return []*database.L2BridgeWithdrawalWithTransactionHashes{
return big.NewInt(0), nil {
L2BridgeWithdrawal: withdrawal,
L2TransactionHash: common.HexToHash("0x789"),
},
}, nil
} }
func TestHealthz(t *testing.T) { func TestHealthz(t *testing.T) {
api := NewApi(&MockBridgeView{}) api := NewApi(&MockBridgeTransfersView{})
request, err := http.NewRequest("GET", "/healthz", nil) request, err := http.NewRequest("GET", "/healthz", nil)
assert.Nil(t, err) assert.Nil(t, err)
...@@ -104,8 +82,8 @@ func TestHealthz(t *testing.T) { ...@@ -104,8 +82,8 @@ func TestHealthz(t *testing.T) {
assert.Equal(t, http.StatusOK, responseRecorder.Code) assert.Equal(t, http.StatusOK, responseRecorder.Code)
} }
func TestDepositsHandler(t *testing.T) { func TestL1BridgeDepositsHandler(t *testing.T) {
api := NewApi(&MockBridgeView{}) api := NewApi(&MockBridgeTransfersView{})
request, err := http.NewRequest("GET", "/api/v0/deposits/0x123", nil) request, err := http.NewRequest("GET", "/api/v0/deposits/0x123", nil)
assert.Nil(t, err) assert.Nil(t, err)
...@@ -115,8 +93,8 @@ func TestDepositsHandler(t *testing.T) { ...@@ -115,8 +93,8 @@ func TestDepositsHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, responseRecorder.Code) assert.Equal(t, http.StatusOK, responseRecorder.Code)
} }
func TestWithdrawalsHandler(t *testing.T) { func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) {
api := NewApi(&MockBridgeView{}) api := NewApi(&MockBridgeTransfersView{})
request, err := http.NewRequest("GET", "/api/v0/withdrawals/0x123", nil) request, err := http.NewRequest("GET", "/api/v0/withdrawals/0x123", nil)
assert.Nil(t, err) assert.Nil(t, err)
......
...@@ -23,15 +23,16 @@ type Cli struct { ...@@ -23,15 +23,16 @@ type Cli struct {
} }
func runIndexer(ctx *cli.Context) error { func runIndexer(ctx *cli.Context) error {
logger := log.NewLogger(log.ReadCLIConfig(ctx))
configPath := ctx.String(ConfigFlag.Name) configPath := ctx.String(ConfigFlag.Name)
cfg, err := config.LoadConfig(configPath) cfg, err := config.LoadConfig(configPath)
if err != nil { if err != nil {
logger.Error("failed to load config", "err", err)
return err return err
} }
// setup logger cfg.Logger = logger
cfg.Logger = log.NewLogger(log.ReadCLIConfig(ctx))
indexer, err := indexer.NewIndexer(cfg) indexer, err := indexer.NewIndexer(cfg)
if err != nil { if err != nil {
return err return err
...@@ -47,17 +48,20 @@ func runIndexer(ctx *cli.Context) error { ...@@ -47,17 +48,20 @@ func runIndexer(ctx *cli.Context) error {
} }
func runApi(ctx *cli.Context) error { func runApi(ctx *cli.Context) error {
configPath := ctx.String(ConfigFlag.Name) logger := log.NewLogger(log.ReadCLIConfig(ctx))
conf, err := config.LoadConfig(configPath)
fmt.Println(conf)
configPath := ctx.String(ConfigFlag.Name)
cfg, err := config.LoadConfig(configPath)
if err != nil { if err != nil {
panic(err) logger.Error("failed to load config", "err", err)
return err
} }
cfg.Logger = logger
fmt.Println(cfg)
// finish me // finish me
return nil return err
} }
var ( var (
......
...@@ -10,9 +10,9 @@ import ( ...@@ -10,9 +10,9 @@ import (
type DB struct { type DB struct {
gorm *gorm.DB gorm *gorm.DB
Blocks BlocksDB Blocks BlocksDB
ContractEvents ContractEventsDB ContractEvents ContractEventsDB
Bridge BridgeDB BridgeTransfers BridgeTransfersDB
} }
func NewDB(dsn string) (*DB, error) { func NewDB(dsn string) (*DB, error) {
...@@ -31,10 +31,10 @@ func NewDB(dsn string) (*DB, error) { ...@@ -31,10 +31,10 @@ func NewDB(dsn string) (*DB, error) {
} }
db := &DB{ db := &DB{
gorm: gorm, gorm: gorm,
Blocks: newBlocksDB(gorm), Blocks: newBlocksDB(gorm),
ContractEvents: newContractEventsDB(gorm), ContractEvents: newContractEventsDB(gorm),
Bridge: newBridgeDB(gorm), BridgeTransfers: newBridgeTransfersDB(gorm),
} }
return db, nil return db, nil
...@@ -59,9 +59,9 @@ func (db *DB) Close() error { ...@@ -59,9 +59,9 @@ func (db *DB) Close() error {
func dbFromGormTx(tx *gorm.DB) *DB { func dbFromGormTx(tx *gorm.DB) *DB {
return &DB{ return &DB{
gorm: tx, gorm: tx,
Blocks: newBlocksDB(tx), Blocks: newBlocksDB(tx),
ContractEvents: newContractEventsDB(tx), ContractEvents: newContractEventsDB(tx),
Bridge: newBridgeDB(tx), BridgeTransfers: newBridgeTransfersDB(tx),
} }
} }
package e2e_tests
import (
"context"
"math/big"
"testing"
"time"
"github.com/ethereum-optimism/optimism/indexer/processor"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
func TestE2EBridge(t *testing.T) {
testSuite := createE2ETestSuite(t)
l1Client := testSuite.OpSys.Clients["l1"]
l2Client := testSuite.OpSys.Clients["sequencer"]
l1StandardBridge, err := bindings.NewL1StandardBridge(testSuite.OpCfg.L1Deployments.L1StandardBridgeProxy, l1Client)
require.NoError(t, err)
l2StandardBridge, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, l2Client)
require.NoError(t, err)
// pre-emptively conduct a deposit & withdrawal to speed up the test
setupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
l2Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L2ChainIDBig())
require.NoError(t, err)
l1Opts.Value = big.NewInt(params.Ether)
l2Opts.Value = big.NewInt(params.Ether)
depositTx, err := l1StandardBridge.DepositETH(l1Opts, 200_000, []byte{byte(1)})
require.NoError(t, err)
withdrawTx, err := l2StandardBridge.Withdraw(l2Opts, processor.EthAddress, big.NewInt(params.Ether), 200_000, []byte{byte(1)})
require.NoError(t, err)
depositReceipt, err := utils.WaitReceiptOK(setupCtx, l1Client, depositTx.Hash())
require.NoError(t, err)
withdrawalReceipt, err := utils.WaitReceiptOK(setupCtx, l2Client, withdrawTx.Hash())
require.NoError(t, err)
t.Run("indexes ETH deposits", func(t *testing.T) {
testCtx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
// Pause the L2Processor so that we can test for finalization separately. A pause is
// required since deposit inclusion is apart of the L2 block derivation process
testSuite.Indexer.L2Processor.PauseForTest()
// (1) Test Deposit Initiation
// wait for processor catchup
require.NoError(t, utils.WaitFor(testCtx, 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil
}))
aliceDeposits, err := testSuite.DB.Bridge.DepositsByAddress(aliceAddr)
require.NoError(t, err)
require.Len(t, aliceDeposits, 1)
require.Equal(t, depositTx.Hash(), aliceDeposits[0].L1TransactionHash)
require.Empty(t, aliceDeposits[0].FinalizedL2TransactionHash)
deposit := aliceDeposits[0].Deposit
require.Nil(t, deposit.FinalizedL2EventGUID)
require.Equal(t, processor.EthAddress, deposit.TokenPair.L1TokenAddress)
require.Equal(t, processor.EthAddress, deposit.TokenPair.L2TokenAddress)
require.Equal(t, big.NewInt(params.Ether), deposit.Tx.Amount.Int)
require.Equal(t, aliceAddr, deposit.Tx.FromAddress)
require.Equal(t, aliceAddr, deposit.Tx.ToAddress)
require.Equal(t, byte(1), deposit.Tx.Data[0])
// (2) Test Deposit Finalization
testSuite.Indexer.L2Processor.ResumeForTest()
// finalization hash can be deterministically derived from TransactionDeposited log
var depositTxHash common.Hash
for _, log := range depositReceipt.Logs {
if log.Topics[0] == derive.DepositEventABIHash {
deposit, err := derive.UnmarshalDepositLogEvent(log)
require.NoError(t, err)
depositTxHash = types.NewTx(deposit).Hash()
break
}
}
// wait for the l2 processor to catch this deposit in the derivation process
_, err = utils.WaitReceiptOK(testCtx, l2Client, depositTxHash)
require.NoError(t, err)
l2Height, err := l2Client.BlockNumber(testCtx)
require.NoError(t, err)
require.NoError(t, utils.WaitFor(testCtx, 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return l2Header != nil && l2Header.Number.Uint64() >= l2Height, nil
}))
aliceDeposits, err = testSuite.DB.Bridge.DepositsByAddress(aliceAddr)
require.NoError(t, err)
require.Equal(t, depositTxHash, aliceDeposits[0].FinalizedL2TransactionHash)
require.NotNil(t, aliceDeposits[0].Deposit.FinalizedL2EventGUID)
})
t.Run("indexes ETH withdrawals", func(t *testing.T) {
testCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
// (1) Test Withdrawal Initiation
// wait for processor catchup
require.NoError(t, utils.WaitFor(testCtx, 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return l2Header != nil && l2Header.Number.Uint64() >= withdrawalReceipt.BlockNumber.Uint64(), nil
}))
aliceWithdrawals, err := testSuite.DB.Bridge.WithdrawalsByAddress(aliceAddr)
require.NoError(t, err)
require.Len(t, aliceWithdrawals, 1)
require.Equal(t, withdrawTx.Hash(), aliceWithdrawals[0].L2TransactionHash)
require.Empty(t, aliceWithdrawals[0].ProvenL1TransactionHash)
require.Empty(t, aliceWithdrawals[0].FinalizedL1TransactionHash)
withdrawal := aliceWithdrawals[0].Withdrawal
require.Nil(t, withdrawal.ProvenL1EventGUID)
require.Nil(t, withdrawal.FinalizedL1EventGUID)
require.Equal(t, processor.EthAddress, withdrawal.TokenPair.L1TokenAddress)
require.Equal(t, processor.EthAddress, withdrawal.TokenPair.L2TokenAddress)
require.Equal(t, big.NewInt(params.Ether), withdrawal.Tx.Amount.Int)
require.Equal(t, aliceAddr, withdrawal.Tx.FromAddress)
require.Equal(t, aliceAddr, withdrawal.Tx.ToAddress)
require.Equal(t, byte(1), withdrawal.Tx.Data[0])
// (2) Test Withdrawal Proven
// prove & wait for processor catchup
withdrawParams, proveReceipt := op_e2e.ProveWithdrawal(t, *testSuite.OpCfg, l1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawalReceipt)
require.NoError(t, utils.WaitFor(testCtx, 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= proveReceipt.BlockNumber.Uint64(), nil
}))
aliceWithdrawals, err = testSuite.DB.Bridge.WithdrawalsByAddress(aliceAddr)
require.NoError(t, err)
require.Empty(t, aliceWithdrawals[0].FinalizedL1TransactionHash)
require.Equal(t, proveReceipt.TxHash, aliceWithdrawals[0].ProvenL1TransactionHash)
// (3) Test Withdrawal Finalization
// finalize & wait for processor catchup
finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, l1Client, testSuite.OpCfg.Secrets.Alice, proveReceipt, withdrawParams)
require.NoError(t, utils.WaitFor(testCtx, 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
}))
aliceWithdrawals, err = testSuite.DB.Bridge.WithdrawalsByAddress(aliceAddr)
require.NoError(t, err)
require.Equal(t, finalizeReceipt.TxHash, aliceWithdrawals[0].FinalizedL1TransactionHash)
})
}
...@@ -23,25 +23,19 @@ import ( ...@@ -23,25 +23,19 @@ import (
func TestE2EBlockHeaders(t *testing.T) { func TestE2EBlockHeaders(t *testing.T) {
testSuite := createE2ETestSuite(t) testSuite := createE2ETestSuite(t)
l1Client := testSuite.OpSys.Clients["l1"] l2OutputOracle, err := bindings.NewL2OutputOracle(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, testSuite.L1Client)
l2Client := testSuite.OpSys.Clients["sequencer"]
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
// a minute for total setup to finish
setupCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
// wait for at least 10 L2 blocks to be created & posted on L1 // wait for at least 10 L2 blocks to be created & posted on L1
require.NoError(t, utils.WaitFor(setupCtx, time.Second, func() (bool, error) { require.NoError(t, utils.WaitFor(context.Background(), time.Second, func() (bool, error) {
l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: setupCtx}) l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: context.Background()})
return l2Height != nil && l2Height.Uint64() >= 9, err return l2Height != nil && l2Height.Uint64() >= 9, err
})) }))
// ensure the processors are caught up to this state // ensure the processors are caught up to this state
l1Height, err := l1Client.BlockNumber(setupCtx) l1Height, err := testSuite.L1Client.BlockNumber(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, utils.WaitFor(setupCtx, time.Second, func() (bool, error) { require.NoError(t, utils.WaitFor(context.Background(), time.Second, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return (l1Header != nil && l1Header.Number.Uint64() >= l1Height) && (l2Header != nil && l2Header.Number.Uint64() >= 9), nil return (l1Header != nil && l1Header.Number.Uint64() >= l1Height) && (l2Header != nil && l2Header.Number.Uint64() >= 9), nil
...@@ -60,7 +54,7 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -60,7 +54,7 @@ func TestE2EBlockHeaders(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, indexedHeader) require.NotNil(t, indexedHeader)
header, err := l2Client.HeaderByNumber(context.Background(), height) header, err := testSuite.L2Client.HeaderByNumber(context.Background(), height)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, indexedHeader) require.NotNil(t, indexedHeader)
...@@ -93,7 +87,7 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -93,7 +87,7 @@ func TestE2EBlockHeaders(t *testing.T) {
require.NotEmpty(t, output.L1ContractEventGUID) require.NotEmpty(t, output.L1ContractEventGUID)
// we may as well check the integrity of the output root // we may as well check the integrity of the output root
l2Block, err := l2Client.BlockByNumber(context.Background(), blockNumber) l2Block, err := testSuite.L2Client.BlockByNumber(context.Background(), blockNumber)
require.NoError(t, err) require.NoError(t, err)
messagePasserStorageHash, err := l2EthClient.StorageHash(predeploys.L2ToL1MessagePasserAddr, blockNumber) messagePasserStorageHash, err := l2EthClient.StorageHash(predeploys.L2ToL1MessagePasserAddr, blockNumber)
require.NoError(t, err) require.NoError(t, err)
...@@ -111,12 +105,10 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -111,12 +105,10 @@ func TestE2EBlockHeaders(t *testing.T) {
testCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) testCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
devContracts := make([]common.Address, 0) l1Contracts := []common.Address{}
testSuite.OpCfg.L1Deployments.ForEach(func(name string, address common.Address) { testSuite.OpCfg.L1Deployments.ForEach(func(name string, addr common.Address) { l1Contracts = append(l1Contracts, addr) })
devContracts = append(devContracts, address) logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: l1Contracts}
}) logs, err := testSuite.L1Client.FilterLogs(testCtx, logFilter) // []types.Log
logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: devContracts}
logs, err := l1Client.FilterLogs(testCtx, logFilter) // []types.Log
require.NoError(t, err) require.NoError(t, err)
for _, log := range logs { for _, log := range logs {
...@@ -128,7 +120,7 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -128,7 +120,7 @@ func TestE2EBlockHeaders(t *testing.T) {
require.Equal(t, log.Index, uint(contractEvent.LogIndex)) require.Equal(t, log.Index, uint(contractEvent.LogIndex))
// ensure the block is also indexed // ensure the block is also indexed
block, err := l1Client.BlockByNumber(testCtx, big.NewInt(int64(log.BlockNumber))) block, err := testSuite.L1Client.BlockByNumber(testCtx, big.NewInt(int64(log.BlockNumber)))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, block.Time(), contractEvent.Timestamp) require.Equal(t, block.Time(), contractEvent.Timestamp)
......
package e2e_tests
import (
"context"
"math/big"
"testing"
"time"
e2etest_utils "github.com/ethereum-optimism/optimism/indexer/e2e_tests/utils"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) {
testSuite := createE2ETestSuite(t)
l1StandardBridge, err := bindings.NewL1StandardBridge(testSuite.OpCfg.L1Deployments.L1StandardBridgeProxy, testSuite.L1Client)
require.NoError(t, err)
// 1 ETH transfer
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
l1Opts.Value = big.NewInt(params.Ether)
// Pause the L2Processor so that we can test for finalization separately. A pause is
// required since deposit inclusion is apart of the L2 block derivation process
testSuite.Indexer.L2Processor.PauseForTest()
// (1) Test Deposit Initiation
depositTx, err := l1StandardBridge.DepositETH(l1Opts, 200_000, []byte{byte(1)})
require.NoError(t, err)
depositReceipt, err := utils.WaitReceiptOK(context.Background(), testSuite.L1Client, depositTx.Hash())
require.NoError(t, err)
depositInfo, err := e2etest_utils.ParseDepositInfo(depositReceipt)
require.NoError(t, err)
// wait for processor catchup
require.NoError(t, utils.WaitFor(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil
}))
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr)
require.NoError(t, err)
require.Len(t, aliceDeposits, 1)
require.Equal(t, depositTx.Hash(), aliceDeposits[0].L1TransactionHash)
require.Empty(t, aliceDeposits[0].FinalizedL2TransactionHash)
deposit := aliceDeposits[0].L1BridgeDeposit
require.Equal(t, predeploys.LegacyERC20ETHAddr, deposit.TokenPair.L1TokenAddress)
require.Equal(t, predeploys.LegacyERC20ETHAddr, deposit.TokenPair.L2TokenAddress)
require.Equal(t, big.NewInt(params.Ether), deposit.Tx.Amount.Int)
require.Equal(t, aliceAddr, deposit.Tx.FromAddress)
require.Equal(t, aliceAddr, deposit.Tx.ToAddress)
require.Equal(t, byte(1), deposit.Tx.Data[0])
// (2) Test Deposit Finalization
require.Nil(t, deposit.FinalizedL2EventGUID)
testSuite.Indexer.L2Processor.ResumeForTest()
// wait for the l2 processor to catch this deposit in the derivation process
depositReceipt, err = utils.WaitReceiptOK(context.Background(), testSuite.L2Client, types.NewTx(depositInfo.DepositTx).Hash())
require.NoError(t, err)
require.NoError(t, utils.WaitFor(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return l2Header != nil && l2Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil
}))
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr)
require.NoError(t, err)
require.NotNil(t, aliceDeposits[0].L1BridgeDeposit.FinalizedL2EventGUID)
require.Equal(t, types.NewTx(depositInfo.DepositTx).Hash(), aliceDeposits[0].FinalizedL2TransactionHash)
}
func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) {
testSuite := createE2ETestSuite(t)
optimismPortal, err := bindings.NewOptimismPortal(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client)
require.NoError(t, err)
l2StandardBridge, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, testSuite.L2Client)
require.NoError(t, err)
// 1 ETH transfer
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
l2Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L2ChainIDBig())
require.NoError(t, err)
l2Opts.Value = big.NewInt(params.Ether)
// Ensure L1 has enough funds for the withdrawal by depositing an equal amount into the OptimismPortal
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
l1Opts.Value = l2Opts.Value
depositTx, err := optimismPortal.Receive(l1Opts)
require.NoError(t, err)
_, err = utils.WaitReceiptOK(context.Background(), testSuite.L1Client, depositTx.Hash())
require.NoError(t, err)
// (1) Test Withdrawal Initiation
withdrawTx, err := l2StandardBridge.Withdraw(l2Opts, predeploys.LegacyERC20ETHAddr, l2Opts.Value, 200_000, []byte{byte(1)})
require.NoError(t, err)
withdrawReceipt, err := utils.WaitReceiptOK(context.Background(), testSuite.L2Client, withdrawTx.Hash())
require.NoError(t, err)
// wait for processor catchup
require.NoError(t, utils.WaitFor(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipt.BlockNumber.Uint64(), nil
}))
aliceWithdrawals, err := testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr)
require.NoError(t, err)
require.Len(t, aliceWithdrawals, 1)
require.Equal(t, withdrawTx.Hash(), aliceWithdrawals[0].L2TransactionHash)
msgPassed, err := withdrawals.ParseMessagePassed(withdrawReceipt)
require.NoError(t, err)
withdrawalHash, err := withdrawals.WithdrawalHash(msgPassed)
require.NoError(t, err)
withdrawal := aliceWithdrawals[0].L2BridgeWithdrawal
require.Equal(t, withdrawalHash, withdrawal.WithdrawalHash)
require.Equal(t, predeploys.LegacyERC20ETHAddr, withdrawal.TokenPair.L1TokenAddress)
require.Equal(t, predeploys.LegacyERC20ETHAddr, withdrawal.TokenPair.L2TokenAddress)
require.Equal(t, big.NewInt(params.Ether), withdrawal.Tx.Amount.Int)
require.Equal(t, aliceAddr, withdrawal.Tx.FromAddress)
require.Equal(t, aliceAddr, withdrawal.Tx.ToAddress)
require.Equal(t, byte(1), withdrawal.Tx.Data[0])
// (2) Test Withdrawal Proven/Finalized. Test the sql join queries to populate the right transaction
require.Nil(t, withdrawal.ProvenL1EventGUID)
require.Nil(t, withdrawal.FinalizedL1EventGUID)
require.Empty(t, aliceWithdrawals[0].ProvenL1TransactionHash)
require.Empty(t, aliceWithdrawals[0].FinalizedL1TransactionHash)
// wait for processor catchup
proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt)
require.NoError(t, utils.WaitFor(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
}))
aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr)
require.NoError(t, err)
require.NotNil(t, aliceWithdrawals[0].L2BridgeWithdrawal.ProvenL1EventGUID)
require.NotNil(t, aliceWithdrawals[0].L2BridgeWithdrawal.FinalizedL1EventGUID)
require.Equal(t, proveReceipt.TxHash, aliceWithdrawals[0].ProvenL1TransactionHash)
require.Equal(t, finalizeReceipt.TxHash, aliceWithdrawals[0].FinalizedL1TransactionHash)
}
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
_ "github.com/jackc/pgx/v5/stdlib" _ "github.com/jackc/pgx/v5/stdlib"
...@@ -33,6 +34,10 @@ type E2ETestSuite struct { ...@@ -33,6 +34,10 @@ type E2ETestSuite struct {
// Rollup // Rollup
OpCfg *op_e2e.SystemConfig OpCfg *op_e2e.SystemConfig
OpSys *op_e2e.System OpSys *op_e2e.System
// Clients
L1Client *ethclient.Client
L2Client *ethclient.Client
} }
func createE2ETestSuite(t *testing.T) E2ETestSuite { func createE2ETestSuite(t *testing.T) E2ETestSuite {
...@@ -48,16 +53,9 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -48,16 +53,9 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
opSys, err := opCfg.Start() opSys, err := opCfg.Start()
require.NoError(t, err) require.NoError(t, err)
l1Contracts := processor.L1Contracts{
OptimismPortal: opCfg.L1Deployments.OptimismPortalProxy,
L2OutputOracle: opCfg.L1Deployments.L2OutputOracleProxy,
L1CrossDomainMessenger: opCfg.L1Deployments.L1CrossDomainMessengerProxy,
L1StandardBridge: opCfg.L1Deployments.L1StandardBridgeProxy,
L1ERC721Bridge: opCfg.L1Deployments.L1ERC721BridgeProxy,
}
// Indexer Configuration and Start // Indexer Configuration and Start
indexerCfg := config.Config{ indexerCfg := config.Config{
Logger: logger,
DB: config.DBConfig{ DB: config.DBConfig{
Host: "127.0.0.1", Host: "127.0.0.1",
Port: 5432, Port: 5432,
...@@ -68,9 +66,14 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -68,9 +66,14 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
L1RPC: opSys.Nodes["l1"].HTTPEndpoint(), L1RPC: opSys.Nodes["l1"].HTTPEndpoint(),
L2RPC: opSys.Nodes["sequencer"].HTTPEndpoint(), L2RPC: opSys.Nodes["sequencer"].HTTPEndpoint(),
}, },
Logger: logger,
Chain: config.ChainConfig{ Chain: config.ChainConfig{
L1Contracts: l1Contracts, L1Contracts: processor.L1Contracts{
OptimismPortal: opCfg.L1Deployments.OptimismPortalProxy,
L2OutputOracle: opCfg.L1Deployments.L2OutputOracleProxy,
L1CrossDomainMessenger: opCfg.L1Deployments.L1CrossDomainMessengerProxy,
L1StandardBridge: opCfg.L1Deployments.L1StandardBridgeProxy,
L1ERC721Bridge: opCfg.L1Deployments.L1ERC721BridgeProxy,
},
}, },
} }
...@@ -99,11 +102,13 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -99,11 +102,13 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
}) })
return E2ETestSuite{ return E2ETestSuite{
t: t, t: t,
DB: db, DB: db,
Indexer: indexer, Indexer: indexer,
OpCfg: &opCfg, OpCfg: &opCfg,
OpSys: opSys, OpSys: opSys,
L1Client: opSys.Clients["l1"],
L2Client: opSys.Clients["sequencer"],
} }
} }
......
package utils
import (
"errors"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type DepositInfo struct {
*bindings.OptimismPortalTransactionDeposited
DepositTx *types.DepositTx
}
func ParseDepositInfo(depositReceipt *types.Receipt) (*DepositInfo, error) {
optimismPortal, err := bindings.NewOptimismPortal(common.Address{}, nil)
if err != nil {
return nil, err
}
for _, log := range depositReceipt.Logs {
if log.Topics[0] == derive.DepositEventABIHash {
portalTxDeposited, err := optimismPortal.ParseTransactionDeposited(*log)
if err != nil {
return nil, err
}
depositTx, err := derive.UnmarshalDepositLogEvent(log)
if err != nil {
return nil, err
}
return &DepositInfo{portalTxDeposited, depositTx}, nil
}
}
return nil, errors.New("cannot find deposit event in receipt")
}
...@@ -72,21 +72,30 @@ func NewIndexer(cfg config.Config) (*Indexer, error) { ...@@ -72,21 +72,30 @@ func NewIndexer(cfg config.Config) (*Indexer, error) {
// Start starts the indexing service on L1 and L2 chains // Start starts the indexing service on L1 and L2 chains
func (i *Indexer) Run(ctx context.Context) error { func (i *Indexer) Run(ctx context.Context) error {
var wg sync.WaitGroup var wg sync.WaitGroup
errCh := make(chan error) errCh := make(chan error, 1)
// If either processor errors out, we stop // If either processor errors out, we stop
processorCtx, cancel := context.WithCancel(ctx) processorCtx, cancel := context.WithCancel(ctx)
run := func(start func(ctx context.Context) error) { run := func(start func(ctx context.Context) error) {
wg.Add(1) wg.Add(1)
defer wg.Done() defer func() {
if err := recover(); err != nil {
i.log.Error("halting indexer on panic", "err", err)
errCh <- fmt.Errorf("panic: %v", err)
}
wg.Done()
}()
err := start(processorCtx) err := start(processorCtx)
if err != nil { if err != nil {
i.log.Error("halting indexer on error", "err", err) i.log.Error("halting indexer on error", "err", err)
cancel() cancel()
errCh <- err
} }
// Send a value down regardless if we've received an error or halted
// via cancellation where err == nil
errCh <- err
} }
// Kick off the processors // Kick off the processors
......
...@@ -67,12 +67,12 @@ CREATE TABLE IF NOT EXISTS output_proposals ( ...@@ -67,12 +67,12 @@ CREATE TABLE IF NOT EXISTS output_proposals (
* BRIDGING DATA * BRIDGING DATA
*/ */
CREATE TABLE IF NOT EXISTS deposits ( CREATE TABLE IF NOT EXISTS l1_bridge_deposits (
guid VARCHAR PRIMARY KEY NOT NULL, guid VARCHAR PRIMARY KEY NOT NULL,
-- Event causing the deposit -- Event causing the deposit
initiated_l1_event_guid VARCHAR NOT NULL REFERENCES l1_contract_events(guid), initiated_l1_event_guid VARCHAR NOT NULL REFERENCES l1_contract_events(guid),
sent_message_nonce UINT256 UNIQUE, cross_domain_messenger_nonce UINT256 UNIQUE,
-- Finalization marker for the deposit -- Finalization marker for the deposit
finalized_l2_event_guid VARCHAR REFERENCES l2_contract_events(guid), finalized_l2_event_guid VARCHAR REFERENCES l2_contract_events(guid),
...@@ -88,12 +88,12 @@ CREATE TABLE IF NOT EXISTS deposits ( ...@@ -88,12 +88,12 @@ CREATE TABLE IF NOT EXISTS deposits (
timestamp INTEGER NOT NULL CHECK (timestamp > 0) timestamp INTEGER NOT NULL CHECK (timestamp > 0)
); );
CREATE TABLE IF NOT EXISTS withdrawals ( CREATE TABLE IF NOT EXISTS l2_bridge_withdrawals (
guid VARCHAR PRIMARY KEY NOT NULL, guid VARCHAR PRIMARY KEY NOT NULL,
-- Event causing this withdrawal -- Event causing this withdrawal
initiated_l2_event_guid VARCHAR NOT NULL REFERENCES l2_contract_events(guid), initiated_l2_event_guid VARCHAR NOT NULL REFERENCES l2_contract_events(guid),
sent_message_nonce UINT256 UNIQUE, cross_domain_messenger_nonce UINT256 UNIQUE,
-- Multistep (bedrock) process of a withdrawal -- Multistep (bedrock) process of a withdrawal
withdrawal_hash VARCHAR NOT NULL, withdrawal_hash VARCHAR NOT NULL,
......
...@@ -255,13 +255,13 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -255,13 +255,13 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
return err return err
} }
deposits := make([]*database.Deposit, len(initiatedDepositEvents)) deposits := make([]*database.L1BridgeDeposit, len(initiatedDepositEvents))
for i, initiatedBridgeEvent := range initiatedDepositEvents { for i, initiatedBridgeEvent := range initiatedDepositEvents {
deposits[i] = &database.Deposit{ deposits[i] = &database.L1BridgeDeposit{
GUID: uuid.New(), GUID: uuid.New(),
InitiatedL1EventGUID: initiatedBridgeEvent.RawEvent.GUID, InitiatedL1EventGUID: initiatedBridgeEvent.RawEvent.GUID,
SentMessageNonce: database.U256{Int: initiatedBridgeEvent.CrossDomainMessengerNonce}, CrossDomainMessengerNonce: database.U256{Int: initiatedBridgeEvent.CrossDomainMessengerNonce},
TokenPair: database.TokenPair{L1TokenAddress: initiatedBridgeEvent.LocalToken, L2TokenAddress: initiatedBridgeEvent.RemoteToken}, TokenPair: database.TokenPair{L1TokenAddress: initiatedBridgeEvent.LocalToken, L2TokenAddress: initiatedBridgeEvent.RemoteToken},
Tx: database.Transaction{ Tx: database.Transaction{
FromAddress: initiatedBridgeEvent.From, FromAddress: initiatedBridgeEvent.From,
ToAddress: initiatedBridgeEvent.To, ToAddress: initiatedBridgeEvent.To,
...@@ -274,7 +274,7 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -274,7 +274,7 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
if len(deposits) > 0 { if len(deposits) > 0 {
processLog.Info("detected L1StandardBridge deposits", "num", len(deposits)) processLog.Info("detected L1StandardBridge deposits", "num", len(deposits))
err := db.Bridge.StoreDeposits(deposits) err := db.BridgeTransfers.StoreL1BridgeDeposits(deposits)
if err != nil { if err != nil {
return err return err
} }
...@@ -286,47 +286,39 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -286,47 +286,39 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
return err return err
} }
// we manually keep track since not every proven withdrawal is a standard bridge withdrawal latestL2Header, err := db.Blocks.LatestL2BlockHeader()
if err != nil {
return err
} else if len(provenWithdrawalEvents) > 0 && latestL2Header == nil {
return errors.New("no indexed L2 state to process any proven L1 transactions")
}
numProvenWithdrawals := 0 numProvenWithdrawals := 0
for _, provenWithdrawalEvent := range provenWithdrawalEvents { for _, provenWithdrawalEvent := range provenWithdrawalEvents {
withdrawalHash := provenWithdrawalEvent.WithdrawalHash withdrawalHash := provenWithdrawalEvent.WithdrawalHash
withdrawal, err := db.Bridge.WithdrawalByHash(withdrawalHash) withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalByWithdrawalHash(withdrawalHash)
if err != nil { if err != nil {
return err return err
} } else if withdrawal == nil {
// NOTE: This needs to be updated to identify if this CrossDomainMessenger message is a StandardBridge message. This
// Check if the L2Processor is behind or really has missed an event. We can compare against the // will be easier to do once we index cross domain messages and track its lifecyle separately
// OptimismPortal#ProvenWithdrawal on-chain mapping relative to the latest indexed L2 height
if withdrawal == nil {
// This needs to be updated to read from config as well as correctly identify if the CrossDomainMessenger message is a standard
// bridge message. This will easier to do once we index passed messages separately which will include the right To/From fields
if provenWithdrawalEvent.From != common.HexToAddress("0x4200000000000000000000000000000000000007") || provenWithdrawalEvent.To != l1Contracts.L1CrossDomainMessenger { if provenWithdrawalEvent.From != common.HexToAddress("0x4200000000000000000000000000000000000007") || provenWithdrawalEvent.To != l1Contracts.L1CrossDomainMessenger {
// non-bridge withdrawal // non-bridge withdrawal
continue continue
} }
// Query for the the proven withdrawal on-chain // Check if the L2Processor is behind or really has missed an event. Since L2 timestamps
provenWithdrawal, err := OptimismPortalQueryProvenWithdrawal(rawEthClient, l1Contracts.OptimismPortal, withdrawalHash) // are derived from L1, we can simply compare timestamps
if err != nil { if provenWithdrawalEvent.RawEvent.Timestamp > latestL2Header.Timestamp {
return err processLog.Warn("behind on indexed L2StandardBridge withdrawals")
}
latestL2Header, err := db.Blocks.LatestL2BlockHeader()
if err != nil {
return err
}
if latestL2Header == nil || provenWithdrawal.L2OutputIndex.Cmp(latestL2Header.Number.Int) > 0 {
processLog.Warn("behind on indexed L2 withdrawals")
return errors.New("waiting for L2Processor to catch up") return errors.New("waiting for L2Processor to catch up")
} else { } else {
processLog.Crit("missing indexed withdrawal for this proven event") processLog.Crit("missing indexed L2StandardBridge withdrawal for this proven event")
return errors.New("missing withdrawal message") return errors.New("missing withdrawal message")
} }
} }
err = db.Bridge.MarkProvenWithdrawalEvent(withdrawal.GUID, provenWithdrawalEvent.RawEvent.GUID) err = db.BridgeTransfers.MarkProvenL2BridgeWithdrawalEvent(withdrawal.GUID, provenWithdrawalEvent.RawEvent.GUID)
if err != nil { if err != nil {
return err return err
} }
...@@ -346,7 +338,7 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -346,7 +338,7 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
for _, finalizedBridgeEvent := range finalizedWithdrawalEvents { for _, finalizedBridgeEvent := range finalizedWithdrawalEvents {
nonce := finalizedBridgeEvent.CrossDomainMessengerNonce nonce := finalizedBridgeEvent.CrossDomainMessengerNonce
withdrawal, err := db.Bridge.WithdrawalByMessageNonce(nonce) withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalByCrossDomainMessengerNonce(nonce)
if err != nil { if err != nil {
processLog.Error("error querying associated withdrawal messsage using nonce", "cross_domain_messenger_nonce", nonce) processLog.Error("error querying associated withdrawal messsage using nonce", "cross_domain_messenger_nonce", nonce)
return err return err
...@@ -359,7 +351,7 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -359,7 +351,7 @@ func l1BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
return errors.New("missing withdrawal message") return errors.New("missing withdrawal message")
} }
err = db.Bridge.MarkFinalizedWithdrawalEvent(withdrawal.GUID, finalizedBridgeEvent.RawEvent.GUID) err = db.BridgeTransfers.MarkFinalizedL2BridgeWithdrawalEvent(withdrawal.GUID, finalizedBridgeEvent.RawEvent.GUID)
if err != nil { if err != nil {
processLog.Error("error finalizing withdrawal", "err", err) processLog.Error("error finalizing withdrawal", "err", err)
return err return err
......
...@@ -183,7 +183,7 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -183,7 +183,7 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
return err return err
} }
withdrawals := make([]*database.Withdrawal, len(initiatedWithdrawalEvents)) withdrawals := make([]*database.L2BridgeWithdrawal, len(initiatedWithdrawalEvents))
for i, initiatedBridgeEvent := range initiatedWithdrawalEvents { for i, initiatedBridgeEvent := range initiatedWithdrawalEvents {
log := events.eventLog[initiatedBridgeEvent.RawEvent.GUID] log := events.eventLog[initiatedBridgeEvent.RawEvent.GUID]
...@@ -195,12 +195,12 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -195,12 +195,12 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
return err return err
} }
withdrawals[i] = &database.Withdrawal{ withdrawals[i] = &database.L2BridgeWithdrawal{
GUID: uuid.New(), GUID: uuid.New(),
InitiatedL2EventGUID: initiatedBridgeEvent.RawEvent.GUID, InitiatedL2EventGUID: initiatedBridgeEvent.RawEvent.GUID,
SentMessageNonce: database.U256{Int: initiatedBridgeEvent.CrossDomainMessengerNonce}, CrossDomainMessengerNonce: database.U256{Int: initiatedBridgeEvent.CrossDomainMessengerNonce},
WithdrawalHash: msgPassedData.WithdrawalHash, WithdrawalHash: msgPassedData.WithdrawalHash,
TokenPair: database.TokenPair{L1TokenAddress: initiatedBridgeEvent.LocalToken, L2TokenAddress: initiatedBridgeEvent.RemoteToken}, TokenPair: database.TokenPair{L1TokenAddress: initiatedBridgeEvent.LocalToken, L2TokenAddress: initiatedBridgeEvent.RemoteToken},
Tx: database.Transaction{ Tx: database.Transaction{
FromAddress: initiatedBridgeEvent.From, FromAddress: initiatedBridgeEvent.From,
ToAddress: initiatedBridgeEvent.To, ToAddress: initiatedBridgeEvent.To,
...@@ -213,7 +213,7 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -213,7 +213,7 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
if len(withdrawals) > 0 { if len(withdrawals) > 0 {
processLog.Info("detected L2StandardBridge withdrawals", "num", len(withdrawals)) processLog.Info("detected L2StandardBridge withdrawals", "num", len(withdrawals))
err := db.Bridge.StoreWithdrawals(withdrawals) err := db.BridgeTransfers.StoreL2BridgeWithdrawals(withdrawals)
if err != nil { if err != nil {
return err return err
} }
...@@ -225,30 +225,33 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl ...@@ -225,30 +225,33 @@ func l2BridgeProcessContractEvents(processLog log.Logger, db *database.DB, ethCl
return err return err
} }
latestL1Header, err := db.Blocks.LatestL1BlockHeader()
if err != nil {
return err
} else if len(finalizationBridgeEvents) > 0 && latestL1Header == nil {
return errors.New("no indexed L1 state to process any L2 bridge finalizations")
}
for _, finalizedBridgeEvent := range finalizationBridgeEvents { for _, finalizedBridgeEvent := range finalizationBridgeEvents {
nonce := finalizedBridgeEvent.CrossDomainMessengerNonce nonce := finalizedBridgeEvent.CrossDomainMessengerNonce
deposit, err := db.Bridge.DepositByMessageNonce(nonce) deposit, err := db.BridgeTransfers.L1BridgeDepositByCrossDomainMessengerNonce(nonce)
if err != nil { if err != nil {
processLog.Error("error querying associated deposit messsage using nonce", "cross_domain_messenger_nonce", nonce) processLog.Error("error querying associated deposit messsage using nonce", "cross_domain_messenger_nonce", nonce)
return err return err
} else if deposit == nil { } else if deposit == nil {
latestNonce, err := db.Bridge.LatestDepositMessageNonce() // Check if the L1Processor is behind or really has missed an event. Since L2 timestamps
if err != nil { // are derived from L1, we can simply compare timestamps
return err if finalizedBridgeEvent.RawEvent.Timestamp > latestL1Header.Timestamp {
} processLog.Warn("behind on indexed L1StandardBridge deposits")
// Check if the L1Processor is behind or really has missed an event
if latestNonce == nil || nonce.Cmp(latestNonce) > 0 {
processLog.Warn("behind on indexed L1 deposits")
return errors.New("waiting for L1Processor to catch up") return errors.New("waiting for L1Processor to catch up")
} else { } else {
processLog.Crit("missing indexed deposit for this finalization event") processLog.Crit("missing indexed L1StandardBridge deposit for this finalization event")
return errors.New("missing deposit message") return errors.New("missing deposit message")
} }
} }
err = db.Bridge.MarkFinalizedDepositEvent(deposit.GUID, finalizedBridgeEvent.RawEvent.GUID) err = db.BridgeTransfers.MarkFinalizedL1BridgeDepositEvent(deposit.GUID, finalizedBridgeEvent.RawEvent.GUID)
if err != nil { if err != nil {
processLog.Error("error finalizing deposit", "err", err) processLog.Error("error finalizing deposit", "err", err)
return err return err
......
This diff is collapsed.
...@@ -31,7 +31,7 @@ var ( ...@@ -31,7 +31,7 @@ var (
// AlphabetVMMetaData contains all meta data concerning the AlphabetVM contract. // AlphabetVMMetaData contains all meta data concerning the AlphabetVM contract.
var AlphabetVMMetaData = &bind.MetaData{ var AlphabetVMMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"Claim\",\"name\":\"_absolutePrestate\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"oracle\",\"outputs\":[{\"internalType\":\"contractIPreimageOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_stateData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"step\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"postState_\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", ABI: "[{\"inputs\":[{\"internalType\":\"Claim\",\"name\":\"_absolutePrestate\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"oracle\",\"outputs\":[{\"internalType\":\"contractIPreimageOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_stateData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"step\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"postState_\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
Bin: "0x60a060405234801561001057600080fd5b5060405161039838038061039883398101604081905261002f9161007a565b608081905261003c610062565b600080546001600160a01b0319166001600160a01b039290921691909117905550610093565b6460016000f36000908152600580601b83f091505090565b60006020828403121561008c57600080fd5b5051919050565b6080516102eb6100ad600039600060ad01526102eb6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063f8e0cb9614610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100986100933660046101a8565b6100a6565b60405190815260200161007c565b60008060007f000000000000000000000000000000000000000000000000000000000000000087876040516100dc929190610214565b60405180910390200361010057600091506100f986880188610224565b905061011f565b61010c8688018861023d565b90925090508161011b8161028e565b9250505b8161012b8260016102c6565b6040805160208101939093528201526060016040516020818303038152906040528051906020012092505050949350505050565b60008083601f84011261017157600080fd5b50813567ffffffffffffffff81111561018957600080fd5b6020830191508360208285010111156101a157600080fd5b9250929050565b600080600080604085870312156101be57600080fd5b843567ffffffffffffffff808211156101d657600080fd5b6101e28883890161015f565b909650945060208701359150808211156101fb57600080fd5b506102088782880161015f565b95989497509550505050565b8183823760009101908152919050565b60006020828403121561023657600080fd5b5035919050565b6000806040838503121561025057600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102bf576102bf61025f565b5060010190565b600082198211156102d9576102d961025f565b50019056fea164736f6c634300080f000a", Bin: "0x60a060405234801561001057600080fd5b50604051610a73380380610a7383398101604081905261002f91610090565b608081905260405161004090610083565b604051809103906000f08015801561005c573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b0392909216919091179055506100a9565b6106c5806103ae83390190565b6000602082840312156100a257600080fd5b5051919050565b6080516102eb6100c3600039600060ad01526102eb6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063f8e0cb9614610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100986100933660046101a8565b6100a6565b60405190815260200161007c565b60008060007f000000000000000000000000000000000000000000000000000000000000000087876040516100dc929190610214565b60405180910390200361010057600091506100f986880188610224565b905061011f565b61010c8688018861023d565b90925090508161011b8161028e565b9250505b8161012b8260016102c6565b6040805160208101939093528201526060016040516020818303038152906040528051906020012092505050949350505050565b60008083601f84011261017157600080fd5b50813567ffffffffffffffff81111561018957600080fd5b6020830191508360208285010111156101a157600080fd5b9250929050565b600080600080604085870312156101be57600080fd5b843567ffffffffffffffff808211156101d657600080fd5b6101e28883890161015f565b909650945060208701359150808211156101fb57600080fd5b506102088782880161015f565b95989497509550505050565b8183823760009101908152919050565b60006020828403121561023657600080fd5b5035919050565b6000806040838503121561025057600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102bf576102bf61025f565b5060010190565b600082198211156102d9576102d961025f565b50019056fea164736f6c634300080f000a608060405234801561001057600080fd5b506106a5806100206000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063e03110e11161005b578063e03110e114610111578063e159261114610139578063fe4ac08e1461014e578063fef2b4ed146101c357600080fd5b806361238bde146100825780638542cf50146100c05780639a1f5e7f146100fe575b600080fd5b6100ad610090366004610551565b600160209081526000928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b6100ee6100ce366004610551565b600260209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016100b7565b6100ad61010c366004610573565b6101e3565b61012461011f366004610551565b6102b6565b604080519283526020830191909152016100b7565b61014c6101473660046105a5565b6103a7565b005b61014c61015c366004610573565b6000838152600260209081526040808320878452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558684528252808320968352958152858220939093559283529082905291902055565b6100ad6101d1366004610621565b60006020819052908152604090205481565b60006101ee856104b0565b90506101fb836008610669565b8211806102085750602083115b1561023f576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602081815260c085901b82526008959095528251828252600286526040808320858452875280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845287528083209483529386528382205581815293849052922055919050565b6000828152600260209081526040808320848452909152812054819060ff1661033f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f7072652d696d616765206d757374206578697374000000000000000000000000604482015260640160405180910390fd5b506000838152602081815260409091205461035b816008610669565b610366856020610669565b106103845783610377826008610669565b6103819190610681565b91505b506000938452600160209081526040808620948652939052919092205492909150565b604435600080600883018611156103c65763fe2549876000526004601cfd5b60c083901b6080526088838682378087017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80151908490207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f02000000000000000000000000000000000000000000000000000000000000001760008181526002602090815260408083208b8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209a83529981528982209390935590815290819052959095209190915550505050565b7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82161761054b81600090815233602052604090207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b92915050565b6000806040838503121561056457600080fd5b50508035926020909101359150565b6000806000806080858703121561058957600080fd5b5050823594602084013594506040840135936060013592509050565b6000806000604084860312156105ba57600080fd5b83359250602084013567ffffffffffffffff808211156105d957600080fd5b818601915086601f8301126105ed57600080fd5b8135818111156105fc57600080fd5b87602082850101111561060e57600080fd5b6020830194508093505050509250925092565b60006020828403121561063357600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561067c5761067c61063a565b500190565b6000828210156106935761069361063a565b50039056fea164736f6c634300080f000a",
} }
// AlphabetVMABI is the input ABI used to generate the binding from. // AlphabetVMABI is the input ABI used to generate the binding from.
......
This diff is collapsed.
This diff is collapsed.
...@@ -470,6 +470,7 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) { ...@@ -470,6 +470,7 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) {
// L1Deployments represents a set of L1 contracts that are deployed. // L1Deployments represents a set of L1 contracts that are deployed.
type L1Deployments struct { type L1Deployments struct {
AddressManager common.Address `json:"AddressManager"` AddressManager common.Address `json:"AddressManager"`
BlockOracle common.Address `json:"BlockOracle"`
DisputeGameFactory common.Address `json:"DisputeGameFactory"` DisputeGameFactory common.Address `json:"DisputeGameFactory"`
DisputeGameFactoryProxy common.Address `json:"DisputeGameFactoryProxy"` DisputeGameFactoryProxy common.Address `json:"DisputeGameFactoryProxy"`
L1CrossDomainMessenger common.Address `json:"L1CrossDomainMessenger"` L1CrossDomainMessenger common.Address `json:"L1CrossDomainMessenger"`
...@@ -512,7 +513,7 @@ func (d *L1Deployments) Check() error { ...@@ -512,7 +513,7 @@ func (d *L1Deployments) Check() error {
for i := 0; i < val.NumField(); i++ { for i := 0; i < val.NumField(); i++ {
name := val.Type().Field(i).Name name := val.Type().Field(i).Name
// Skip the non production ready contracts // Skip the non production ready contracts
if name == "DisputeGameFactory" || name == "DisputeGameFactoryProxy" { if name == "DisputeGameFactory" || name == "DisputeGameFactoryProxy" || name == "BlockOracle" {
continue continue
} }
if val.Field(i).Interface().(common.Address) == (common.Address{}) { if val.Field(i).Interface().(common.Address) == (common.Address{}) {
......
...@@ -13,17 +13,16 @@ import ( ...@@ -13,17 +13,16 @@ import (
) )
var ( var (
l1EthRpc = "http://example.com:8545" l1EthRpc = "http://example.com:8545"
gameAddressValue = "0xaa00000000000000000000000000000000000000" gameAddressValue = "0xaa00000000000000000000000000000000000000"
preimageOracleAddressValue = "0xbb00000000000000000000000000000000000000" cannonBin = "./bin/cannon"
cannonBin = "./bin/cannon" cannonServer = "./bin/op-program"
cannonServer = "./bin/op-program" cannonPreState = "./pre.json"
cannonPreState = "./pre.json" cannonDatadir = "./test_data"
cannonDatadir = "./test_data" cannonL2 = "http://example.com:9545"
cannonL2 = "http://example.com:9545" alphabetTrace = "abcdefghijz"
alphabetTrace = "abcdefghijz" agreeWithProposedOutput = "true"
agreeWithProposedOutput = "true" gameDepth = "4"
gameDepth = "4"
) )
func TestLogLevel(t *testing.T) { func TestLogLevel(t *testing.T) {
...@@ -43,14 +42,14 @@ func TestLogLevel(t *testing.T) { ...@@ -43,14 +42,14 @@ func TestLogLevel(t *testing.T) {
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) { func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet)) cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet))
defaultCfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), common.HexToAddress(preimageOracleAddressValue), config.TraceTypeAlphabet, true, 4) defaultCfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true, 4)
// Add in the extra CLI options required when using alphabet trace type // Add in the extra CLI options required when using alphabet trace type
defaultCfg.AlphabetTrace = alphabetTrace defaultCfg.AlphabetTrace = alphabetTrace
require.Equal(t, defaultCfg, cfg) require.Equal(t, defaultCfg, cfg)
} }
func TestDefaultConfigIsValid(t *testing.T) { func TestDefaultConfigIsValid(t *testing.T) {
cfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), common.HexToAddress(preimageOracleAddressValue), config.TraceTypeAlphabet, true, 4) cfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true, 4)
// Add in options that are required based on the specific trace type // Add in options that are required based on the specific trace type
// To avoid needing to specify unused options, these aren't included in the params for NewConfig // To avoid needing to specify unused options, these aren't included in the params for NewConfig
cfg.AlphabetTrace = alphabetTrace cfg.AlphabetTrace = alphabetTrace
...@@ -140,26 +139,6 @@ func TestGameDepth(t *testing.T) { ...@@ -140,26 +139,6 @@ func TestGameDepth(t *testing.T) {
}) })
} }
func TestPreimageOracleAddress(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--preimage-oracle-address"))
})
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag preimage-oracle-address is required", addRequiredArgsExcept(config.TraceTypeCannon, "--preimage-oracle-address"))
})
t.Run("Valid", func(t *testing.T) {
addr := common.Address{0xbb, 0xcc, 0xdd}
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeCannon, "--preimage-oracle-address", "--preimage-oracle-address="+addr.Hex()))
require.Equal(t, addr, cfg.PreimageOracleAddress)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, "invalid address: foo", addRequiredArgsExcept(config.TraceTypeCannon, "--preimage-oracle-address", "--preimage-oracle-address=foo"))
})
}
func TestCannonBin(t *testing.T) { func TestCannonBin(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) { t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-bin")) configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-bin"))
...@@ -288,7 +267,6 @@ func requiredArgs(traceType config.TraceType) map[string]string { ...@@ -288,7 +267,6 @@ func requiredArgs(traceType config.TraceType) map[string]string {
"--agree-with-proposed-output": agreeWithProposedOutput, "--agree-with-proposed-output": agreeWithProposedOutput,
"--l1-eth-rpc": l1EthRpc, "--l1-eth-rpc": l1EthRpc,
"--game-address": gameAddressValue, "--game-address": gameAddressValue,
"--preimage-oracle-address": preimageOracleAddressValue,
"--trace-type": traceType.String(), "--trace-type": traceType.String(),
} }
switch traceType { switch traceType {
......
...@@ -61,7 +61,6 @@ const DefaultCannonSnapshotFreq = uint(10_000) ...@@ -61,7 +61,6 @@ const DefaultCannonSnapshotFreq = uint(10_000)
type Config struct { type Config struct {
L1EthRpc string // L1 RPC Url L1EthRpc string // L1 RPC Url
GameAddress common.Address // Address of the fault game GameAddress common.Address // Address of the fault game
PreimageOracleAddress common.Address // Address of the pre-image oracle
AgreeWithProposedOutput bool // Temporary config if we agree or disagree with the posted output AgreeWithProposedOutput bool // Temporary config if we agree or disagree with the posted output
GameDepth int // Depth of the game tree GameDepth int // Depth of the game tree
...@@ -84,15 +83,13 @@ type Config struct { ...@@ -84,15 +83,13 @@ type Config struct {
func NewConfig( func NewConfig(
l1EthRpc string, l1EthRpc string,
gameAddress common.Address, gameAddress common.Address,
preimageOracleAddress common.Address,
traceType TraceType, traceType TraceType,
agreeWithProposedOutput bool, agreeWithProposedOutput bool,
gameDepth int, gameDepth int,
) Config { ) Config {
return Config{ return Config{
L1EthRpc: l1EthRpc, L1EthRpc: l1EthRpc,
GameAddress: gameAddress, GameAddress: gameAddress,
PreimageOracleAddress: preimageOracleAddress,
AgreeWithProposedOutput: agreeWithProposedOutput, AgreeWithProposedOutput: agreeWithProposedOutput,
GameDepth: gameDepth, GameDepth: gameDepth,
...@@ -116,9 +113,6 @@ func (c Config) Check() error { ...@@ -116,9 +113,6 @@ func (c Config) Check() error {
return ErrMissingTraceType return ErrMissingTraceType
} }
if c.TraceType == TraceTypeCannon { if c.TraceType == TraceTypeCannon {
if c.PreimageOracleAddress == (common.Address{}) {
return ErrMissingPreimageOracleAddress
}
if c.CannonBin == "" { if c.CannonBin == "" {
return ErrMissingCannonBin return ErrMissingCannonBin
} }
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
var ( var (
validL1EthRpc = "http://localhost:8545" validL1EthRpc = "http://localhost:8545"
validGameAddress = common.HexToAddress("0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139") validGameAddress = common.HexToAddress("0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139")
validPreimageOracleAddress = common.HexToAddress("0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139")
validAlphabetTrace = "abcdefgh" validAlphabetTrace = "abcdefgh"
validCannonBin = "./bin/cannon" validCannonBin = "./bin/cannon"
validCannonOpProgramBin = "./bin/op-program" validCannonOpProgramBin = "./bin/op-program"
...@@ -23,7 +22,7 @@ var ( ...@@ -23,7 +22,7 @@ var (
) )
func validConfig(traceType TraceType) Config { func validConfig(traceType TraceType) Config {
cfg := NewConfig(validL1EthRpc, validGameAddress, validPreimageOracleAddress, traceType, agreeWithProposedOutput, gameDepth) cfg := NewConfig(validL1EthRpc, validGameAddress, traceType, agreeWithProposedOutput, gameDepth)
switch traceType { switch traceType {
case TraceTypeAlphabet: case TraceTypeAlphabet:
cfg.AlphabetTrace = validAlphabetTrace cfg.AlphabetTrace = validAlphabetTrace
...@@ -74,12 +73,6 @@ func TestAlphabetTraceRequired(t *testing.T) { ...@@ -74,12 +73,6 @@ func TestAlphabetTraceRequired(t *testing.T) {
require.ErrorIs(t, config.Check(), ErrMissingAlphabetTrace) require.ErrorIs(t, config.Check(), ErrMissingAlphabetTrace)
} }
func TestCannonPreimageOracleAddressRequired(t *testing.T) {
config := validConfig(TraceTypeCannon)
config.PreimageOracleAddress = common.Address{}
require.ErrorIs(t, config.Check(), ErrMissingPreimageOracleAddress)
}
func TestCannonBinRequired(t *testing.T) { func TestCannonBinRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(TraceTypeCannon)
config.CannonBin = "" config.CannonBin = ""
......
...@@ -23,16 +23,18 @@ type Agent struct { ...@@ -23,16 +23,18 @@ type Agent struct {
solver *solver.Solver solver *solver.Solver
loader Loader loader Loader
responder Responder responder Responder
updater types.OracleUpdater
maxDepth int maxDepth int
agreeWithProposedOutput bool agreeWithProposedOutput bool
log log.Logger log log.Logger
} }
func NewAgent(loader Loader, maxDepth int, trace types.TraceProvider, responder Responder, agreeWithProposedOutput bool, log log.Logger) *Agent { func NewAgent(loader Loader, maxDepth int, trace types.TraceProvider, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent {
return &Agent{ return &Agent{
solver: solver.NewSolver(maxDepth, trace), solver: solver.NewSolver(maxDepth, trace),
loader: loader, loader: loader,
responder: responder, responder: responder,
updater: updater,
maxDepth: maxDepth, maxDepth: maxDepth,
agreeWithProposedOutput: agreeWithProposedOutput, agreeWithProposedOutput: agreeWithProposedOutput,
log: log, log: log,
...@@ -132,6 +134,17 @@ func (a *Agent) step(ctx context.Context, claim types.Claim, game types.Game) er ...@@ -132,6 +134,17 @@ func (a *Agent) step(ctx context.Context, claim types.Claim, game types.Game) er
return nil return nil
} }
oracleData, err := a.solver.GetOracleData(ctx, claim)
if err != nil {
a.log.Debug("Failed to get oracle data", "err", err)
return nil
}
a.log.Info("Updating oracle data", "oracleKey", oracleData.OracleKey, "oracleData", oracleData.OracleData)
if err := a.updater.UpdateOracle(ctx, *oracleData); err != nil {
return fmt.Errorf("failed to load oracle data: %w", err)
}
a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth) a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth)
step, err := a.solver.AttemptStep(ctx, claim, agreeWithClaimLevel) step, err := a.solver.AttemptStep(ctx, claim, agreeWithClaimLevel)
if err != nil { if err != nil {
......
...@@ -56,31 +56,44 @@ func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs) ...@@ -56,31 +56,44 @@ func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs)
} }
func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) error { func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) error {
start, err := e.selectSnapshot(e.logger, filepath.Join(e.dataDir, snapsDir), e.absolutePreState, i) snapshotDir := filepath.Join(dir, snapsDir)
start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, i)
if err != nil { if err != nil {
return fmt.Errorf("find starting snapshot: %w", err) return fmt.Errorf("find starting snapshot: %w", err)
} }
proofDir := filepath.Join(dir, proofsDir)
dataDir := filepath.Join(e.dataDir, preimagesDir)
args := []string{ args := []string{
"run", "run",
"--input", start, "--input", start,
"--output", filepath.Join(dir, "out.json"),
"--meta", "",
"--proof-at", "=" + strconv.FormatUint(i, 10), "--proof-at", "=" + strconv.FormatUint(i, 10),
"--stop-at", "=" + strconv.FormatUint(i+1, 10), "--stop-at", "=" + strconv.FormatUint(i+1, 10),
"--proof-fmt", filepath.Join(dir, proofsDir, "%d.json"), "--proof-fmt", filepath.Join(proofDir, "%d.json"),
"--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10), "--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10),
"--snapshot-fmt", filepath.Join(e.dataDir, snapsDir, "%d.json"), "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json"),
"--", "--",
e.server, e.server,
"--l1", e.l1, "--l1", e.l1,
"--l2", e.l2, "--l2", e.l2,
"--datadir", filepath.Join(e.dataDir, preimagesDir), "--datadir", dataDir,
"--l1.head", e.inputs.l1Head.Hex(), "--l1.head", e.inputs.l1Head.Hex(),
"--l2.head", e.inputs.l2Head.Hex(), "--l2.head", e.inputs.l2Head.Hex(),
"--l2.outputroot", e.inputs.l2OutputRoot.Hex(), "--l2.outputroot", e.inputs.l2OutputRoot.Hex(),
"--l2.claim", e.inputs.l2Claim.Hex(), "--l2.claim", e.inputs.l2Claim.Hex(),
"--l2.blocknumber", e.inputs.l2BlockNumber.Text(10), "--l2.blocknumber", e.inputs.l2BlockNumber.Text(10),
"--l2.chainid", e.inputs.l2ChainId.Text(10),
} }
if err := os.MkdirAll(snapshotDir, 0755); err != nil {
return fmt.Errorf("could not create snapshot directory %v: %w", snapshotDir, err)
}
if err := os.MkdirAll(dataDir, 0755); err != nil {
return fmt.Errorf("could not create preimage cache directory %v: %w", dataDir, err)
}
if err := os.MkdirAll(proofDir, 0755); err != nil {
return fmt.Errorf("could not create proofs directory %v: %w", proofDir, err)
}
e.logger.Info("Generating trace", "proof", i, "cmd", e.cannon, "args", args) e.logger.Info("Generating trace", "proof", i, "cmd", e.cannon, "args", args)
return e.cmdExecutor(ctx, e.logger.New("proof", i), e.cannon, args...) return e.cmdExecutor(ctx, e.logger.New("proof", i), e.cannon, args...)
} }
......
...@@ -20,7 +20,7 @@ const execTestCannonPrestate = "/foo/pre.json" ...@@ -20,7 +20,7 @@ const execTestCannonPrestate = "/foo/pre.json"
func TestGenerateProof(t *testing.T) { func TestGenerateProof(t *testing.T) {
input := "starting.json" input := "starting.json"
cfg := config.NewConfig("http://localhost:8888", common.Address{0xaa}, common.Address{0xbb}, config.TraceTypeCannon, true, 5) cfg := config.NewConfig("http://localhost:8888", common.Address{0xaa}, config.TraceTypeCannon, true, 5)
cfg.CannonDatadir = t.TempDir() cfg.CannonDatadir = t.TempDir()
cfg.CannonAbsolutePreState = "pre.json" cfg.CannonAbsolutePreState = "pre.json"
cfg.CannonBin = "./bin/cannon" cfg.CannonBin = "./bin/cannon"
...@@ -30,7 +30,6 @@ func TestGenerateProof(t *testing.T) { ...@@ -30,7 +30,6 @@ func TestGenerateProof(t *testing.T) {
inputs := localGameInputs{ inputs := localGameInputs{
l1Head: common.Hash{0x11}, l1Head: common.Hash{0x11},
l2ChainId: big.NewInt(2342),
l2Head: common.Hash{0x22}, l2Head: common.Hash{0x22},
l2OutputRoot: common.Hash{0x33}, l2OutputRoot: common.Hash{0x33},
l2Claim: common.Hash{0x44}, l2Claim: common.Hash{0x44},
...@@ -54,9 +53,15 @@ func TestGenerateProof(t *testing.T) { ...@@ -54,9 +53,15 @@ func TestGenerateProof(t *testing.T) {
} }
err := executor.GenerateProof(context.Background(), cfg.CannonDatadir, 150_000_000) err := executor.GenerateProof(context.Background(), cfg.CannonDatadir, 150_000_000)
require.NoError(t, err) require.NoError(t, err)
require.DirExists(t, filepath.Join(cfg.CannonDatadir, preimagesDir))
require.DirExists(t, filepath.Join(cfg.CannonDatadir, proofsDir))
require.DirExists(t, filepath.Join(cfg.CannonDatadir, snapsDir))
require.Equal(t, cfg.CannonBin, binary) require.Equal(t, cfg.CannonBin, binary)
require.Equal(t, "run", subcommand) require.Equal(t, "run", subcommand)
require.Equal(t, input, args["--input"]) require.Equal(t, input, args["--input"])
require.Contains(t, args, "--meta")
require.Equal(t, "", args["--meta"])
require.Equal(t, filepath.Join(cfg.CannonDatadir, "out.json"), args["--output"])
require.Equal(t, "=150000000", args["--proof-at"]) require.Equal(t, "=150000000", args["--proof-at"])
require.Equal(t, "=150000001", args["--stop-at"]) require.Equal(t, "=150000001", args["--stop-at"])
require.Equal(t, "%500", args["--snapshot-at"]) require.Equal(t, "%500", args["--snapshot-at"])
...@@ -73,7 +78,6 @@ func TestGenerateProof(t *testing.T) { ...@@ -73,7 +78,6 @@ func TestGenerateProof(t *testing.T) {
require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"]) require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"])
require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"]) require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"])
require.Equal(t, "3333", args["--l2.blocknumber"]) require.Equal(t, "3333", args["--l2.blocknumber"])
require.Equal(t, "2342", args["--l2.chainid"])
} }
func TestRunCmdLogsOutput(t *testing.T) { func TestRunCmdLogsOutput(t *testing.T) {
......
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
type localGameInputs struct { type localGameInputs struct {
l1Head common.Hash l1Head common.Hash
l2ChainId *big.Int
l2Head common.Hash l2Head common.Hash
l2OutputRoot common.Hash l2OutputRoot common.Hash
l2Claim common.Hash l2Claim common.Hash
...@@ -39,10 +38,6 @@ func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameI ...@@ -39,10 +38,6 @@ func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameI
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err) return localGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err)
} }
l2ChainId, err := l2Client.ChainID(ctx)
if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L2 chain ID: %w", err)
}
proposals, err := caller.Proposals(opts) proposals, err := caller.Proposals(opts)
if err != nil { if err != nil {
...@@ -58,7 +53,6 @@ func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameI ...@@ -58,7 +53,6 @@ func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameI
return localGameInputs{ return localGameInputs{
l1Head: l1Head, l1Head: l1Head,
l2ChainId: l2ChainId,
l2Head: l2Head, l2Head: l2Head,
l2OutputRoot: agreedOutput.OutputRoot, l2OutputRoot: agreedOutput.OutputRoot,
l2Claim: claimedOutput.OutputRoot, l2Claim: claimedOutput.OutputRoot,
......
...@@ -40,7 +40,6 @@ func TestFetchLocalInputs(t *testing.T) { ...@@ -40,7 +40,6 @@ func TestFetchLocalInputs(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, l1Client.l1Head, inputs.l1Head) require.Equal(t, l1Client.l1Head, inputs.l1Head)
require.Equal(t, l2Client.chainID, inputs.l2ChainId)
require.Equal(t, l2Client.header.Hash(), inputs.l2Head) require.Equal(t, l2Client.header.Hash(), inputs.l2Head)
require.EqualValues(t, l1Client.starting.OutputRoot, inputs.l2OutputRoot) require.EqualValues(t, l1Client.starting.OutputRoot, inputs.l2OutputRoot)
require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.l2Claim) require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.l2Claim)
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"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"
...@@ -28,8 +29,36 @@ type cannonUpdater struct { ...@@ -28,8 +29,36 @@ type cannonUpdater struct {
preimageOracleAddr common.Address preimageOracleAddr common.Address
} }
// NewOracleUpdater returns a new updater. // NewOracleUpdater returns a new updater. The pre-image oracle address is loaded from the fault dispute game.
func NewOracleUpdater( func NewOracleUpdater(
ctx context.Context,
logger log.Logger,
txMgr txmgr.TxManager,
fdgAddr common.Address,
client bind.ContractCaller,
) (*cannonUpdater, error) {
gameCaller, err := bindings.NewFaultDisputeGameCaller(fdgAddr, client)
if err != nil {
return nil, fmt.Errorf("create caller for game %v: %w", fdgAddr, err)
}
opts := &bind.CallOpts{Context: ctx}
vm, err := gameCaller.VM(opts)
if err != nil {
return nil, fmt.Errorf("failed to load VM address from game %v: %w", fdgAddr, err)
}
mipsCaller, err := bindings.NewMIPSCaller(vm, client)
if err != nil {
return nil, fmt.Errorf("failed to create MIPS caller for address %v: %w", vm, err)
}
oracleAddr, err := mipsCaller.Oracle(opts)
if err != nil {
return nil, fmt.Errorf("failed to load pre-image oracle address from game %v: %w", fdgAddr, err)
}
return NewOracleUpdaterWithOracle(logger, txMgr, fdgAddr, oracleAddr)
}
// NewOracleUpdaterWithOracle returns a new updater using a specified pre-image oracle address.
func NewOracleUpdaterWithOracle(
logger log.Logger, logger log.Logger,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
fdgAddr common.Address, fdgAddr common.Address,
......
...@@ -64,7 +64,7 @@ func newTestCannonUpdater(t *testing.T, sendFails bool) (*cannonUpdater, *mockTx ...@@ -64,7 +64,7 @@ func newTestCannonUpdater(t *testing.T, sendFails bool) (*cannonUpdater, *mockTx
from: mockFdgAddress, from: mockFdgAddress,
sendFails: sendFails, sendFails: sendFails,
} }
updater, err := NewOracleUpdater(logger, txMgr, mockFdgAddress, mockPreimageOracleAddress) updater, err := NewOracleUpdaterWithOracle(logger, txMgr, mockFdgAddress, mockPreimageOracleAddress)
require.NoError(t, err) require.NoError(t, err)
return updater, txMgr return updater, txMgr
} }
......
...@@ -49,7 +49,7 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se ...@@ -49,7 +49,7 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
if err != nil { if err != nil {
return nil, fmt.Errorf("create cannon trace provider: %w", err) return nil, fmt.Errorf("create cannon trace provider: %w", err)
} }
updater, err = cannon.NewOracleUpdater(logger, txMgr, cfg.GameAddress, cfg.PreimageOracleAddress) updater, err = cannon.NewOracleUpdater(ctx, logger, txMgr, cfg.GameAddress, client)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create the cannon updater: %w", err) return nil, fmt.Errorf("failed to create the cannon updater: %w", err)
} }
...@@ -64,8 +64,7 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se ...@@ -64,8 +64,7 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
} }
// newTypedService creates a new Service from a provided trace provider. // newTypedService creates a new Service from a provided trace provider.
func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, client *ethclient.Client, provider types.TraceProvider, uploader types.OracleUpdater, txMgr txmgr.TxManager) (*service, error) { func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, client *ethclient.Client, provider types.TraceProvider, updater types.OracleUpdater, txMgr txmgr.TxManager) (*service, error) {
contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client) contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err) return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err)
...@@ -83,7 +82,7 @@ func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config, ...@@ -83,7 +82,7 @@ func newTypedService(ctx context.Context, logger log.Logger, cfg *config.Config,
return nil, fmt.Errorf("failed to bind the fault contract: %w", err) return nil, fmt.Errorf("failed to bind the fault contract: %w", err)
} }
agent := NewAgent(loader, cfg.GameDepth, provider, responder, cfg.AgreeWithProposedOutput, gameLogger) agent := NewAgent(loader, cfg.GameDepth, provider, responder, updater, cfg.AgreeWithProposedOutput, gameLogger)
return &service{ return &service{
agent: agent, agent: agent,
......
...@@ -28,6 +28,12 @@ func NewSolver(gameDepth int, traceProvider types.TraceProvider) *Solver { ...@@ -28,6 +28,12 @@ func NewSolver(gameDepth int, traceProvider types.TraceProvider) *Solver {
} }
} }
// GetOracleData returns the oracle data for the provided claim.
// It passes through to the [TraceProvider] by finding the trace index for the claim.
func (s *Solver) GetOracleData(ctx context.Context, claim types.Claim) (*types.PreimageOracleData, error) {
return s.trace.GetOracleData(ctx, claim.TraceIndex(s.gameDepth))
}
// NextMove returns the next move to make given the current state of the game. // NextMove returns the next move to make given the current state of the game.
func (s *Solver) NextMove(ctx context.Context, claim types.Claim, agreeWithClaimLevel bool) (*types.Claim, error) { func (s *Solver) NextMove(ctx context.Context, claim types.Claim, agreeWithClaimLevel bool) (*types.Claim, error) {
if agreeWithClaimLevel { if agreeWithClaimLevel {
......
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
...@@ -59,11 +58,6 @@ var ( ...@@ -59,11 +58,6 @@ var (
Usage: "Correct Alphabet Trace (alphabet trace type only)", Usage: "Correct Alphabet Trace (alphabet trace type only)",
EnvVars: prefixEnvVars("ALPHABET"), EnvVars: prefixEnvVars("ALPHABET"),
} }
PreimageOracleAddressFlag = &cli.StringFlag{
Name: "preimage-oracle-address",
Usage: "Address of the Preimage Oracle contract (only required for cannon).",
EnvVars: prefixEnvVars("PREIMAGE_ORACLE_ADDRESS"),
}
CannonBinFlag = &cli.StringFlag{ CannonBinFlag = &cli.StringFlag{
Name: "cannon-bin", Name: "cannon-bin",
Usage: "Path to cannon executable to use when generating trace data (cannon trace type only)", Usage: "Path to cannon executable to use when generating trace data (cannon trace type only)",
...@@ -109,7 +103,6 @@ var requiredFlags = []cli.Flag{ ...@@ -109,7 +103,6 @@ var requiredFlags = []cli.Flag{
// optionalFlags is a list of unchecked cli flags // optionalFlags is a list of unchecked cli flags
var optionalFlags = []cli.Flag{ var optionalFlags = []cli.Flag{
AlphabetFlag, AlphabetFlag,
PreimageOracleAddressFlag,
CannonBinFlag, CannonBinFlag,
CannonServerFlag, CannonServerFlag,
CannonPreStateFlag, CannonPreStateFlag,
...@@ -137,9 +130,6 @@ func CheckRequired(ctx *cli.Context) error { ...@@ -137,9 +130,6 @@ func CheckRequired(ctx *cli.Context) error {
gameType := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name))) gameType := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name)))
switch gameType { switch gameType {
case config.TraceTypeCannon: case config.TraceTypeCannon:
if !ctx.IsSet(PreimageOracleAddressFlag.Name) {
return fmt.Errorf("flag %s is required", PreimageOracleAddressFlag.Name)
}
if !ctx.IsSet(CannonBinFlag.Name) { if !ctx.IsSet(CannonBinFlag.Name) {
return fmt.Errorf("flag %s is required", CannonBinFlag.Name) return fmt.Errorf("flag %s is required", CannonBinFlag.Name)
} }
...@@ -179,21 +169,11 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { ...@@ -179,21 +169,11 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
traceTypeFlag := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name))) traceTypeFlag := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name)))
preimageOracleAddress := common.Address{}
preimageOracleValue := ctx.String(PreimageOracleAddressFlag.Name)
if traceTypeFlag == config.TraceTypeCannon || preimageOracleValue != "" {
preimageOracleAddress, err = opservice.ParseAddress(preimageOracleValue)
if err != nil {
return nil, err
}
}
return &config.Config{ return &config.Config{
// Required Flags // Required Flags
L1EthRpc: ctx.String(L1EthRpcFlag.Name), L1EthRpc: ctx.String(L1EthRpcFlag.Name),
TraceType: traceTypeFlag, TraceType: traceTypeFlag,
GameAddress: dgfAddress, GameAddress: dgfAddress,
PreimageOracleAddress: preimageOracleAddress,
AlphabetTrace: ctx.String(AlphabetFlag.Name), AlphabetTrace: ctx.String(AlphabetFlag.Name),
CannonBin: ctx.String(CannonBinFlag.Name), CannonBin: ctx.String(CannonBinFlag.Name),
CannonServer: ctx.String(CannonServerFlag.Name), CannonServer: ctx.String(CannonServerFlag.Name),
......
package disputegame
import (
"context"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum-optimism/optimism/op-service/clock"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
)
// deployDisputeGameContracts deploys the DisputeGameFactory, AlphabetVM and FaultDisputeGame contracts
// It configures the alphabet fault game as game type 0 (faultGameType)
// If/when the dispute game factory becomes a predeployed contract this can be removed and just use the
// predeployed version
func deployDisputeGameContracts(require *require.Assertions, ctx context.Context, clock *clock.AdvancingClock, client *ethclient.Client, opts *bind.TransactOpts, gameDuration uint64) (*bindings.DisputeGameFactory, *big.Int) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
// Deploy the proxy
_, tx, proxy, err := bindings.DeployProxy(opts, client, deployer.TestAddress)
require.NoError(err)
proxyAddr, err := bind.WaitDeployed(ctx, client, tx)
require.NoError(err)
// Deploy the dispute game factory implementation
_, tx, _, err = bindings.DeployDisputeGameFactory(opts, client)
require.NoError(err)
factoryAddr, err := bind.WaitDeployed(ctx, client, tx)
require.NoError(err)
// Point the proxy at the implementation and create bindings going via the proxy
disputeGameFactoryAbi, err := bindings.DisputeGameFactoryMetaData.GetAbi()
require.NoError(err)
data, err := disputeGameFactoryAbi.Pack("initialize", deployer.TestAddress)
require.NoError(err)
tx, err = proxy.UpgradeToAndCall(opts, factoryAddr, data)
require.NoError(err)
_, err = utils.WaitReceiptOK(ctx, client, tx.Hash())
require.NoError(err)
factory, err := bindings.NewDisputeGameFactory(proxyAddr, client)
require.NoError(err)
// Now setup the fault dispute game type
// Start by deploying the AlphabetVM
_, tx, _, err = bindings.DeployAlphabetVM(opts, client, alphabetVMAbsolutePrestateClaim)
require.NoError(err)
alphaVMAddr, err := bind.WaitDeployed(ctx, client, tx)
require.NoError(err)
l2OutputOracle, err := bindings.NewL2OutputOracle(config.L1Deployments.L2OutputOracleProxy, client)
require.NoError(err)
// Deploy the block hash oracle
_, tx, _, err = bindings.DeployBlockOracle(opts, client)
require.NoError(err)
blockHashOracleAddr, err := bind.WaitDeployed(ctx, client, tx)
require.NoError(err)
blockHashOracle, err := bindings.NewBlockOracle(blockHashOracleAddr, client)
require.NoError(err)
// Deploy the fault dispute game implementation
_, tx, _, err = bindings.DeployFaultDisputeGame(
opts,
client,
uint8(0),
alphabetVMAbsolutePrestateClaim,
big.NewInt(alphabetGameDepth),
gameDuration,
alphaVMAddr,
config.L1Deployments.L2OutputOracleProxy,
blockHashOracleAddr,
)
require.NoError(err)
faultDisputeGameAddr, err := bind.WaitDeployed(ctx, client, tx)
require.NoError(err)
// Create a proposer transactor
secrets, err := e2eutils.DefaultMnemonicConfig.Secrets()
require.NoError(err)
chainId, err := client.ChainID(ctx)
require.NoError(err)
proposerOpts, err := bind.NewKeyedTransactorWithChainID(secrets.Proposer, chainId)
require.NoError(err)
// Propose 2 outputs
for i := uint8(0); i < 2; i++ {
nextBlockNumber, err := l2OutputOracle.NextBlockNumber(&bind.CallOpts{Pending: true, Context: ctx})
require.NoError(err)
block, err := client.BlockByNumber(ctx, big.NewInt(int64(i)))
require.NoError(err)
tx, err = l2OutputOracle.ProposeL2Output(proposerOpts, [32]byte{i + 1}, nextBlockNumber, block.Hash(), block.Number())
require.NoError(err)
_, err = utils.WaitReceiptOK(ctx, client, tx.Hash())
require.NoError(err)
}
// Set the fault game type implementation
tx, err = factory.SetImplementation(opts, faultGameType, faultDisputeGameAddr)
require.NoError(err)
_, err = utils.WaitReceiptOK(ctx, client, tx.Hash())
require.NoError(err, "wait for final transaction to be included and OK")
// Warp 15 seconds ahead for a diff in the timestamp.
clock.AdvanceTime(15 * time.Second)
// Store the current block in the oracle
tx, err = blockHashOracle.Checkpoint(opts)
require.NoError(err)
r, err := utils.WaitReceiptOK(ctx, client, tx.Hash())
require.NoError(err, "failed to store block in blockoracle")
return factory, new(big.Int).Sub(r.BlockNumber, big.NewInt(1))
}
...@@ -10,15 +10,14 @@ import ( ...@@ -10,15 +10,14 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet" "github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-service/client/utils" "github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum-optimism/optimism/op-service/clock"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -35,48 +34,90 @@ const ( ...@@ -35,48 +34,90 @@ const (
StatusDefenderWins StatusDefenderWins
) )
var alphabetVMAbsolutePrestate = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060") func (s Status) String() string {
var alphabetVMAbsolutePrestateClaim = crypto.Keccak256Hash(alphabetVMAbsolutePrestate) switch s {
case StatusInProgress:
return "In Progress"
case StatusChallengerWins:
return "Challenger Wins"
case StatusDefenderWins:
return "Defender Wins"
default:
return fmt.Sprintf("Unknown status: %v", int(s))
}
}
var CorrectAlphabet = "abcdefghijklmnop" var CorrectAlphabet = "abcdefghijklmnop"
type FactoryHelper struct { type FactoryHelper struct {
t *testing.T t *testing.T
require *require.Assertions require *require.Assertions
client *ethclient.Client client *ethclient.Client
opts *bind.TransactOpts opts *bind.TransactOpts
factory *bindings.DisputeGameFactory factory *bindings.DisputeGameFactory
l1Head *big.Int blockOracle *bindings.BlockOracle
l2oo *bindings.L2OutputOracleCaller
} }
func NewFactoryHelper(t *testing.T, ctx context.Context, clock *clock.AdvancingClock, client *ethclient.Client, gameDuration uint64) *FactoryHelper { func NewFactoryHelper(t *testing.T, ctx context.Context, deployments *genesis.L1Deployments, client *ethclient.Client) *FactoryHelper {
require := require.New(t) require := require.New(t)
chainID, err := client.ChainID(ctx) chainID, err := client.ChainID(ctx)
require.NoError(err) require.NoError(err)
opts, err := bind.NewKeyedTransactorWithChainID(deployer.TestKey, chainID) opts, err := bind.NewKeyedTransactorWithChainID(deployer.TestKey, chainID)
require.NoError(err) require.NoError(err)
factory, l1Head := deployDisputeGameContracts(require, ctx, clock, client, opts, gameDuration) require.NotNil(deployments, "No deployments")
factory, err := bindings.NewDisputeGameFactory(deployments.DisputeGameFactoryProxy, client)
require.NoError(err)
blockOracle, err := bindings.NewBlockOracle(deployments.BlockOracle, client)
require.NoError(err)
l2oo, err := bindings.NewL2OutputOracleCaller(deployments.L2OutputOracleProxy, client)
require.NoError(err, "Error creating l2oo caller")
//factory, l1Head := deployDisputeGameContracts(require, ctx, clock, client, opts, gameDuration)
return &FactoryHelper{ return &FactoryHelper{
t: t, t: t,
require: require, require: require,
client: client, client: client,
opts: opts, opts: opts,
factory: factory, factory: factory,
l1Head: l1Head, blockOracle: blockOracle,
l2oo: l2oo,
} }
} }
func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet string) *FaultGameHelper { func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet string) *FaultGameHelper {
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) // Wait for two output proposals to be published
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel() defer cancel()
trace := alphabet.NewTraceProvider(claimedAlphabet, 4) err := utils.WaitFor(ctx, time.Second, func() (bool, error) {
index, err := h.l2oo.LatestOutputIndex(&bind.CallOpts{Context: ctx})
if err != nil {
h.t.Logf("Could not get latest output index: %v", err.Error())
return false, nil
}
h.t.Logf("Latest output index: %v", index)
return index.Cmp(big.NewInt(1)) >= 0, nil
})
h.require.NoError(err, "Did not get two output roots")
ctx, cancel = context.WithTimeout(ctx, 1*time.Minute)
defer cancel()
// Store the current block in the oracle
tx, err := h.blockOracle.Checkpoint(h.opts)
h.require.NoError(err)
r, err := utils.WaitReceiptOK(ctx, h.client, tx.Hash())
h.require.NoError(err, "failed to store block in blockoracle")
l1Head := new(big.Int).Sub(r.BlockNumber, big.NewInt(1))
trace := alphabet.NewTraceProvider(claimedAlphabet, alphabetGameDepth)
rootClaim, err := trace.Get(ctx, lastAlphabetTraceIndex) rootClaim, err := trace.Get(ctx, lastAlphabetTraceIndex)
h.require.NoError(err, "get root claim") h.require.NoError(err, "get root claim")
extraData := make([]byte, 64) extraData := make([]byte, 64)
binary.BigEndian.PutUint64(extraData[24:], uint64(8)) binary.BigEndian.PutUint64(extraData[24:], uint64(8))
binary.BigEndian.PutUint64(extraData[56:], h.l1Head.Uint64()) binary.BigEndian.PutUint64(extraData[56:], l1Head.Uint64())
tx, err := h.factory.Create(h.opts, faultGameType, rootClaim, extraData) tx, err = h.factory.Create(h.opts, faultGameType, rootClaim, extraData)
h.require.NoError(err, "create fault dispute game") h.require.NoError(err, "create fault dispute game")
rcpt, err := utils.WaitReceiptOK(ctx, h.client, tx.Hash()) rcpt, err := utils.WaitReceiptOK(ctx, h.client, tx.Hash())
h.require.NoError(err, "wait for create fault dispute game receipt to be OK") h.require.NoError(err, "wait for create fault dispute game receipt to be OK")
...@@ -128,6 +169,12 @@ func (g *FaultGameHelper) StartChallenger(ctx context.Context, l1Endpoint string ...@@ -128,6 +169,12 @@ func (g *FaultGameHelper) StartChallenger(ctx context.Context, l1Endpoint string
return c return c
} }
func (g *FaultGameHelper) GameDuration(ctx context.Context) time.Duration {
duration, err := g.game.GAMEDURATION(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to get game duration")
return time.Duration(duration) * time.Second
}
func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) { func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) {
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() defer cancel()
...@@ -190,6 +237,7 @@ func (g *FaultGameHelper) Resolve(ctx context.Context) { ...@@ -190,6 +237,7 @@ func (g *FaultGameHelper) Resolve(ctx context.Context) {
} }
func (g *FaultGameHelper) WaitForGameStatus(ctx context.Context, expected Status) { func (g *FaultGameHelper) WaitForGameStatus(ctx context.Context, expected Status) {
g.t.Logf("Waiting for game %v to have status %v", g.addr, expected)
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() defer cancel()
err := utils.WaitFor(ctx, 1*time.Second, func() (bool, error) { err := utils.WaitFor(ctx, 1*time.Second, func() (bool, error) {
...@@ -199,7 +247,7 @@ func (g *FaultGameHelper) WaitForGameStatus(ctx context.Context, expected Status ...@@ -199,7 +247,7 @@ func (g *FaultGameHelper) WaitForGameStatus(ctx context.Context, expected Status
if err != nil { if err != nil {
return false, fmt.Errorf("game status unavailable: %w", err) return false, fmt.Errorf("game status unavailable: %w", err)
} }
g.t.Logf("Game %v has state %v, waiting for state %v", g.addr, Status(status), expected)
return expected == Status(status), nil return expected == Status(status), nil
}) })
g.require.NoError(err, "wait for game status") g.require.NoError(err, "wait for game status")
......
...@@ -3,7 +3,6 @@ package op_e2e ...@@ -3,7 +3,6 @@ package op_e2e
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
...@@ -17,13 +16,13 @@ func TestResolveDisputeGame(t *testing.T) { ...@@ -17,13 +16,13 @@ func TestResolveDisputeGame(t *testing.T) {
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startL1OnlySystem(t) sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
gameDuration := 24 * time.Hour disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.TimeTravelClock, l1Client, uint64(gameDuration.Seconds()))
game := disputeGameFactory.StartAlphabetGame(ctx, "zyxwvut") game := disputeGameFactory.StartAlphabetGame(ctx, "zyxwvut")
require.NotNil(t, game) require.NotNil(t, game)
gameDuration := game.GameDuration(ctx)
game.WaitForGameStatus(ctx, disputegame.StatusInProgress) game.WaitForGameStatus(ctx, disputegame.StatusInProgress)
...@@ -115,13 +114,13 @@ func TestChallengerCompleteDisputeGame(t *testing.T) { ...@@ -115,13 +114,13 @@ func TestChallengerCompleteDisputeGame(t *testing.T) {
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
sys, l1Client := startL1OnlySystem(t) sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
gameDuration := 24 * time.Hour disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.TimeTravelClock, l1Client, uint64(gameDuration.Seconds()))
game := disputeGameFactory.StartAlphabetGame(ctx, test.rootClaimAlphabet) game := disputeGameFactory.StartAlphabetGame(ctx, test.rootClaimAlphabet)
require.NotNil(t, game) require.NotNil(t, game)
gameDuration := game.GameDuration(ctx)
game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "Defender", func(c *config.Config) { game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "Defender", func(c *config.Config) {
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory) c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory)
...@@ -144,13 +143,13 @@ func TestChallengerCompleteDisputeGame(t *testing.T) { ...@@ -144,13 +143,13 @@ func TestChallengerCompleteDisputeGame(t *testing.T) {
} }
} }
func startL1OnlySystem(t *testing.T) (*System, *ethclient.Client) { func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) {
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L1BlockTime = 1
delete(cfg.Nodes, "verifier") delete(cfg.Nodes, "verifier")
delete(cfg.Nodes, "sequencer")
cfg.SupportL1TimeTravel = true cfg.SupportL1TimeTravel = true
cfg.DeployConfig.L2OutputOracleSubmissionInterval = 2
cfg.NonFinalizedProposals = true // Submit output proposals asap
sys, err := cfg.Start() sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system") require.NoError(t, err, "Error starting up system")
return sys, sys.Clients["l1"] return sys, sys.Clients["l1"]
} }
...@@ -87,38 +87,39 @@ FaucetTest:test_nonAdmin_drip_fails() (gas: 262520) ...@@ -87,38 +87,39 @@ FaucetTest:test_nonAdmin_drip_fails() (gas: 262520)
FaucetTest:test_receive_succeeds() (gas: 17401) FaucetTest:test_receive_succeeds() (gas: 17401)
FaucetTest:test_withdraw_nonAdmin_reverts() (gas: 13145) FaucetTest:test_withdraw_nonAdmin_reverts() (gas: 13145)
FaucetTest:test_withdraw_succeeds() (gas: 78359) FaucetTest:test_withdraw_succeeds() (gas: 78359)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 498844) FaultDisputeGame_ResolvesCorrectly_CorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 498906)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 505718) FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 505746)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 502387) FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 502447)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 505594) FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 505644)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 504883) FaultDisputeGame_ResolvesCorrectly_CorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 504933)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 497609) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 497671)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 504483) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 504511)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 501152) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 501212)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 502359) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 502409)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 501648) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 501698)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 32354) FaultDisputeGame_Test:test_addLocalData_static_succeeds() (gas: 640503)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 32806) FaultDisputeGame_Test:test_createdAt_succeeds() (gas: 10342)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10388) FaultDisputeGame_Test:test_extraData_succeeds() (gas: 32377)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8272) FaultDisputeGame_Test:test_gameData_succeeds() (gas: 32829)
FaultDisputeGame_Test:test_initialize_correctData_succeeds() (gas: 57716) FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8250)
FaultDisputeGame_Test:test_initialize_firstOutput_reverts() (gas: 210532) FaultDisputeGame_Test:test_initialize_correctData_succeeds() (gas: 57694)
FaultDisputeGame_Test:test_initialize_l1HeadTooOld_reverts() (gas: 228402) FaultDisputeGame_Test:test_initialize_firstOutput_reverts() (gas: 210576)
FaultDisputeGame_Test:test_move_clockCorrectness_succeeds() (gas: 415921) FaultDisputeGame_Test:test_initialize_l1HeadTooOld_reverts() (gas: 228425)
FaultDisputeGame_Test:test_move_clockTimeExceeded_reverts() (gas: 26428) FaultDisputeGame_Test:test_move_clockCorrectness_succeeds() (gas: 415993)
FaultDisputeGame_Test:test_move_defendRoot_reverts() (gas: 13388) FaultDisputeGame_Test:test_move_clockTimeExceeded_reverts() (gas: 23219)
FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 103377) FaultDisputeGame_Test:test_move_defendRoot_reverts() (gas: 13366)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 408294) FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 102920)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 407913)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 11024) FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 11024)
FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24689) FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24732)
FaultDisputeGame_Test:test_move_simpleAttack_succeeds() (gas: 107300) FaultDisputeGame_Test:test_move_simpleAttack_succeeds() (gas: 107341)
FaultDisputeGame_Test:test_resolve_challengeContested_succeeds() (gas: 224869) FaultDisputeGame_Test:test_resolve_challengeContested_succeeds() (gas: 224906)
FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9664) FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9664)
FaultDisputeGame_Test:test_resolve_rootContested_succeeds() (gas: 109838) FaultDisputeGame_Test:test_resolve_rootContested_succeeds() (gas: 109856)
FaultDisputeGame_Test:test_resolve_rootUncontestedClockNotExpired_succeeds() (gas: 21443) FaultDisputeGame_Test:test_resolve_rootUncontestedClockNotExpired_succeeds() (gas: 21421)
FaultDisputeGame_Test:test_resolve_rootUncontested_succeeds() (gas: 27278) FaultDisputeGame_Test:test_resolve_rootUncontested_succeeds() (gas: 27256)
FaultDisputeGame_Test:test_resolve_teamDeathmatch_succeeds() (gas: 395607) FaultDisputeGame_Test:test_resolve_teamDeathmatch_succeeds() (gas: 395635)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8253) FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8276)
FeeVault_Test:test_constructor_succeeds() (gas: 18185) FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352139) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352139)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950346) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950346)
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
"eip1559Elasticity": 6, "eip1559Elasticity": 6,
"l1GenesisBlockTimestamp": "0x64c811bf", "l1GenesisBlockTimestamp": "0x64c811bf",
"l2GenesisRegolithTimeOffset": "0x0", "l2GenesisRegolithTimeOffset": "0x0",
"faultGameAbsolutePrestate": 96, "faultGameAbsolutePrestate": "0x41c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98",
"faultGameMaxDepth": 4, "faultGameMaxDepth": 4,
"faultGameMaxDuration": 120 "faultGameMaxDuration": 300
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
"src/L2/L2StandardBridge.sol": "0x73a4fea3dca8ac7d7ba32e38aadeb69bd344042666a40a75e8c28849f01999e5", "src/L2/L2StandardBridge.sol": "0x73a4fea3dca8ac7d7ba32e38aadeb69bd344042666a40a75e8c28849f01999e5",
"src/L2/L2ToL1MessagePasser.sol": "0xed800b600cb3f67e18a1ab10750e3934a8b3e42178f422bcacfde770a6e8e8bd", "src/L2/L2ToL1MessagePasser.sol": "0xed800b600cb3f67e18a1ab10750e3934a8b3e42178f422bcacfde770a6e8e8bd",
"src/L2/SequencerFeeVault.sol": "0xd57c143b1f042400430b991b806bf971628e6980406c751e82d19ae80eeb4e8d", "src/L2/SequencerFeeVault.sol": "0xd57c143b1f042400430b991b806bf971628e6980406c751e82d19ae80eeb4e8d",
"src/dispute/FaultDisputeGame.sol": "0x01b3770e4d056550e71b4b9e9ada5401de98668d0a1e0ff336ed8f73a0127e99", "src/dispute/FaultDisputeGame.sol": "0x2a7f367443752f841d734f339a596c4ad93e16f7df0230f1cdd5e6f0b4b58368",
"src/legacy/DeployerWhitelist.sol": "0x5e80f7b13ef73f06c63bd9b118a49da1ff06a5c0fcf8067b5a3365d731c23765", "src/legacy/DeployerWhitelist.sol": "0x5e80f7b13ef73f06c63bd9b118a49da1ff06a5c0fcf8067b5a3365d731c23765",
"src/legacy/L1BlockNumber.sol": "0x84cc587148de5920dfcd19da44d28e769f0e4d08ca2bcc93f18aa78c6cc2ebe6", "src/legacy/L1BlockNumber.sol": "0x84cc587148de5920dfcd19da44d28e769f0e4d08ca2bcc93f18aa78c6cc2ebe6",
"src/legacy/LegacyMessagePasser.sol": "0x2692b50b227e5f75a53439c0cf303498edfd4fc087555b3fc9bc4bceb518229b", "src/legacy/LegacyMessagePasser.sol": "0x2692b50b227e5f75a53439c0cf303498edfd4fc087555b3fc9bc4bceb518229b",
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeErrors.sol";
import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol"; import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol";
import { import {
OwnableUpgradeable OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Semver } from "../universal/Semver.sol"; import { Semver } from "src/universal/Semver.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol"; import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";
/// @title DisputeGameFactory /// @title DisputeGameFactory
/// @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games /// @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games
/// are stored in both a mapping and an append only array. The timestamp of the creation /// are stored in both a mapping and an append only array. The timestamp of the creation
...@@ -37,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -37,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
GameId[] internal _disputeGameList; GameId[] internal _disputeGameList;
/// @notice constructs a new DisputeGameFactory contract. /// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() Semver(0, 0, 2) { constructor() OwnableUpgradeable() Semver(0, 0, 3) {
initialize(address(0)); initialize(address(0));
} }
...@@ -88,9 +88,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -88,9 +88,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
IDisputeGame impl = gameImpls[gameType]; IDisputeGame impl = gameImpls[gameType];
// If there is no implementation to clone for the given `GameType`, revert. // If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) { if (address(impl) == address(0)) revert NoImplementation(gameType);
revert NoImplementation(gameType);
}
// Clone the implementation contract and initialize it with the given parameters. // Clone the implementation contract and initialize it with the given parameters.
proxy = IDisputeGame(address(impl).clone(abi.encodePacked(rootClaim, extraData))); proxy = IDisputeGame(address(impl).clone(abi.encodePacked(rootClaim, extraData)));
...@@ -100,9 +98,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -100,9 +98,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
Hash uuid = getGameUUID(gameType, rootClaim, extraData); Hash uuid = getGameUUID(gameType, rootClaim, extraData);
// If a dispute game with the same UUID already exists, revert. // If a dispute game with the same UUID already exists, revert.
if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) { if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid);
revert GameAlreadyExists(uuid);
}
GameId slot = _packSlot(address(proxy), block.timestamp); GameId slot = _packSlot(address(proxy), block.timestamp);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import { IPreimageOracle } from "../../cannon/interfaces/IPreimageOracle.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol";
/// @title IBigStepper /// @title IBigStepper
/// @notice An interface for a contract with a state transition function that /// @notice An interface for a contract with a state transition function that
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
import { IBondManager } from "./IBondManager.sol"; import { IBondManager } from "./IBondManager.sol";
import { IInitializable } from "./IInitializable.sol"; import { IInitializable } from "./IInitializable.sol";
import "src/libraries/DisputeTypes.sol";
/// @title IDisputeGame /// @title IDisputeGame
/// @notice The generic interface for a DisputeGame contract. /// @notice The generic interface for a DisputeGame contract.
interface IDisputeGame is IInitializable { interface IDisputeGame is IInitializable {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
import { IDisputeGame } from "./IDisputeGame.sol"; import { IDisputeGame } from "./IDisputeGame.sol";
import "src/libraries/DisputeTypes.sol";
/// @title IDisputeGameFactory /// @title IDisputeGameFactory
/// @notice The interface for a DisputeGameFactory contract. /// @notice The interface for a DisputeGameFactory contract.
interface IDisputeGameFactory { interface IDisputeGameFactory {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
import { IDisputeGame } from "./IDisputeGame.sol"; import { IDisputeGame } from "./IDisputeGame.sol";
import "src/libraries/DisputeTypes.sol";
/// @title IFaultDisputeGame /// @title IFaultDisputeGame
/// @notice The interface for a fault proof backed dispute game. /// @notice The interface for a fault proof backed dispute game.
interface IFaultDisputeGame is IDisputeGame { interface IFaultDisputeGame is IDisputeGame {
/// @notice The `ClaimData` struct represents the data associated with a Claim. /// @notice The `ClaimData` struct represents the data associated with a Claim.
/// @dev TODO: Add bond ID information. /// @dev TODO(clabby): Add bond ID information.
struct ClaimData { struct ClaimData {
uint32 parentIndex; uint32 parentIndex;
bool countered; bool countered;
...@@ -26,7 +26,14 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -26,7 +26,14 @@ interface IFaultDisputeGame is IDisputeGame {
Hash outputRoot; Hash outputRoot;
} }
/// @notice A container for two consecutive `OutputProposal`s. /// @notice A container for two consecutive `OutputProposal`s, used to store the starting
/// and disputed output proposals for a given dispute game. The starting output
/// proposal will be used to determine where the off chain agents should begin
/// running their fault proof program, and the disputed output proposal will be
/// fed into the program and treated as disputed state. The program's exit code
/// expresses its opinion on the validity of the state transition from the starting,
/// trusted output proposal to the disputed output proposal, and ultimately resolves
/// the dispute.
struct OutputProposals { struct OutputProposals {
OutputProposal starting; OutputProposal starting;
OutputProposal disputed; OutputProposal disputed;
...@@ -39,27 +46,26 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -39,27 +46,26 @@ interface IFaultDisputeGame is IDisputeGame {
event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant); event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant);
/// @notice Attack a disagreed upon `Claim`. /// @notice Attack a disagreed upon `Claim`.
/// @param _parentIndex Index of the `Claim` to attack in `claimData`. /// @param _parentIndex Index of the `Claim` to attack in the `claimData` array.
/// @param _claim The `Claim` at the relative attack position. /// @param _claim The `Claim` at the relative attack position.
function attack(uint256 _parentIndex, Claim _claim) external payable; function attack(uint256 _parentIndex, Claim _claim) external payable;
/// @notice Defend an agreed upon `Claim`. /// @notice Defend an agreed upon `Claim`.
/// @param _parentIndex Index of the claim to defend in `claimData`. /// @param _parentIndex Index of the claim to defend in the `claimData` array.
/// @param _claim The `Claim` at the relative defense position. /// @param _claim The `Claim` at the relative defense position.
function defend(uint256 _parentIndex, Claim _claim) external payable; function defend(uint256 _parentIndex, Claim _claim) external payable;
/// @notice Perform the final step via an on-chain fault proof processor /// @notice Perform an instruction step via an on-chain fault proof processor.
/// @dev This function should point to a fault proof processor in order to execute /// @dev This function should point to a fault proof processor in order to execute
/// a step in the fault proof program on-chain. The interface of the fault proof /// a step in the fault proof program on-chain. The interface of the fault proof
/// processor contract should be generic enough such that we can use different /// processor contract should adhere to the `IBigStepper` interface.
/// fault proof VMs (MIPS, RiscV5, etc.)
/// @param _claimIndex The index of the challenged claim within `claimData`. /// @param _claimIndex The index of the challenged claim within `claimData`.
/// @param _isAttack Whether or not the step is an attack or a defense. /// @param _isAttack Whether or not the step is an attack or a defense.
/// @param _stateData The stateData of the step is the preimage of the claim at the given /// @param _stateData The stateData of the step is the preimage of the claim at the given
/// prestate, which is at `_stateIndex` if the move is an attack and `_claimIndex` if /// prestate, which is at `_stateIndex` if the move is an attack and `_claimIndex` if
/// the move is a defense. If the step is an attack on the first instruction, it is /// the move is a defense. If the step is an attack on the first instruction, it is
/// the absolute prestate of the fault proof VM. /// the absolute prestate of the fault proof VM.
/// @param _proof Proof to access memory leaf nodes in the VM. /// @param _proof Proof to access memory nodes in the VM's merkle state tree.
function step( function step(
uint256 _claimIndex, uint256 _claimIndex,
bool _isAttack, bool _isAttack,
...@@ -72,11 +78,13 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -72,11 +78,13 @@ interface IFaultDisputeGame is IDisputeGame {
/// @param _partOffset The offset of the data to post. /// @param _partOffset The offset of the data to post.
function addLocalData(uint256 _ident, uint256 _partOffset) external; function addLocalData(uint256 _ident, uint256 _partOffset) external;
/// @notice Returns the L1 block hash at the time of the game's creation. /// @notice An L1 block hash that contains the disputed output root, fetched from the
/// `BlockOracle` and verified by referencing the timestamp associated with the
/// first L2 Output Proposal in the `L2OutputOracle` that contains the disputed
/// L2 block number.
function l1Head() external view returns (Hash l1Head_); function l1Head() external view returns (Hash l1Head_);
/// @notice The l2BlockNumber that the `rootClaim` commits to. The trace being bisected within /// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`.
/// the game is from `l2BlockNumber - 1` -> `l2BlockNumber`.
function l2BlockNumber() external view returns (uint256 l2BlockNumber_); function l2BlockNumber() external view returns (uint256 l2BlockNumber_);
/// @notice The l1BlockNumber that Cannon was ran from to generate the root claim. /// @notice The l1BlockNumber that Cannon was ran from to generate the root claim.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
/// @title LibClock /// @title LibClock
/// @notice This library contains helper functions for working with the `Clock` type. /// @notice This library contains helper functions for working with the `Clock` type.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
/// @title Hashing /// @title Hashing
/// @notice This library contains all of the hashing utilities used in the Cannon contracts. /// @notice This library contains all of the hashing utilities used in the Cannon contracts.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
/// @title LibPosition /// @title LibPosition
/// @notice This library contains helper functions for working with the `Position` type. /// @notice This library contains helper functions for working with the `Position` type.
......
...@@ -58,6 +58,9 @@ error ValidStep(); ...@@ -58,6 +58,9 @@ error ValidStep();
/// not contain the disputed output root. /// not contain the disputed output root.
error L1HeadTooOld(); error L1HeadTooOld();
/// @notice Thrown when an invalid local identifier is passed to the `addLocalData` function.
error InvalidLocalIdent();
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// `AttestationDisputeGame` Errors // // `AttestationDisputeGame` Errors //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
......
...@@ -8,6 +8,8 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; ...@@ -8,6 +8,8 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol"; import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { BlockOracle } from "src/dispute/BlockOracle.sol"; import { BlockOracle } from "src/dispute/BlockOracle.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { PreimageKeyLib } from "src/cannon/PreimageKeyLib.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol"; import "src/libraries/DisputeErrors.sol";
...@@ -38,12 +40,7 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init { ...@@ -38,12 +40,7 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
// Propose 2 mock outputs // Propose 2 mock outputs
vm.startPrank(oracle.PROPOSER()); vm.startPrank(oracle.PROPOSER());
for (uint256 i; i < 2; i++) { for (uint256 i; i < 2; i++) {
oracle.proposeL2Output( oracle.proposeL2Output(bytes32(i + 1), oracle.nextBlockNumber(), blockhash(i), i);
bytes32(i + 1),
oracle.nextBlockNumber(),
blockhash(i),
i
);
// Advance 1 block // Advance 1 block
vm.roll(block.number + 1); vm.roll(block.number + 1);
...@@ -102,9 +99,9 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -102,9 +99,9 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
assertEq(gameProxy.extraData(), extraData); assertEq(gameProxy.extraData(), extraData);
} }
/// @dev Tests that the game's status is set correctly. /// @dev Tests that the game's starting timestamp is set correctly.
function test_gameStart_succeeds() public { function test_createdAt_succeeds() public {
assertEq(Timestamp.unwrap(gameProxy.gameStart()), block.timestamp); assertEq(Timestamp.unwrap(gameProxy.createdAt()), block.timestamp);
} }
/// @dev Tests that the game's type is set correctly. /// @dev Tests that the game's type is set correctly.
...@@ -129,7 +126,11 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -129,7 +126,11 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
/// contain the disputed L2 output root. /// contain the disputed L2 output root.
function test_initialize_l1HeadTooOld_reverts() public { function test_initialize_l1HeadTooOld_reverts() public {
// Store a mock block hash for the genesis block. The timestamp will default to 0. // Store a mock block hash for the genesis block. The timestamp will default to 0.
vm.store(address(gameImpl.BLOCK_ORACLE()), keccak256(abi.encode(0, 0)), bytes32(uint256(1))); vm.store(
address(gameImpl.BLOCK_ORACLE()),
keccak256(abi.encode(0, 0)),
bytes32(uint256(1))
);
bytes memory _extraData = abi.encode(oracle.SUBMISSION_INTERVAL() * 2, 0); bytes memory _extraData = abi.encode(oracle.SUBMISSION_INTERVAL() * 2, 0);
vm.expectRevert(L1HeadTooOld.selector); vm.expectRevert(L1HeadTooOld.selector);
...@@ -149,7 +150,10 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -149,7 +150,10 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
/// @dev Tests that the game is initialized with the correct data. /// @dev Tests that the game is initialized with the correct data.
function test_initialize_correctData_succeeds() public { function test_initialize_correctData_succeeds() public {
// Starting // Starting
(FaultDisputeGame.OutputProposal memory startingProp, FaultDisputeGame.OutputProposal memory disputedProp) = gameProxy.proposals(); (
FaultDisputeGame.OutputProposal memory startingProp,
FaultDisputeGame.OutputProposal memory disputedProp
) = gameProxy.proposals();
Types.OutputProposal memory starting = oracle.getL2Output(startingProp.index); Types.OutputProposal memory starting = oracle.getL2Output(startingProp.index);
assertEq(startingProp.index, 0); assertEq(startingProp.index, 0);
assertEq(startingProp.l2BlockNumber, starting.l2BlockNumber); assertEq(startingProp.l2BlockNumber, starting.l2BlockNumber);
...@@ -421,6 +425,59 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init { ...@@ -421,6 +425,59 @@ contract FaultDisputeGame_Test is FaultDisputeGame_Init {
assertEq(uint8(status), uint8(GameStatus.CHALLENGER_WINS)); assertEq(uint8(status), uint8(GameStatus.CHALLENGER_WINS));
assertEq(uint8(gameProxy.status()), uint8(GameStatus.CHALLENGER_WINS)); assertEq(uint8(gameProxy.status()), uint8(GameStatus.CHALLENGER_WINS));
} }
/// @dev Tests that adding local data with an out of bounds identifier reverts.
function testFuzz_addLocalData_oob_reverts(uint256 _ident) public {
// [1, 5] are valid local data identifiers.
if (_ident <= 5) _ident = 0;
vm.expectRevert(InvalidLocalIdent.selector);
gameProxy.addLocalData(_ident, 0);
}
/// @dev Tests that local data is loaded into the preimage oracle correctly.
function test_addLocalData_static_succeeds() public {
IPreimageOracle oracle = IPreimageOracle(address(gameProxy.VM().oracle()));
(
FaultDisputeGame.OutputProposal memory starting,
FaultDisputeGame.OutputProposal memory disputed
) = gameProxy.proposals();
bytes32[5] memory data = [
Hash.unwrap(gameProxy.l1Head()),
Hash.unwrap(starting.outputRoot),
Hash.unwrap(disputed.outputRoot),
bytes32(uint256(starting.l2BlockNumber) << 0xC0),
bytes32(block.chainid << 0xC0)
];
for (uint256 i = 1; i <= 5; i++) {
uint256 expectedLen = i > 3 ? 8 : 32;
gameProxy.addLocalData(i, 0);
bytes32 key = _getKey(i);
(bytes32 dat, uint256 datLen) = oracle.readPreimage(key, 0);
assertEq(dat >> 0xC0, bytes32(expectedLen));
// Account for the length prefix if i > 3 (the data stored
// at identifiers i <= 3 are 32 bytes long, so the expected
// length is already correct. If i > 3, the data is only 8
// bytes long, so the length prefix + the data is 16 bytes
// total.)
assertEq(datLen, expectedLen + (i > 3 ? 8 : 0));
gameProxy.addLocalData(i, 8);
key = _getKey(i);
(dat, datLen) = oracle.readPreimage(key, 8);
assertEq(dat, data[i - 1]);
assertEq(datLen, expectedLen);
}
}
/// @dev Helper to get the localized key for an identifier in the context of the game proxy.
function _getKey(uint256 _ident) internal view returns (bytes32) {
bytes32 h = keccak256(abi.encode(_ident | (1 << 248), address(gameProxy)));
return bytes32((uint256(h) & ~uint256(0xFF << 248)) | (1 << 248));
}
} }
/// @notice A generic game player actor with a configurable trace. /// @notice A generic game player actor with a configurable trace.
...@@ -963,7 +1020,7 @@ contract AlphabetVM is IBigStepper { ...@@ -963,7 +1020,7 @@ contract AlphabetVM is IBigStepper {
constructor(Claim _absolutePrestate) { constructor(Claim _absolutePrestate) {
ABSOLUTE_PRESTATE = _absolutePrestate; ABSOLUTE_PRESTATE = _absolutePrestate;
oracle = IPreimageOracle(deployNoop()); oracle = new PreimageOracle();
} }
/// @inheritdoc IBigStepper /// @inheritdoc IBigStepper
...@@ -987,16 +1044,3 @@ contract AlphabetVM is IBigStepper { ...@@ -987,16 +1044,3 @@ contract AlphabetVM is IBigStepper {
postState_ = keccak256(abi.encode(traceIndex, claim + 1)); postState_ = keccak256(abi.encode(traceIndex, claim + 1));
} }
} }
////////////////////////////////////////////////////////////////
// HELPERS //
////////////////////////////////////////////////////////////////
/// @notice Deploys a noop contract.
function deployNoop() returns (address noop_) {
assembly {
mstore(0x00, 0x60016000F3)
let size := 5
noop_ := create(0, sub(0x20, size), size)
}
}
# @eth-optimism/fee-estimation # @eth-optimism/fee-estimation
## 0.15.2
### Patch Changes
- [#6609](https://github.com/ethereum-optimism/optimism/pull/6609) [`0e83c4452`](https://github.com/ethereum-optimism/optimism/commit/0e83c44522e1a13e4d5c1395fd4dc9dbae8be08d) Thanks [@roninjin10](https://github.com/roninjin10)! - Fixed bug with 'estimateFees' not taking into account the l2 gas price
## 0.15.1 ## 0.15.1
### Patch Changes ### Patch Changes
......
...@@ -37,11 +37,9 @@ yarn add @eth-optimism/fee-estimation ...@@ -37,11 +37,9 @@ yarn add @eth-optimism/fee-estimation
### Basic Usage ### Basic Usage
```ts ```ts
import { import { estimateFees } from '@eth-optimism/fee-estimation'
estimateFees, import { optimistABI } from '@eth-optimism/contracts-ts'
} from '@eth-optimism/fee-estimation' import { viemClient } from './viem-client'
import {optimistABI} from '@eth-optimism/contracts-ts'
import {viemClient} from './viem-client'
const optimistOwnerAddress = const optimistOwnerAddress =
'0x77194aa25a06f932c10c0f25090f3046af2c85a6' as const '0x77194aa25a06f932c10c0f25090f3046af2c85a6' as const
......
{ {
"name": "@eth-optimism/fee-estimation", "name": "@eth-optimism/fee-estimation",
"version": "0.15.1", "version": "0.15.2",
"description": "Lightweight library for doing OP-Chain gas estimation", "description": "Lightweight library for doing OP-Chain gas estimation",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
......
...@@ -24,6 +24,19 @@ import { ...@@ -24,6 +24,19 @@ import {
} from '@eth-optimism/contracts-ts' } from '@eth-optimism/contracts-ts'
import { parseEther, parseGwei } from 'viem' import { parseEther, parseGwei } from 'viem'
vi.mock('viem', async () => {
const _viem = (await vi.importActual('viem')) as any
return {
..._viem,
// no way to get historical gas price
createPublicClient: (...args: [any]) => {
const client = _viem.createPublicClient(...args)
client.getGasPrice = async () => parseGwei('0.00000042')
return client
},
}
})
// using this optimist https://optimistic.etherscan.io/tx/0xaa291efba7ea40b0742e5ff84a1e7831a2eb6c2fc35001fa03ba80fd3b609dc9 // using this optimist https://optimistic.etherscan.io/tx/0xaa291efba7ea40b0742e5ff84a1e7831a2eb6c2fc35001fa03ba80fd3b609dc9
const blockNumber = BigInt(107028270) const blockNumber = BigInt(107028270)
const optimistOwnerAddress = const optimistOwnerAddress =
...@@ -95,44 +108,44 @@ beforeEach(() => { ...@@ -95,44 +108,44 @@ beforeEach(() => {
test('estimateFees should return correct fees', async () => { test('estimateFees should return correct fees', async () => {
// burn // burn
const res = await estimateFees({ ...paramsWithRpcUrl, ...functionDataBurn }) const res = await estimateFees({ ...paramsWithRpcUrl, ...functionDataBurn })
expect(res).toMatchInlineSnapshot('20573185261089n') expect(res).toMatchInlineSnapshot('20573203833264n')
expect(formatEther(res)).toMatchInlineSnapshot('"0.000020573185261089"') expect(formatEther(res)).toMatchInlineSnapshot('"0.000020573203833264"')
expect( expect(
await estimateFees({ ...paramsWithRpcUrl, ...functionDataBurn }) await estimateFees({ ...paramsWithRpcUrl, ...functionDataBurn })
).toMatchInlineSnapshot('20573185261089n') ).toMatchInlineSnapshot('20573203833264n')
expect( expect(
await estimateFees({ ...paramsWithViemClient, ...functionDataBurn }) await estimateFees({ ...paramsWithViemClient, ...functionDataBurn })
).toMatchInlineSnapshot('20573185261089n') ).toMatchInlineSnapshot('20573203833264n')
expect( expect(
await estimateFees({ await estimateFees({
...paramsWithRpcUrl, ...paramsWithRpcUrl,
...functionDataBurnWithPriorityFees, ...functionDataBurnWithPriorityFees,
}) })
).toMatchInlineSnapshot('21536974118090n') ).toMatchInlineSnapshot('21536992690265n')
// what is the l2 and l1 part of the fees for reference? // what is the l2 and l1 part of the fees for reference?
const l1Fee = await getL1Fee({ ...paramsWithRpcUrl, ...functionDataBurn }) const l1Fee = await getL1Fee({ ...paramsWithRpcUrl, ...functionDataBurn })
const l2Fee = res - l1Fee const l2Fee = res - l1Fee
expect(l1Fee).toMatchInlineSnapshot('20573185216764n') expect(l1Fee).toMatchInlineSnapshot('20573185216764n')
expect(formatEther(l1Fee)).toMatchInlineSnapshot('"0.000020573185216764"') expect(formatEther(l1Fee)).toMatchInlineSnapshot('"0.000020573185216764"')
expect(l2Fee).toMatchInlineSnapshot('44325n') expect(l2Fee).toMatchInlineSnapshot('18616500n')
expect(formatEther(l2Fee)).toMatchInlineSnapshot('"0.000000000000044325"') expect(formatEther(l2Fee)).toMatchInlineSnapshot('"0.0000000000186165"')
// withdraw // withdraw
const res2 = await estimateFees({ const res2 = await estimateFees({
...paramsWithRpcUrlWithdraw, ...paramsWithRpcUrlWithdraw,
...functionDataWithdraw, ...functionDataWithdraw,
}) })
expect(res2).toMatchInlineSnapshot('62857039016380n') expect(res2).toMatchInlineSnapshot('62857090247510n')
expect( expect(
await estimateFees({ ...paramsWithRpcUrlWithdraw, ...functionDataWithdraw }) await estimateFees({ ...paramsWithRpcUrlWithdraw, ...functionDataWithdraw })
).toMatchInlineSnapshot('62857039016380n') ).toMatchInlineSnapshot('62857090247510n')
expect( expect(
await estimateFees({ ...paramsWithRpcUrlWithdraw, ...functionDataWithdraw }) await estimateFees({ ...paramsWithRpcUrlWithdraw, ...functionDataWithdraw })
).toMatchInlineSnapshot('62857039016380n') ).toMatchInlineSnapshot('62857090247510n')
expect( expect(
await estimateFees({ ...paramsWithRpcUrlWithdraw, ...functionDataWithdraw }) await estimateFees({ ...paramsWithRpcUrlWithdraw, ...functionDataWithdraw })
).toMatchInlineSnapshot('62857039016380n') ).toMatchInlineSnapshot('62857090247510n')
expect(formatEther(res2)).toMatchInlineSnapshot('"0.00006285703901638"') expect(formatEther(res2)).toMatchInlineSnapshot('"0.00006285709024751"')
// what is the l2 and l1 part of the fees for reference? // what is the l2 and l1 part of the fees for reference?
const l1Fee2 = await getL1Fee({ const l1Fee2 = await getL1Fee({
...paramsWithRpcUrlWithdraw, ...paramsWithRpcUrlWithdraw,
...@@ -141,8 +154,8 @@ test('estimateFees should return correct fees', async () => { ...@@ -141,8 +154,8 @@ test('estimateFees should return correct fees', async () => {
const l2Fee2 = res2 - l1Fee const l2Fee2 = res2 - l1Fee
expect(l1Fee2).toMatchInlineSnapshot('62857038894110n') expect(l1Fee2).toMatchInlineSnapshot('62857038894110n')
expect(formatEther(l1Fee2)).toMatchInlineSnapshot('"0.00006285703889411"') expect(formatEther(l1Fee2)).toMatchInlineSnapshot('"0.00006285703889411"')
expect(l2Fee2).toMatchInlineSnapshot('42283853799616n') expect(l2Fee2).toMatchInlineSnapshot('42283905030746n')
expect(formatEther(l2Fee2)).toMatchInlineSnapshot('"0.000042283853799616"') expect(formatEther(l2Fee2)).toMatchInlineSnapshot('"0.000042283905030746"')
}) })
test('baseFee should return the correct result', async () => { test('baseFee should return the correct result', async () => {
......
...@@ -332,7 +332,7 @@ export const estimateFees: EstimateFees = async (options) => { ...@@ -332,7 +332,7 @@ export const estimateFees: EstimateFees = async (options) => {
args: options.args, args: options.args,
functionName: options.functionName, functionName: options.functionName,
} as EncodeFunctionDataParameters) } as EncodeFunctionDataParameters)
const [l1Fee, l2Fee] = await Promise.all([ const [l1Fee, l2Gas, l2GasPrice] = await Promise.all([
getL1Fee({ getL1Fee({
...options, ...options,
// account must be undefined or else viem will return undefined // account must be undefined or else viem will return undefined
...@@ -347,6 +347,7 @@ export const estimateFees: EstimateFees = async (options) => { ...@@ -347,6 +347,7 @@ export const estimateFees: EstimateFees = async (options) => {
data: encodedFunctionData, data: encodedFunctionData,
value: options.value, value: options.value,
} as EstimateGasParameters<typeof chains.optimism>), } as EstimateGasParameters<typeof chains.optimism>),
client.getGasPrice(),
]) ])
return l1Fee + l2Fee return l1Fee + l2Gas * l2GasPrice
} }
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