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

Merge branch 'develop' into aj/cannon-witness

parents 2b15b4a5 a40a8771
v1.10.25
---
'@eth-optimism/fee-estimation': patch
---
Fixed bug with 'estimateFees' not taking into account the l2 gas price
......@@ -5,8 +5,8 @@ go 1.20
require github.com/ethereum-optimism/optimism v0.0.0
require (
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/sys v0.11.0 // indirect
)
replace github.com/ethereum-optimism/optimism v0.0.0 => ../../..
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/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
......@@ -23,7 +23,7 @@ require (
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-leveldb v0.5.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/libp2p/go-libp2p v0.25.1
github.com/libp2p/go-libp2p-pubsub v0.9.3
......@@ -40,10 +40,10 @@ require (
github.com/stretchr/testify v1.8.4
github.com/urfave/cli v1.22.14
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/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
gorm.io/driver/postgres v1.5.2
gorm.io/gorm v1.25.2
......@@ -185,9 +185,9 @@ require (
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // 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
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/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
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-20190608224051-11cab39313c9/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
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
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-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
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
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-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
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-20181017192945-9dcd33a902f4/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
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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
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-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.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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=
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
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-20181108054448-85acf8d2951c/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 {
HasNextPage bool `json:"hasNextPage"`
}
func (a *Api) DepositsHandler(w http.ResponseWriter, r *http.Request) {
bv := a.bridgeView
func (a *Api) L1DepositsHandler(w http.ResponseWriter, r *http.Request) {
bv := a.BridgeTransfersView
address := common.HexToAddress(chi.URLParam(r, "address"))
// limit := getIntFromQuery(r, "limit", 10)
// cursor := r.URL.Query().Get("cursor")
// sortDirection := r.URL.Query().Get("sortDirection")
deposits, err := bv.DepositsByAddress(address)
deposits, err := bv.L1BridgeDepositsByAddress(address)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
......@@ -43,17 +41,15 @@ func (a *Api) DepositsHandler(w http.ResponseWriter, r *http.Request) {
jsonResponse(w, response, http.StatusOK)
}
func (a *Api) WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
bv := a.bridgeView
func (a *Api) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
bv := a.BridgeTransfersView
address := common.HexToAddress(chi.URLParam(r, "address"))
// limit := getIntFromQuery(r, "limit", 10)
// cursor := r.URL.Query().Get("cursor")
// sortDirection := r.URL.Query().Get("sortDirection")
withdrawals, err := bv.WithdrawalsByAddress(address)
withdrawals, err := bv.L2BridgeWithdrawalsByAddress(address)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
......@@ -83,22 +79,20 @@ func jsonResponse(w http.ResponseWriter, data interface{}, statusCode int) {
}
type Api struct {
Router *chi.Mux
bridgeView database.BridgeView
Router *chi.Mux
BridgeTransfersView database.BridgeTransfersView
}
func NewApi(bv database.BridgeView) *Api {
func NewApi(bv database.BridgeTransfersView) *Api {
r := chi.NewRouter()
api := &Api{
Router: r,
bridgeView: bv,
}
api := &Api{Router: r, BridgeTransfersView: bv}
// 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
// with go-ethereum and throw a friendly error message
r.Get("/api/v0/deposits/{address:.+}", api.DepositsHandler)
r.Get("/api/v0/withdrawals/{address:.+}", api.WithdrawalsHandler)
r.Get("/api/v0/deposits/{address:.+}", api.L1DepositsHandler)
r.Get("/api/v0/withdrawals/{address:.+}", api.L2WithdrawalsHandler)
r.Get("/healthz", api.HealthzHandler)
return api
......
......@@ -8,93 +8,64 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
// MockBridgeView mocks the BridgeView interface
type MockBridgeView struct{}
const (
guid1 = "8408b6d2-7c90-4cfc-8604-b2204116cb6a"
guid2 = "8408b6d2-7c90-4cfc-8604-b2204116cb6b"
// MockBridgeTransfersView mocks the BridgeTransfersView interface
type MockBridgeTransfersView struct{}
var (
deposit = database.L1BridgeDeposit{
TransactionSourceHash: common.HexToHash("abc"),
CrossDomainMessengerNonce: &database.U256{Int: big.NewInt(0)},
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}
withdrawal = database.L2BridgeWithdrawal{
TransactionWithdrawalHash: common.HexToHash("0x456"),
CrossDomainMessengerNonce: &database.U256{Int: big.NewInt(0)},
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}
)
// DepositsByAddress mocks returning deposits by an address
func (mbv *MockBridgeView) DepositsByAddress(address common.Address) ([]*database.DepositWithTransactionHashes, error) {
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),
InitiatedL1EventGUID: uuid.MustParse(guid2),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}, nil
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 *MockBridgeView) LatestDepositMessageNonce() (*big.Int, error) {
return big.NewInt(0), nil
func (mbv *MockBridgeTransfersView) L1BridgeDepositByCrossDomainMessengerNonce(nonce *big.Int) (*database.L1BridgeDeposit, error) {
return &deposit, nil
}
// WithdrawalsByAddress mocks returning withdrawals by an address
func (mbv *MockBridgeView) WithdrawalsByAddress(address common.Address) ([]*database.WithdrawalWithTransactionHashes, error) {
return []*database.WithdrawalWithTransactionHashes{
func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Address) ([]*database.L1BridgeDepositWithTransactionHashes, error) {
return []*database.L1BridgeDepositWithTransactionHashes{
{
Withdrawal: database.Withdrawal{
GUID: uuid.MustParse(guid2),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
},
L2TransactionHash: common.HexToHash("0x789"),
L1BridgeDeposit: deposit,
L1TransactionHash: common.HexToHash("0x123"),
},
}, nil
}
// WithdrawalsByMessageNonce mocks returning withdrawals by a withdrawal hash
func (mbv *MockBridgeView) WithdrawalByMessageNonce(nonce *big.Int) (*database.Withdrawal, error) {
return &database.Withdrawal{
GUID: uuid.MustParse(guid2),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}, nil
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawal(address common.Hash) (*database.L2BridgeWithdrawal, error) {
return &withdrawal, nil
}
// WithdrawalsByHash mocks returning withdrawals by a withdrawal hash
func (mbv *MockBridgeView) WithdrawalByHash(address common.Hash) (*database.Withdrawal, error) {
return &database.Withdrawal{
GUID: uuid.MustParse(guid2),
InitiatedL2EventGUID: uuid.MustParse(guid1),
WithdrawalHash: common.HexToHash("0x456"),
Tx: database.Transaction{},
TokenPair: database.TokenPair{},
}, nil
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalByCrossDomainMessengerNonce(nonce *big.Int) (*database.L2BridgeWithdrawal, error) {
return &withdrawal, nil
}
// LatestWithdrawalMessageNonce mocks returning the latest cross domain message nonce for a withdrawal
func (mbv *MockBridgeView) LatestWithdrawalMessageNonce() (*big.Int, error) {
return big.NewInt(0), nil
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.Address) ([]*database.L2BridgeWithdrawalWithTransactionHashes, error) {
return []*database.L2BridgeWithdrawalWithTransactionHashes{
{
L2BridgeWithdrawal: withdrawal,
L2TransactionHash: common.HexToHash("0x789"),
},
}, nil
}
func TestHealthz(t *testing.T) {
api := NewApi(&MockBridgeView{})
api := NewApi(&MockBridgeTransfersView{})
request, err := http.NewRequest("GET", "/healthz", nil)
assert.Nil(t, err)
......@@ -104,8 +75,8 @@ func TestHealthz(t *testing.T) {
assert.Equal(t, http.StatusOK, responseRecorder.Code)
}
func TestDepositsHandler(t *testing.T) {
api := NewApi(&MockBridgeView{})
func TestL1BridgeDepositsHandler(t *testing.T) {
api := NewApi(&MockBridgeTransfersView{})
request, err := http.NewRequest("GET", "/api/v0/deposits/0x123", nil)
assert.Nil(t, err)
......@@ -115,8 +86,8 @@ func TestDepositsHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, responseRecorder.Code)
}
func TestWithdrawalsHandler(t *testing.T) {
api := NewApi(&MockBridgeView{})
func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) {
api := NewApi(&MockBridgeTransfersView{})
request, err := http.NewRequest("GET", "/api/v0/withdrawals/0x123", nil)
assert.Nil(t, err)
......
......@@ -23,15 +23,16 @@ type Cli struct {
}
func runIndexer(ctx *cli.Context) error {
logger := log.NewLogger(log.ReadCLIConfig(ctx))
configPath := ctx.String(ConfigFlag.Name)
cfg, err := config.LoadConfig(configPath)
if err != nil {
logger.Error("failed to load config", "err", err)
return err
}
// setup logger
cfg.Logger = log.NewLogger(log.ReadCLIConfig(ctx))
cfg.Logger = logger
indexer, err := indexer.NewIndexer(cfg)
if err != nil {
return err
......@@ -47,17 +48,20 @@ func runIndexer(ctx *cli.Context) error {
}
func runApi(ctx *cli.Context) error {
configPath := ctx.String(ConfigFlag.Name)
conf, err := config.LoadConfig(configPath)
fmt.Println(conf)
logger := log.NewLogger(log.ReadCLIConfig(ctx))
configPath := ctx.String(ConfigFlag.Name)
cfg, err := config.LoadConfig(configPath)
if err != nil {
panic(err)
logger.Error("failed to load config", "err", err)
return err
}
cfg.Logger = logger
fmt.Println(cfg)
// finish me
return nil
return err
}
var (
......
package database
import (
"errors"
"fmt"
"math/big"
"gorm.io/gorm"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/google/uuid"
)
/**
* Types
*/
type Transaction struct {
FromAddress common.Address `gorm:"serializer:json"`
ToAddress common.Address `gorm:"serializer:json"`
Amount U256
Data hexutil.Bytes `gorm:"serializer:json"`
Timestamp uint64
}
type TokenPair struct {
L1TokenAddress common.Address `gorm:"serializer:json"`
L2TokenAddress common.Address `gorm:"serializer:json"`
}
type Deposit struct {
GUID uuid.UUID `gorm:"primaryKey"`
InitiatedL1EventGUID uuid.UUID
// Since we're only currently indexing a single StandardBridge,
// the message nonce serves as a unique identifier for this
// deposit. Once this generalizes to more than 1 deployed
// bridge, we need to include the `CrossDomainMessenger` address
// such that the (messenger_addr, nonce) is the unique identifier
// for a bridge msg
SentMessageNonce U256
FinalizedL2EventGUID *uuid.UUID
Tx Transaction `gorm:"embedded"`
TokenPair TokenPair `gorm:"embedded"`
}
type DepositWithTransactionHashes struct {
Deposit Deposit `gorm:"embedded"`
L1TransactionHash common.Hash `gorm:"serializer:json"`
FinalizedL2TransactionHash common.Hash `gorm:"serializer:json"`
}
type Withdrawal struct {
GUID uuid.UUID `gorm:"primaryKey"`
InitiatedL2EventGUID uuid.UUID
// Since we're only currently indexing a single StandardBridge,
// the message nonce serves as a unique identifier for this
// withdrawal. Once this generalizes to more than 1 deployed
// bridge, we need to include the `CrossDomainMessenger` address
// such that the (messenger_addr, nonce) is the unique identifier
// for a bridge msg
SentMessageNonce U256
WithdrawalHash common.Hash `gorm:"serializer:json"`
ProvenL1EventGUID *uuid.UUID
FinalizedL1EventGUID *uuid.UUID
Tx Transaction `gorm:"embedded"`
TokenPair TokenPair `gorm:"embedded"`
}
type WithdrawalWithTransactionHashes struct {
Withdrawal Withdrawal `gorm:"embedded"`
L2TransactionHash common.Hash `gorm:"serializer:json"`
ProvenL1TransactionHash common.Hash `gorm:"serializer:json"`
FinalizedL1TransactionHash common.Hash `gorm:"serializer:json"`
}
type BridgeView interface {
DepositsByAddress(address common.Address) ([]*DepositWithTransactionHashes, error)
DepositByMessageNonce(*big.Int) (*Deposit, error)
LatestDepositMessageNonce() (*big.Int, error)
WithdrawalsByAddress(address common.Address) ([]*WithdrawalWithTransactionHashes, error)
WithdrawalByMessageNonce(*big.Int) (*Withdrawal, error)
WithdrawalByHash(common.Hash) (*Withdrawal, error)
LatestWithdrawalMessageNonce() (*big.Int, error)
}
type BridgeDB interface {
BridgeView
StoreDeposits([]*Deposit) error
MarkFinalizedDepositEvent(uuid.UUID, uuid.UUID) error
StoreWithdrawals([]*Withdrawal) error
MarkProvenWithdrawalEvent(uuid.UUID, uuid.UUID) error
MarkFinalizedWithdrawalEvent(uuid.UUID, uuid.UUID) error
}
/**
* Implementation
*/
type bridgeDB struct {
gorm *gorm.DB
}
func newBridgeDB(db *gorm.DB) BridgeDB {
return &bridgeDB{gorm: db}
}
// Deposits
func (db *bridgeDB) StoreDeposits(deposits []*Deposit) error {
result := db.gorm.Create(&deposits)
return result.Error
}
func (db *bridgeDB) DepositsByAddress(address common.Address) ([]*DepositWithTransactionHashes, error) {
depositsQuery := db.gorm.Table("deposits").Select("deposits.*, l1_contract_events.transaction_hash AS l1_transaction_hash, l2_contract_events.transaction_hash AS finalized_l2_transaction_hash")
initiatedJoinQuery := depositsQuery.Joins("LEFT JOIN l1_contract_events ON deposits.initiated_l1_event_guid = l1_contract_events.guid")
finalizedJoinQuery := initiatedJoinQuery.Joins("LEFT JOIN l2_contract_events ON deposits.finalized_l2_event_guid = l2_contract_events.guid")
// add in cursoring options
filteredQuery := finalizedJoinQuery.Where(&Transaction{FromAddress: address}).Order("deposits.timestamp DESC").Limit(100)
deposits := make([]*DepositWithTransactionHashes, 100)
result := filteredQuery.Scan(&deposits)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return deposits, nil
}
func (db *bridgeDB) DepositByMessageNonce(nonce *big.Int) (*Deposit, error) {
var deposit Deposit
result := db.gorm.Where(&Deposit{SentMessageNonce: U256{Int: nonce}}).Take(&deposit)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &deposit, nil
}
func (db *bridgeDB) LatestDepositMessageNonce() (*big.Int, error) {
var deposit Deposit
result := db.gorm.Order("sent_message_nonce DESC").Take(&deposit)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return deposit.SentMessageNonce.Int, nil
}
func (db *bridgeDB) MarkFinalizedDepositEvent(guid, finalizationEventGUID uuid.UUID) error {
var deposit Deposit
result := db.gorm.Where(&Deposit{GUID: guid}).Take(&deposit)
if result.Error != nil {
return result.Error
}
deposit.FinalizedL2EventGUID = &finalizationEventGUID
result = db.gorm.Save(&deposit)
return result.Error
}
// Withdrawals
func (db *bridgeDB) StoreWithdrawals(withdrawals []*Withdrawal) error {
result := db.gorm.Create(&withdrawals)
return result.Error
}
func (db *bridgeDB) MarkProvenWithdrawalEvent(guid, provenL1EventGuid uuid.UUID) error {
var withdrawal Withdrawal
result := db.gorm.Where(&Withdrawal{GUID: guid}).Take(&withdrawal)
if result.Error != nil {
return result.Error
}
withdrawal.ProvenL1EventGUID = &provenL1EventGuid
result = db.gorm.Save(&withdrawal)
return result.Error
}
func (db *bridgeDB) MarkFinalizedWithdrawalEvent(guid, finalizedL1EventGuid uuid.UUID) error {
var withdrawal Withdrawal
result := db.gorm.Where(&Withdrawal{GUID: guid}).Take(&withdrawal)
if result.Error != nil {
return result.Error
}
if withdrawal.ProvenL1EventGUID == nil {
return fmt.Errorf("withdrawal %s marked finalized prior to being proven", guid)
}
withdrawal.FinalizedL1EventGUID = &finalizedL1EventGuid
result = db.gorm.Save(&withdrawal)
return result.Error
}
func (db *bridgeDB) WithdrawalsByAddress(address common.Address) ([]*WithdrawalWithTransactionHashes, error) {
withdrawalsQuery := db.gorm.Table("withdrawals").Select("withdrawals.*, l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_contract_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash")
eventsJoinQuery := withdrawalsQuery.Joins("LEFT JOIN l2_contract_events ON withdrawals.initiated_l2_event_guid = l2_contract_events.guid")
provenJoinQuery := eventsJoinQuery.Joins("LEFT JOIN l1_contract_events AS proven_l1_contract_events ON withdrawals.proven_l1_event_guid = proven_l1_contract_events.guid")
finalizedJoinQuery := provenJoinQuery.Joins("LEFT JOIN l1_contract_events AS finalized_l1_contract_events ON withdrawals.finalized_l1_event_guid = finalized_l1_contract_events.guid")
// add in cursoring options
filteredQuery := finalizedJoinQuery.Where(&Transaction{FromAddress: address}).Order("withdrawals.timestamp DESC").Limit(100)
withdrawals := make([]*WithdrawalWithTransactionHashes, 100)
result := filteredQuery.Scan(&withdrawals)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return withdrawals, nil
}
func (db *bridgeDB) WithdrawalByMessageNonce(nonce *big.Int) (*Withdrawal, error) {
var withdrawal Withdrawal
result := db.gorm.Where(&Withdrawal{SentMessageNonce: U256{Int: nonce}}).Take(&withdrawal)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &withdrawal, nil
}
func (db *bridgeDB) WithdrawalByHash(hash common.Hash) (*Withdrawal, error) {
var withdrawal Withdrawal
result := db.gorm.Where(&Withdrawal{WithdrawalHash: hash}).Take(&withdrawal)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &withdrawal, nil
}
func (db *bridgeDB) LatestWithdrawalMessageNonce() (*big.Int, error) {
var withdrawal Withdrawal
result := db.gorm.Order("sent_message_nonce DESC").Take(&withdrawal)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return withdrawal.SentMessageNonce.Int, nil
}
package database
import (
"errors"
"fmt"
"math/big"
"gorm.io/gorm"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
)
/**
* Types
*/
type BridgeMessage struct {
Nonce U256 `gorm:"primaryKey"`
MessageHash common.Hash `gorm:"serializer:json"`
SentMessageEventGUID uuid.UUID
RelayedMessageEventGUID *uuid.UUID
Tx Transaction `gorm:"embedded"`
GasLimit U256
}
type L1BridgeMessage struct {
BridgeMessage `gorm:"embedded"`
TransactionSourceHash common.Hash `gorm:"serializer:json"`
}
type L2BridgeMessage struct {
BridgeMessage `gorm:"embedded"`
TransactionWithdrawalHash common.Hash `gorm:"serializer:json"`
}
type BridgeMessagesView interface {
L1BridgeMessage(*big.Int) (*L1BridgeMessage, error)
L1BridgeMessageByHash(common.Hash) (*L1BridgeMessage, error)
LatestL1BridgeMessageNonce() (*big.Int, error)
L2BridgeMessage(*big.Int) (*L2BridgeMessage, error)
L2BridgeMessageByHash(common.Hash) (*L2BridgeMessage, error)
LatestL2BridgeMessageNonce() (*big.Int, error)
}
type BridgeMessagesDB interface {
BridgeMessagesView
StoreL1BridgeMessages([]*L1BridgeMessage) error
MarkRelayedL1BridgeMessage(common.Hash, uuid.UUID) error
StoreL2BridgeMessages([]*L2BridgeMessage) error
MarkRelayedL2BridgeMessage(common.Hash, uuid.UUID) error
}
/**
* Implementation
*/
type bridgeMessagesDB struct {
gorm *gorm.DB
}
func newBridgeMessagesDB(db *gorm.DB) BridgeMessagesDB {
return &bridgeMessagesDB{gorm: db}
}
/**
* Arbitrary Messages Sent from L1
*/
func (db bridgeMessagesDB) StoreL1BridgeMessages(messages []*L1BridgeMessage) error {
result := db.gorm.Create(&messages)
return result.Error
}
func (db bridgeMessagesDB) L1BridgeMessage(nonce *big.Int) (*L1BridgeMessage, error) {
var sentMessage L1BridgeMessage
result := db.gorm.Where(&BridgeMessage{Nonce: U256{Int: nonce}}).Take(&sentMessage)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &sentMessage, nil
}
func (db bridgeMessagesDB) L1BridgeMessageByHash(messageHash common.Hash) (*L1BridgeMessage, error) {
var sentMessage L1BridgeMessage
result := db.gorm.Where(&BridgeMessage{MessageHash: messageHash}).Take(&sentMessage)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &sentMessage, nil
}
func (db bridgeMessagesDB) LatestL1BridgeMessageNonce() (*big.Int, error) {
var sentMessage L1BridgeMessage
result := db.gorm.Order("nonce DESC").Take(&sentMessage)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return sentMessage.Nonce.Int, nil
}
/**
* Arbitrary Messages Sent from L2
*/
func (db bridgeMessagesDB) MarkRelayedL1BridgeMessage(messageHash common.Hash, relayEvent uuid.UUID) error {
message, err := db.L1BridgeMessageByHash(messageHash)
if err != nil {
return err
} else if message == nil {
return fmt.Errorf("L1BridgeMessage with message hash %s not found", messageHash)
}
message.RelayedMessageEventGUID = &relayEvent
result := db.gorm.Save(message)
return result.Error
}
func (db bridgeMessagesDB) StoreL2BridgeMessages(messages []*L2BridgeMessage) error {
result := db.gorm.Create(&messages)
return result.Error
}
func (db bridgeMessagesDB) L2BridgeMessage(nonce *big.Int) (*L2BridgeMessage, error) {
var sentMessage L2BridgeMessage
result := db.gorm.Where(&BridgeMessage{Nonce: U256{Int: nonce}}).Take(&sentMessage)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &sentMessage, nil
}
func (db bridgeMessagesDB) L2BridgeMessageByHash(messageHash common.Hash) (*L2BridgeMessage, error) {
var sentMessage L2BridgeMessage
result := db.gorm.Where(&BridgeMessage{MessageHash: messageHash}).Take(&sentMessage)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &sentMessage, nil
}
func (db bridgeMessagesDB) LatestL2BridgeMessageNonce() (*big.Int, error) {
var sentMessage L2BridgeMessage
result := db.gorm.Order("nonce DESC").Take(&sentMessage)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return sentMessage.Nonce.Int, nil
}
func (db bridgeMessagesDB) MarkRelayedL2BridgeMessage(messageHash common.Hash, relayEvent uuid.UUID) error {
message, err := db.L2BridgeMessageByHash(messageHash)
if err != nil {
return err
} else if message == nil {
return fmt.Errorf("L2BridgeMessage with message hash %s not found", messageHash)
}
message.RelayedMessageEventGUID = &relayEvent
result := db.gorm.Save(message)
return result.Error
}
package database
import (
"errors"
"fmt"
"github.com/google/uuid"
"gorm.io/gorm"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
/**
* Types
*/
type Transaction struct {
FromAddress common.Address `gorm:"serializer:json"`
ToAddress common.Address `gorm:"serializer:json"`
Amount U256
Data hexutil.Bytes `gorm:"serializer:json"`
Timestamp uint64
}
type L1TransactionDeposit struct {
SourceHash common.Hash `gorm:"serializer:json;primaryKey"`
L2TransactionHash common.Hash `gorm:"serializer:json"`
InitiatedL1EventGUID uuid.UUID
Version U256
OpaqueData hexutil.Bytes `gorm:"serializer:json"`
Tx Transaction `gorm:"embedded"`
GasLimit U256
}
type L2TransactionWithdrawal struct {
WithdrawalHash common.Hash `gorm:"serializer:json;primaryKey"`
Nonce U256
InitiatedL2EventGUID uuid.UUID
ProvenL1EventGUID *uuid.UUID
FinalizedL1EventGUID *uuid.UUID
Succeeded *bool
Tx Transaction `gorm:"embedded"`
GasLimit U256
}
type BridgeTransactionsView interface {
L1TransactionDeposit(common.Hash) (*L1TransactionDeposit, error)
L2TransactionWithdrawal(common.Hash) (*L2TransactionWithdrawal, error)
}
type BridgeTransactionsDB interface {
BridgeTransactionsView
StoreL1TransactionDeposits([]*L1TransactionDeposit) error
StoreL2TransactionWithdrawals([]*L2TransactionWithdrawal) error
MarkL2TransactionWithdrawalProvenEvent(common.Hash, uuid.UUID) error
MarkL2TransactionWithdrawalFinalizedEvent(common.Hash, uuid.UUID, bool) error
}
/**
* Implementation
*/
type bridgeTransactionsDB struct {
gorm *gorm.DB
}
func newBridgeTransactionsDB(db *gorm.DB) BridgeTransactionsDB {
return &bridgeTransactionsDB{gorm: db}
}
/**
* Transactions deposited from L1
*/
func (db *bridgeTransactionsDB) StoreL1TransactionDeposits(deposits []*L1TransactionDeposit) error {
result := db.gorm.Create(&deposits)
return result.Error
}
func (db *bridgeTransactionsDB) L1TransactionDeposit(sourceHash common.Hash) (*L1TransactionDeposit, error) {
var deposit L1TransactionDeposit
result := db.gorm.Where(&L1TransactionDeposit{SourceHash: sourceHash}).Take(&deposit)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &deposit, nil
}
/**
* Transactions withdrawn from L2
*/
func (db *bridgeTransactionsDB) StoreL2TransactionWithdrawals(withdrawals []*L2TransactionWithdrawal) error {
result := db.gorm.Create(&withdrawals)
return result.Error
}
func (db *bridgeTransactionsDB) L2TransactionWithdrawal(withdrawalHash common.Hash) (*L2TransactionWithdrawal, error) {
var withdrawal L2TransactionWithdrawal
result := db.gorm.Where(&L2TransactionWithdrawal{WithdrawalHash: withdrawalHash}).Take(&withdrawal)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &withdrawal, nil
}
// MarkL2TransactionWithdrawalProvenEvent links a withdrawn transaction with associated Prove action on L1.
func (db *bridgeTransactionsDB) MarkL2TransactionWithdrawalProvenEvent(withdrawalHash common.Hash, provenL1EventGuid uuid.UUID) error {
withdrawal, err := db.L2TransactionWithdrawal(withdrawalHash)
if err != nil {
return err
}
if withdrawal == nil {
return fmt.Errorf("transaction withdrawal hash %s not found", withdrawalHash)
}
withdrawal.ProvenL1EventGUID = &provenL1EventGuid
result := db.gorm.Save(&withdrawal)
return result.Error
}
// MarkL2TransactionWithdrawalProvenEvent links a withdrawn transaction in its finalized state
func (db *bridgeTransactionsDB) MarkL2TransactionWithdrawalFinalizedEvent(withdrawalHash common.Hash, finalizedL1EventGuid uuid.UUID, succeeded bool) error {
withdrawal, err := db.L2TransactionWithdrawal(withdrawalHash)
if err != nil {
return err
}
if withdrawal == nil {
return fmt.Errorf("transaction withdrawal hash %s not found", withdrawalHash)
}
if withdrawal.ProvenL1EventGUID == nil {
return fmt.Errorf("cannot mark unproven withdrawal hash %s as finalized", withdrawal.WithdrawalHash)
}
withdrawal.FinalizedL1EventGUID = &finalizedL1EventGuid
withdrawal.Succeeded = &succeeded
result := db.gorm.Save(&withdrawal)
return result.Error
}
package database
import (
"errors"
"math/big"
"gorm.io/gorm"
"github.com/ethereum/go-ethereum/common"
)
/**
* Types
*/
type TokenPair struct {
L1TokenAddress common.Address `gorm:"serializer:json"`
L2TokenAddress common.Address `gorm:"serializer:json"`
}
type L1BridgeDeposit struct {
TransactionSourceHash common.Hash `gorm:"primaryKey;serializer:json"`
CrossDomainMessengerNonce *U256
Tx Transaction `gorm:"embedded"`
TokenPair TokenPair `gorm:"embedded"`
}
type L1BridgeDepositWithTransactionHashes struct {
L1BridgeDeposit L1BridgeDeposit `gorm:"embedded"`
L1TransactionHash common.Hash `gorm:"serializer:json"`
L2TransactionHash common.Hash `gorm:"serializer:json"`
}
type L2BridgeWithdrawal struct {
TransactionWithdrawalHash common.Hash `gorm:"primaryKey;serializer:json"`
CrossDomainMessengerNonce *U256
Tx Transaction `gorm:"embedded"`
TokenPair TokenPair `gorm:"embedded"`
}
type L2BridgeWithdrawalWithTransactionHashes struct {
L2BridgeWithdrawal L2BridgeWithdrawal `gorm:"embedded"`
L2TransactionHash common.Hash `gorm:"serializer:json"`
ProvenL1TransactionHash common.Hash `gorm:"serializer:json"`
FinalizedL1TransactionHash common.Hash `gorm:"serializer:json"`
}
type BridgeTransfersView interface {
L1BridgeDeposit(common.Hash) (*L1BridgeDeposit, error)
L1BridgeDepositByCrossDomainMessengerNonce(*big.Int) (*L1BridgeDeposit, error)
L1BridgeDepositsByAddress(common.Address) ([]*L1BridgeDepositWithTransactionHashes, error)
L2BridgeWithdrawal(common.Hash) (*L2BridgeWithdrawal, error)
L2BridgeWithdrawalByCrossDomainMessengerNonce(*big.Int) (*L2BridgeWithdrawal, error)
L2BridgeWithdrawalsByAddress(common.Address) ([]*L2BridgeWithdrawalWithTransactionHashes, error)
}
type BridgeTransfersDB interface {
BridgeTransfersView
StoreL1BridgeDeposits([]*L1BridgeDeposit) error
StoreL2BridgeWithdrawals([]*L2BridgeWithdrawal) error
}
/**
* Implementation
*/
type bridgeTransfersDB struct {
gorm *gorm.DB
}
func newBridgeTransfersDB(db *gorm.DB) BridgeTransfersDB {
return &bridgeTransfersDB{gorm: db}
}
/**
* Tokens Bridged (Deposited) from L1
*/
func (db *bridgeTransfersDB) StoreL1BridgeDeposits(deposits []*L1BridgeDeposit) error {
result := db.gorm.Create(&deposits)
return result.Error
}
func (db *bridgeTransfersDB) L1BridgeDeposit(txSourceHash common.Hash) (*L1BridgeDeposit, error) {
var deposit L1BridgeDeposit
result := db.gorm.Where(&L1BridgeDeposit{TransactionSourceHash: txSourceHash}).Take(&deposit)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &deposit, nil
}
// L1BridgeDepositByCrossDomainMessengerNonce retrieves tokens deposited, specified by the associated `L1CrossDomainMessenger` nonce.
// All tokens bridged via the StandardBridge flows through the L1CrossDomainMessenger
func (db *bridgeTransfersDB) L1BridgeDepositByCrossDomainMessengerNonce(nonce *big.Int) (*L1BridgeDeposit, error) {
var deposit L1BridgeDeposit
result := db.gorm.Where(&L1BridgeDeposit{CrossDomainMessengerNonce: &U256{Int: nonce}}).Take(&deposit)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &deposit, nil
}
// L1BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction
// hashes that complete the bridge transaction.
func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address) ([]*L1BridgeDepositWithTransactionHashes, error) {
depositsQuery := db.gorm.Table("l1_bridge_deposits").Select(`
l1_bridge_deposits.*,
l1_contract_events.transaction_hash AS l1_transaction_hash,
l1_transaction_deposits.l2_transaction_hash`)
depositsQuery = depositsQuery.Joins("INNER JOIN l1_transaction_deposits ON l1_bridge_deposits.transaction_source_hash = l1_transaction_deposits.source_hash")
depositsQuery = depositsQuery.Joins("INNER JOIN l1_contract_events ON l1_transaction_deposits.initiated_l1_event_guid = l1_contract_events.guid")
// add in cursoring options
filteredQuery := depositsQuery.Where(&Transaction{FromAddress: address}).Order("l1_bridge_deposits.timestamp DESC").Limit(100)
deposits := []*L1BridgeDepositWithTransactionHashes{}
result := filteredQuery.Scan(&deposits)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return deposits, nil
}
/**
* Tokens Bridged (Withdrawn) from L2
*/
func (db *bridgeTransfersDB) StoreL2BridgeWithdrawals(withdrawals []*L2BridgeWithdrawal) error {
result := db.gorm.Create(&withdrawals)
return result.Error
}
func (db *bridgeTransfersDB) L2BridgeWithdrawal(txWithdrawalHash common.Hash) (*L2BridgeWithdrawal, error) {
var withdrawal L2BridgeWithdrawal
result := db.gorm.Where(&L2BridgeWithdrawal{TransactionWithdrawalHash: txWithdrawalHash}).Take(&withdrawal)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &withdrawal, nil
}
// L2BridgeWithdrawalByCrossDomainMessengerNonce retrieves tokens withdrawn, specified by the associated `L2CrossDomainMessenger` nonce.
// All tokens bridged via the StandardBridge flows through the L2CrossDomainMessenger
func (db *bridgeTransfersDB) L2BridgeWithdrawalByCrossDomainMessengerNonce(nonce *big.Int) (*L2BridgeWithdrawal, error) {
var withdrawal L2BridgeWithdrawal
result := db.gorm.Where(&L2BridgeWithdrawal{CrossDomainMessengerNonce: &U256{Int: nonce}}).Take(&withdrawal)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &withdrawal, nil
}
// L2BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction hashes
// that complete the bridge transaction. The hashes that correspond to with the Bedrock multistep withdrawal process are also surfaced
func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address) ([]*L2BridgeWithdrawalWithTransactionHashes, error) {
withdrawalsQuery := db.gorm.Table("l2_bridge_withdrawals").Select(`
l2_bridge_withdrawals.*,
l2_contract_events.transaction_hash AS l2_transaction_hash,
proven_l1_contract_events.transaction_hash AS proven_l1_transaction_hash,
finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`)
withdrawalsQuery = withdrawalsQuery.Joins("INNER JOIN l2_transaction_withdrawals ON l2_bridge_withdrawals.transaction_withdrawal_hash = l2_transaction_withdrawals.withdrawal_hash")
withdrawalsQuery = withdrawalsQuery.Joins("INNER JOIN l2_contract_events ON l2_transaction_withdrawals.initiated_l2_event_guid = l2_contract_events.guid")
withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS proven_l1_contract_events ON l2_transaction_withdrawals.proven_l1_event_guid = proven_l1_contract_events.guid")
withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS finalized_l1_contract_events ON l2_transaction_withdrawals.finalized_l1_event_guid = finalized_l1_contract_events.guid")
// add in cursoring options
filteredQuery := withdrawalsQuery.Where(&Transaction{FromAddress: address}).Order("l2_bridge_withdrawals.timestamp DESC").Limit(100)
withdrawals := []*L2BridgeWithdrawalWithTransactionHashes{}
result := filteredQuery.Scan(&withdrawals)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return withdrawals, nil
}
......@@ -10,9 +10,11 @@ import (
type DB struct {
gorm *gorm.DB
Blocks BlocksDB
ContractEvents ContractEventsDB
Bridge BridgeDB
Blocks BlocksDB
ContractEvents ContractEventsDB
BridgeTransfers BridgeTransfersDB
BridgeMessages BridgeMessagesDB
BridgeTransactions BridgeTransactionsDB
}
func NewDB(dsn string) (*DB, error) {
......@@ -31,10 +33,12 @@ func NewDB(dsn string) (*DB, error) {
}
db := &DB{
gorm: gorm,
Blocks: newBlocksDB(gorm),
ContractEvents: newContractEventsDB(gorm),
Bridge: newBridgeDB(gorm),
gorm: gorm,
Blocks: newBlocksDB(gorm),
ContractEvents: newContractEventsDB(gorm),
BridgeTransfers: newBridgeTransfersDB(gorm),
BridgeMessages: newBridgeMessagesDB(gorm),
BridgeTransactions: newBridgeTransactionsDB(gorm),
}
return db, nil
......@@ -59,9 +63,11 @@ func (db *DB) Close() error {
func dbFromGormTx(tx *gorm.DB) *DB {
return &DB{
gorm: tx,
Blocks: newBlocksDB(tx),
ContractEvents: newContractEventsDB(tx),
Bridge: newBridgeDB(tx),
gorm: tx,
Blocks: newBlocksDB(tx),
ContractEvents: newContractEventsDB(tx),
BridgeTransfers: newBridgeTransfersDB(tx),
BridgeMessages: newBridgeMessagesDB(tx),
BridgeTransactions: newBridgeTransactionsDB(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 (
func TestE2EBlockHeaders(t *testing.T) {
testSuite := createE2ETestSuite(t)
l1Client := testSuite.OpSys.Clients["l1"]
l2Client := testSuite.OpSys.Clients["sequencer"]
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, l1Client)
l2OutputOracle, err := bindings.NewL2OutputOracle(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, testSuite.L1Client)
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
require.NoError(t, utils.WaitFor(setupCtx, time.Second, func() (bool, error) {
l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: setupCtx})
require.NoError(t, utils.WaitFor(context.Background(), time.Second, func() (bool, error) {
l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: context.Background()})
return l2Height != nil && l2Height.Uint64() >= 9, err
}))
// 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, 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()
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return (l1Header != nil && l1Header.Number.Uint64() >= l1Height) && (l2Header != nil && l2Header.Number.Uint64() >= 9), nil
......@@ -60,7 +54,7 @@ func TestE2EBlockHeaders(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, indexedHeader)
header, err := l2Client.HeaderByNumber(context.Background(), height)
header, err := testSuite.L2Client.HeaderByNumber(context.Background(), height)
require.NoError(t, err)
require.NotNil(t, indexedHeader)
......@@ -93,7 +87,7 @@ func TestE2EBlockHeaders(t *testing.T) {
require.NotEmpty(t, output.L1ContractEventGUID)
// 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)
messagePasserStorageHash, err := l2EthClient.StorageHash(predeploys.L2ToL1MessagePasserAddr, blockNumber)
require.NoError(t, err)
......@@ -111,12 +105,10 @@ func TestE2EBlockHeaders(t *testing.T) {
testCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
devContracts := make([]common.Address, 0)
testSuite.OpCfg.L1Deployments.ForEach(func(name string, address common.Address) {
devContracts = append(devContracts, address)
})
logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: devContracts}
logs, err := l1Client.FilterLogs(testCtx, logFilter) // []types.Log
l1Contracts := []common.Address{}
testSuite.OpCfg.L1Deployments.ForEach(func(name string, addr common.Address) { l1Contracts = append(l1Contracts, addr) })
logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: l1Contracts}
logs, err := testSuite.L1Client.FilterLogs(testCtx, logFilter) // []types.Log
require.NoError(t, err)
for _, log := range logs {
......@@ -128,7 +120,7 @@ func TestE2EBlockHeaders(t *testing.T) {
require.Equal(t, log.Index, uint(contractEvent.LogIndex))
// 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.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"
"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/withdrawals"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) {
testSuite := createE2ETestSuite(t)
l1CrossDomainMessenger, err := bindings.NewL1CrossDomainMessenger(testSuite.OpCfg.L1Deployments.L1CrossDomainMessengerProxy, testSuite.L1Client)
require.NoError(t, err)
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
// Attach 1ETH and random calldata to the sent messages
calldata := []byte{byte(1), byte(2), byte(3)}
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
l1Opts.Value = big.NewInt(params.Ether)
// Pause the processor to track relayed event
testSuite.Indexer.L2Processor.PauseForTest()
// (1) Send the Message
sentMsgTx, err := l1CrossDomainMessenger.SendMessage(l1Opts, aliceAddr, calldata, 100_000)
require.NoError(t, err)
sentMsgReceipt, err := utils.WaitReceiptOK(context.Background(), testSuite.L1Client, sentMsgTx.Hash())
require.NoError(t, err)
depositInfo, err := e2etest_utils.ParseDepositInfo(sentMsgReceipt)
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() >= sentMsgReceipt.BlockNumber.Uint64(), nil
}))
parsedMessage, err := e2etest_utils.ParseCrossDomainMessage(sentMsgReceipt)
require.NoError(t, err)
// nonce for this message is zero but the current cross domain message version is 1.
nonceBytes := [31]byte{0: byte(1)}
nonce := new(big.Int).SetBytes(nonceBytes[:])
sentMessage, err := testSuite.DB.BridgeMessages.L1BridgeMessage(nonce)
require.NoError(t, err)
require.NotNil(t, sentMessage)
require.NotNil(t, sentMessage.SentMessageEventGUID)
require.Equal(t, depositInfo.DepositTx.SourceHash, sentMessage.TransactionSourceHash)
require.Equal(t, parsedMessage.MessageHash, sentMessage.MessageHash)
require.Equal(t, uint64(100_000), sentMessage.GasLimit.Int.Uint64())
require.Equal(t, big.NewInt(params.Ether), sentMessage.Tx.Amount.Int)
require.Equal(t, aliceAddr, sentMessage.Tx.FromAddress)
require.Equal(t, aliceAddr, sentMessage.Tx.ToAddress)
require.ElementsMatch(t, calldata, sentMessage.Tx.Data)
// (2) Process RelayedMesssage on inclusion
require.Nil(t, sentMessage.RelayedMessageEventGUID)
testSuite.Indexer.L2Processor.ResumeForTest()
transaction, err := testSuite.DB.BridgeTransactions.L1TransactionDeposit(sentMessage.TransactionSourceHash)
require.NoError(t, err)
// wait for processor catchup
depositReceipt, err := utils.WaitReceiptOK(context.Background(), testSuite.L2Client, transaction.L2TransactionHash)
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
}))
sentMessage, err = testSuite.DB.BridgeMessages.L1BridgeMessage(nonce)
require.NoError(t, err)
require.NotNil(t, sentMessage)
require.NotNil(t, sentMessage.RelayedMessageEventGUID)
event, err := testSuite.DB.ContractEvents.L2ContractEvent(*sentMessage.RelayedMessageEventGUID)
require.NoError(t, err)
require.NotNil(t, event)
require.Equal(t, event.TransactionHash, transaction.L2TransactionHash)
}
func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) {
testSuite := createE2ETestSuite(t)
optimismPortal, err := bindings.NewOptimismPortal(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client)
require.NoError(t, err)
l2CrossDomainMessenger, err := bindings.NewL2CrossDomainMessenger(predeploys.L2CrossDomainMessengerAddr, testSuite.L2Client)
require.NoError(t, err)
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
// Attach 1ETH and random calldata to the sent messages
calldata := []byte{byte(1), byte(2), byte(3)}
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) Send the Message
sentMsgTx, err := l2CrossDomainMessenger.SendMessage(l2Opts, aliceAddr, calldata, 100_000)
require.NoError(t, err)
sentMsgReceipt, err := utils.WaitReceiptOK(context.Background(), testSuite.L2Client, sentMsgTx.Hash())
require.NoError(t, err)
msgPassed, err := withdrawals.ParseMessagePassed(sentMsgReceipt)
require.NoError(t, err)
withdrawalHash, err := withdrawals.WithdrawalHash(msgPassed)
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() >= sentMsgReceipt.BlockNumber.Uint64(), nil
}))
parsedMessage, err := e2etest_utils.ParseCrossDomainMessage(sentMsgReceipt)
require.NoError(t, err)
// nonce for this message is zero but the current message version is 1.
nonceBytes := [31]byte{0: byte(1)}
nonce := new(big.Int).SetBytes(nonceBytes[:])
sentMessage, err := testSuite.DB.BridgeMessages.L2BridgeMessage(nonce)
require.NoError(t, err)
require.NotNil(t, sentMessage)
require.NotNil(t, sentMessage.SentMessageEventGUID)
require.Equal(t, withdrawalHash, sentMessage.TransactionWithdrawalHash)
require.Equal(t, parsedMessage.MessageHash, sentMessage.MessageHash)
require.Equal(t, uint64(100_000), sentMessage.GasLimit.Int.Uint64())
require.Equal(t, big.NewInt(params.Ether), sentMessage.Tx.Amount.Int)
require.Equal(t, aliceAddr, sentMessage.Tx.FromAddress)
require.Equal(t, aliceAddr, sentMessage.Tx.ToAddress)
require.ElementsMatch(t, calldata, sentMessage.Tx.Data)
// (2) Process RelayedMessage on withdrawal finalization
require.Nil(t, sentMessage.RelayedMessageEventGUID)
_, finalizedReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, sentMsgReceipt)
// 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() >= finalizedReceipt.BlockNumber.Uint64(), nil
}))
// message is marked as relayed
sentMessage, err = testSuite.DB.BridgeMessages.L2BridgeMessage(nonce)
require.NoError(t, err)
require.NotNil(t, sentMessage)
require.NotNil(t, sentMessage.RelayedMessageEventGUID)
event, err := testSuite.DB.ContractEvents.L1ContractEvent(*sentMessage.RelayedMessageEventGUID)
require.NoError(t, err)
require.NotNil(t, event)
require.Equal(t, event.TransactionHash, finalizedReceipt.TxHash)
}
package e2e_tests
import (
"context"
"math/big"
"testing"
"time"
e2etest_utils "github.com/ethereum-optimism/optimism/indexer/e2e_tests/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/withdrawals"
"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 TestE2EBridgeTransactionsOptimismPortalDeposits(t *testing.T) {
testSuite := createE2ETestSuite(t)
optimismPortal, err := bindings.NewOptimismPortal(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client)
require.NoError(t, err)
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
// attach 1 ETH to the deposit and random calldata
calldata := []byte{byte(1), byte(2), byte(3)}
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
l1Opts.Value = big.NewInt(params.Ether)
depositTx, err := optimismPortal.DepositTransaction(l1Opts, aliceAddr, l1Opts.Value, 100_000, false, calldata)
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)
depositL2TxHash := types.NewTx(depositInfo.DepositTx).Hash()
// 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
}))
deposit, err := testSuite.DB.BridgeTransactions.L1TransactionDeposit(depositInfo.DepositTx.SourceHash)
require.NoError(t, err)
require.Equal(t, depositL2TxHash, deposit.L2TransactionHash)
require.Equal(t, big.NewInt(100_000), deposit.GasLimit.Int)
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.ElementsMatch(t, calldata, deposit.Tx.Data)
require.Equal(t, depositInfo.Version.Uint64(), deposit.Version.Int.Uint64())
require.ElementsMatch(t, depositInfo.OpaqueData, deposit.OpaqueData)
event, err := testSuite.DB.ContractEvents.L1ContractEvent(deposit.InitiatedL1EventGUID)
require.NoError(t, err)
require.NotNil(t, event)
require.Equal(t, event.TransactionHash, depositTx.Hash())
// NOTE: The indexer does not track deposit inclusion as it's apart of the block derivation process.
// If this changes, we'd like to test for this here.
}
func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) {
testSuite := createE2ETestSuite(t)
optimismPortal, err := bindings.NewOptimismPortal(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client)
require.NoError(t, err)
l2ToL1MessagePasser, err := bindings.NewL2ToL1MessagePasser(predeploys.L2ToL1MessagePasserAddr, testSuite.L2Client)
require.NoError(t, err)
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
// attach 1 ETH to the withdrawal and random calldata
calldata := []byte{byte(1), byte(2), byte(3)}
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)
withdrawTx, err := l2ToL1MessagePasser.InitiateWithdrawal(l2Opts, aliceAddr, big.NewInt(100_000), calldata)
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
}))
msgPassed, err := withdrawals.ParseMessagePassed(withdrawReceipt)
require.NoError(t, err)
withdrawalHash, err := withdrawals.WithdrawalHash(msgPassed)
require.NoError(t, err)
withdraw, err := testSuite.DB.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
require.NoError(t, err)
require.Equal(t, msgPassed.Nonce.Uint64(), withdraw.Nonce.Int.Uint64())
require.Equal(t, big.NewInt(100_000), withdraw.GasLimit.Int)
require.Equal(t, big.NewInt(params.Ether), withdraw.Tx.Amount.Int)
require.Equal(t, aliceAddr, withdraw.Tx.FromAddress)
require.Equal(t, aliceAddr, withdraw.Tx.ToAddress)
require.ElementsMatch(t, calldata, withdraw.Tx.Data)
event, err := testSuite.DB.ContractEvents.L2ContractEvent(withdraw.InitiatedL2EventGUID)
require.NoError(t, err)
require.NotNil(t, event)
require.Equal(t, event.TransactionHash, withdrawTx.Hash())
// Test Withdrawal Proven
require.Nil(t, withdraw.ProvenL1EventGUID)
require.Nil(t, withdraw.FinalizedL1EventGUID)
withdrawParams, proveReceipt := op_e2e.ProveWithdrawal(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() >= proveReceipt.BlockNumber.Uint64(), nil
}))
withdraw, err = testSuite.DB.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
require.NoError(t, err)
require.NotNil(t, withdraw.ProvenL1EventGUID)
proveEvent, err := testSuite.DB.ContractEvents.L1ContractEvent(*withdraw.ProvenL1EventGUID)
require.NoError(t, err)
require.NotNil(t, event)
require.Equal(t, proveEvent.TransactionHash, proveReceipt.TxHash)
// Test Withdrawal Finalized
require.Nil(t, withdraw.FinalizedL1EventGUID)
finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpCfg.Secrets.Alice, proveReceipt, withdrawParams)
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
}))
withdraw, err = testSuite.DB.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
require.NoError(t, err)
require.NotNil(t, withdraw.FinalizedL1EventGUID)
require.NotNil(t, withdraw.Succeeded)
require.True(t, *withdraw.Succeeded)
finalizedEvent, err := testSuite.DB.ContractEvents.L1ContractEvent(*withdraw.FinalizedL1EventGUID)
require.NoError(t, err)
require.NotNil(t, event)
require.Equal(t, finalizedEvent.TransactionHash, finalizeReceipt.TxHash)
}
func TestE2EBridgeTransactionsL2ToL1MessagePasserFailedWithdrawal(t *testing.T) {
testSuite := createE2ETestSuite(t)
l2ToL1MessagePasser, err := bindings.NewL2ToL1MessagePasser(predeploys.L2ToL1MessagePasserAddr, testSuite.L2Client)
require.NoError(t, err)
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
// Try to withdraw 1 ETH from L2 without any corresponding deposits on L1
l2Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L2ChainIDBig())
require.NoError(t, err)
l2Opts.Value = big.NewInt(params.Ether)
withdrawTx, err := l2ToL1MessagePasser.InitiateWithdrawal(l2Opts, aliceAddr, big.NewInt(100_000), nil)
require.NoError(t, err)
withdrawReceipt, err := utils.WaitReceiptOK(context.Background(), testSuite.L2Client, withdrawTx.Hash())
require.NoError(t, err)
msgPassed, err := withdrawals.ParseMessagePassed(withdrawReceipt)
require.NoError(t, err)
withdrawalHash, err := withdrawals.WithdrawalHash(msgPassed)
require.NoError(t, err)
// Prove&Finalize withdrawal
_, 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
}))
// Withdrawal registered but marked as unsuccessful
withdraw, err := testSuite.DB.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
require.NoError(t, err)
require.NotNil(t, withdraw.Succeeded)
require.False(t, *withdraw.Succeeded)
}
This diff is collapsed.
......@@ -17,6 +17,7 @@ import (
op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
_ "github.com/jackc/pgx/v5/stdlib"
......@@ -33,6 +34,10 @@ type E2ETestSuite struct {
// Rollup
OpCfg *op_e2e.SystemConfig
OpSys *op_e2e.System
// Clients
L1Client *ethclient.Client
L2Client *ethclient.Client
}
func createE2ETestSuite(t *testing.T) E2ETestSuite {
......@@ -48,16 +53,13 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
opSys, err := opCfg.Start()
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,
}
// E2E tests can run on the order of magnitude of minutes. Once
// the system is running, mark this test for Parallel execution
t.Parallel()
// Indexer Configuration and Start
indexerCfg := config.Config{
Logger: logger,
DB: config.DBConfig{
Host: "127.0.0.1",
Port: 5432,
......@@ -68,9 +70,14 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
L1RPC: opSys.Nodes["l1"].HTTPEndpoint(),
L2RPC: opSys.Nodes["sequencer"].HTTPEndpoint(),
},
Logger: logger,
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,
},
},
}
......@@ -79,36 +86,38 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
indexer, err := indexer.NewIndexer(indexerCfg)
require.NoError(t, err)
indexerStoppedCh := make(chan interface{}, 1)
indexerCtx, indexerStop := context.WithCancel(context.Background())
go func() {
err := indexer.Run(indexerCtx)
require.NoError(t, err)
indexer.Cleanup()
indexerStoppedCh <- nil
}()
t.Cleanup(func() {
indexerStop()
<-indexerStoppedCh
// wait a second for the stop signal to be received
time.Sleep(1 * time.Second)
indexer.Cleanup()
db.Close()
opSys.Close()
})
return E2ETestSuite{
t: t,
DB: db,
Indexer: indexer,
OpCfg: &opCfg,
OpSys: opSys,
t: t,
DB: db,
Indexer: indexer,
OpCfg: &opCfg,
OpSys: opSys,
L1Client: opSys.Clients["l1"],
L2Client: opSys.Clients["sequencer"],
}
}
func setupTestDatabase(t *testing.T) string {
user := os.Getenv("DB_USER")
require.NotEmpty(t, user, "DB_USER env variable expected to instantiate test database")
pg, err := sql.Open("pgx", fmt.Sprintf("postgres://%s@localhost:5432?sslmode=disable", user))
require.NoError(t, err)
require.NoError(t, pg.Ping())
......
package utils
import (
"errors"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/processor"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type CrossDomainMessengerSentMessage struct {
*bindings.CrossDomainMessengerSentMessage
Value *big.Int
MessageHash common.Hash
}
func ParseCrossDomainMessage(sentMessageReceipt *types.Receipt) (CrossDomainMessengerSentMessage, error) {
abi, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return CrossDomainMessengerSentMessage{}, err
}
sentMessageEventAbi := abi.Events["SentMessage"]
messenger, err := bindings.NewCrossDomainMessenger(common.Address{}, nil)
if err != nil {
return CrossDomainMessengerSentMessage{}, err
}
for i, log := range sentMessageReceipt.Logs {
if len(log.Topics) > 0 && log.Topics[0] == sentMessageEventAbi.ID {
sentMessage, err := messenger.ParseSentMessage(*log)
if err != nil {
return CrossDomainMessengerSentMessage{}, err
}
sentMessageExtension, err := messenger.ParseSentMessageExtension1(*sentMessageReceipt.Logs[i+1])
if err != nil {
return CrossDomainMessengerSentMessage{}, err
}
msgHash, err := CrossDomainMessengerSentMessageHash(sentMessage, sentMessageExtension.Value)
if err != nil {
return CrossDomainMessengerSentMessage{}, err
}
return CrossDomainMessengerSentMessage{sentMessage, sentMessageExtension.Value, msgHash}, nil
}
}
return CrossDomainMessengerSentMessage{}, errors.New("missing SentMessage receipts")
}
func CrossDomainMessengerSentMessageHash(sentMessage *bindings.CrossDomainMessengerSentMessage, value *big.Int) (common.Hash, error) {
abi, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return common.Hash{}, err
}
return processor.CrossDomainMessageHash(abi, sentMessage, value)
}
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) {
// Start starts the indexing service on L1 and L2 chains
func (i *Indexer) Run(ctx context.Context) error {
var wg sync.WaitGroup
errCh := make(chan error)
errCh := make(chan error, 1)
// If either processor errors out, we stop
processorCtx, cancel := context.WithCancel(ctx)
run := func(start func(ctx context.Context) error) {
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)
if err != nil {
i.log.Error("halting indexer on error", "err", err)
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
......
CREATE DOMAIN UINT256 AS NUMERIC NOT NULL
CREATE DOMAIN UINT256 AS NUMERIC
CHECK (VALUE >= 0 AND VALUE < 2^256 and SCALE(VALUE) = 0);
/**
......@@ -9,7 +9,7 @@ CREATE DOMAIN UINT256 AS NUMERIC NOT NULL
CREATE TABLE IF NOT EXISTS l1_block_headers (
hash VARCHAR NOT NULL PRIMARY KEY,
parent_hash VARCHAR NOT NULL,
number UINT256,
number UINT256 NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
......@@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS l2_block_headers (
-- Block header
hash VARCHAR NOT NULL PRIMARY KEY,
parent_hash VARCHAR NOT NULL,
number UINT256,
number UINT256 NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
......@@ -57,8 +57,8 @@ CREATE TABLE IF NOT EXISTS legacy_state_batches (
CREATE TABLE IF NOT EXISTS output_proposals (
output_root VARCHAR NOT NULL PRIMARY KEY,
l2_output_index UINT256,
l2_block_number UINT256,
l2_output_index UINT256 NOT NULL,
l2_block_number UINT256 NOT NULL,
l1_contract_event_guid VARCHAR REFERENCES l1_contract_events(guid)
);
......@@ -67,47 +67,111 @@ CREATE TABLE IF NOT EXISTS output_proposals (
* BRIDGING DATA
*/
CREATE TABLE IF NOT EXISTS deposits (
guid VARCHAR PRIMARY KEY NOT NULL,
-- OptimismPortal/L2ToL1MessagePasser
CREATE TABLE IF NOT EXISTS l1_transaction_deposits (
source_hash VARCHAR NOT NULL PRIMARY KEY,
l2_transaction_hash VARCHAR NOT NULL,
-- Event causing the deposit
initiated_l1_event_guid VARCHAR NOT NULL REFERENCES l1_contract_events(guid),
sent_message_nonce UINT256 UNIQUE,
-- Finalization marker for the deposit
finalized_l2_event_guid VARCHAR REFERENCES l2_contract_events(guid),
-- OptimismPortal specific
version UINT256 NOT NULL,
opaque_data VARCHAR NOT NULL,
-- transaction data
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
amount UINT256 NOT NULL,
gas_limit UINT256 NOT NULL,
data VARCHAR NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
CREATE TABLE IF NOT EXISTS l2_transaction_withdrawals (
withdrawal_hash VARCHAR NOT NULL PRIMARY KEY,
-- Deposit information (do we need indexes on from/to?)
from_address VARCHAR NOT NULL,
initiated_l2_event_guid VARCHAR NOT NULL REFERENCES l2_contract_events(guid),
-- Multistep (bedrock) process of a withdrawal
proven_l1_event_guid VARCHAR REFERENCES l1_contract_events(guid),
finalized_l1_event_guid VARCHAR REFERENCES l1_contract_events(guid),
succeeded BOOLEAN,
-- L2ToL1MessagePasser specific
nonce UINT256 UNIQUE,
-- transaction data
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
amount UINT256 NOT NULL,
gas_limit UINT256 NOT NULL,
data VARCHAR NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
-- CrossDomainMessenger
CREATE TABLE IF NOT EXISTS l1_bridge_messages(
nonce UINT256 NOT NULL PRIMARY KEY,
message_hash VARCHAR NOT NULL,
transaction_source_hash VARCHAR NOT NULL REFERENCES l1_transaction_deposits(source_hash),
sent_message_event_guid VARCHAR NOT NULL UNIQUE REFERENCES l1_contract_events(guid),
relayed_message_event_guid VARCHAR UNIQUE REFERENCES l2_contract_events(guid),
-- sent message
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
amount UINT256 NOT NULL,
gas_limit UINT256 NOT NULL,
data VARCHAR NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
CREATE TABLE IF NOT EXISTS l2_bridge_messages(
nonce UINT256 NOT NULL PRIMARY KEY,
message_hash VARCHAR NOT NULL,
transaction_withdrawal_hash VARCHAR NOT NULL REFERENCES l2_transaction_withdrawals(withdrawal_hash),
sent_message_event_guid VARCHAR NOT NULL UNIQUE REFERENCES l2_contract_events(guid),
relayed_message_event_guid VARCHAR UNIQUE REFERENCES l1_contract_events(guid),
-- sent message
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
amount UINT256 NOT NULL,
gas_limit UINT256 NOT NULL,
data VARCHAR NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
-- StandardBridge
CREATE TABLE IF NOT EXISTS l1_bridge_deposits (
transaction_source_hash VARCHAR PRIMARY KEY REFERENCES l1_transaction_deposits(source_hash),
-- We allow the cross_domain_messenger_nonce to be NULL-able to account
-- for scenarios where ETH is simply sent to the OptimismPortal contract
cross_domain_messenger_nonce UINT256 UNIQUE REFERENCES l1_bridge_messages(nonce),
-- Deposit information
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
l1_token_address VARCHAR NOT NULL,
l2_token_address VARCHAR NOT NULL,
amount UINT256,
amount UINT256 NOT NULL,
data VARCHAR NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
CREATE TABLE IF NOT EXISTS l2_bridge_withdrawals (
transaction_withdrawal_hash VARCHAR PRIMARY KEY REFERENCES l2_transaction_withdrawals(withdrawal_hash),
CREATE TABLE IF NOT EXISTS withdrawals (
guid VARCHAR PRIMARY KEY NOT NULL,
-- Event causing this withdrawal
initiated_l2_event_guid VARCHAR NOT NULL REFERENCES l2_contract_events(guid),
sent_message_nonce UINT256 UNIQUE,
-- Multistep (bedrock) process of a withdrawal
withdrawal_hash VARCHAR NOT NULL,
proven_l1_event_guid VARCHAR REFERENCES l1_contract_events(guid),
-- Finalization marker (legacy & bedrock)
finalized_l1_event_guid VARCHAR REFERENCES l1_contract_events(guid),
-- We allow the cross_domain_messenger_nonce to be NULL-able to account for
-- scenarios where ETH is simply sent to the L2ToL1MessagePasser contract
cross_domain_messenger_nonce UINT256 UNIQUE REFERENCES l2_bridge_messages(nonce),
-- Withdrawal information (do we need indexes on from/to?)
-- Withdrawal information
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
l1_token_address VARCHAR NOT NULL,
l2_token_address VARCHAR NOT NULL,
amount UINT256,
amount UINT256 NOT NULL,
data VARCHAR NOT NULL,
timestamp INTEGER NOT NULL CHECK (timestamp > 0)
);
package processor
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
)
var (
// Standard ABI types copied from golang ABI tests
Uint256Type, _ = abi.NewType("uint256", "", nil)
BytesType, _ = abi.NewType("bytes", "", nil)
AddressType, _ = abi.NewType("address", "", nil)
LegacyCrossDomainMessengerRelayMessageMethod = abi.NewMethod(
"relayMessage",
"relayMessage",
abi.Function,
"external", // mutability
false, // isConst
true, // payable
abi.Arguments{ // inputs
{Name: "sender", Type: AddressType},
{Name: "target", Type: AddressType},
{Name: "data", Type: BytesType},
{Name: "nonce", Type: Uint256Type},
},
abi.Arguments{}, // outputs
)
)
type CrossDomainMessengerSentMessageEvent struct {
*bindings.CrossDomainMessengerSentMessage
Value *big.Int
MessageHash common.Hash
RawEvent *database.ContractEvent
}
type CrossDomainMessengerRelayedMessageEvent struct {
*bindings.CrossDomainMessengerRelayedMessage
RawEvent *database.ContractEvent
}
func CrossDomainMessengerSentMessageEvents(events *ProcessedContractEvents) ([]CrossDomainMessengerSentMessageEvent, error) {
crossDomainMessengerABI, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
sentMessageEventAbi := crossDomainMessengerABI.Events["SentMessage"]
sentMessageEventExtensionAbi := crossDomainMessengerABI.Events["SentMessageExtension1"]
processedSentMessageEvents := events.eventsBySignature[sentMessageEventAbi.ID]
crossDomainMessageEvents := make([]CrossDomainMessengerSentMessageEvent, len(processedSentMessageEvents))
for i, sentMessageEvent := range processedSentMessageEvents {
log := events.eventLog[sentMessageEvent.GUID]
var sentMsgData bindings.CrossDomainMessengerSentMessage
err = UnpackLog(&sentMsgData, log, sentMessageEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
var sentMsgExtensionData bindings.CrossDomainMessengerSentMessageExtension1
extensionLog := events.eventLog[events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 1}].GUID]
err = UnpackLog(&sentMsgExtensionData, extensionLog, sentMessageEventExtensionAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
msgHash, err := CrossDomainMessageHash(crossDomainMessengerABI, &sentMsgData, sentMsgExtensionData.Value)
if err != nil {
return nil, err
}
crossDomainMessageEvents[i] = CrossDomainMessengerSentMessageEvent{
CrossDomainMessengerSentMessage: &sentMsgData,
Value: sentMsgExtensionData.Value,
MessageHash: msgHash,
RawEvent: sentMessageEvent,
}
}
return crossDomainMessageEvents, nil
}
func CrossDomainMessengerRelayedMessageEvents(events *ProcessedContractEvents) ([]CrossDomainMessengerRelayedMessageEvent, error) {
crossDomainMessengerABI, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
relayedMessageEventAbi := crossDomainMessengerABI.Events["RelayedMessage"]
processedRelayedMessageEvents := events.eventsBySignature[relayedMessageEventAbi.ID]
crossDomainMessageEvents := make([]CrossDomainMessengerRelayedMessageEvent, len(processedRelayedMessageEvents))
for i, relayedMessageEvent := range processedRelayedMessageEvents {
log := events.eventLog[relayedMessageEvent.GUID]
var relayedMsgData bindings.CrossDomainMessengerRelayedMessage
err = UnpackLog(&relayedMsgData, log, relayedMessageEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
crossDomainMessageEvents[i] = CrossDomainMessengerRelayedMessageEvent{&relayedMsgData, relayedMessageEvent}
}
return crossDomainMessageEvents, nil
}
// Replica of `Hashing.sol#hashCrossDomainMessage` solidity implementation
func CrossDomainMessageHash(abi *abi.ABI, sentMsg *bindings.CrossDomainMessengerSentMessage, value *big.Int) (common.Hash, error) {
version, _ := DecodeVersionedNonce(sentMsg.MessageNonce)
switch version {
case 0:
// Legacy Message
inputBytes, err := LegacyCrossDomainMessengerRelayMessageMethod.Inputs.Pack(sentMsg.Sender, sentMsg.Target, sentMsg.Message, sentMsg.MessageNonce)
if err != nil {
return common.Hash{}, err
}
msgBytes := append(LegacyCrossDomainMessengerRelayMessageMethod.ID, inputBytes...)
return crypto.Keccak256Hash(msgBytes), nil
case 1:
// Current Message
msgBytes, err := abi.Pack("relayMessage", sentMsg.MessageNonce, sentMsg.Sender, sentMsg.Target, value, sentMsg.GasLimit, sentMsg.Message)
if err != nil {
return common.Hash{}, err
}
return crypto.Keccak256Hash(msgBytes), nil
}
return common.Hash{}, fmt.Errorf("unsupported cross domain messenger version: %d", version)
}
package processor
import (
"encoding/binary"
"math/big"
)
// DecodeVersionNonce is an re-implementation of Encoding.sol#decodeVersionedNonce.
// If the nonce is greater than 32 bytes (solidity uint256), bytes [32:] are ignored
func DecodeVersionedNonce(nonce *big.Int) (uint16, *big.Int) {
nonceBytes := nonce.Bytes()
nonceByteLen := len(nonceBytes)
if nonceByteLen < 30 {
// version is 0x0000
return 0, nonce
} else if nonceByteLen == 31 {
// version is 0x00[01..ff]
return uint16(nonceBytes[0]), new(big.Int).SetBytes(nonceBytes[1:])
} else {
// fully specified
version := binary.BigEndian.Uint16(nonceBytes[:2])
return version, new(big.Int).SetBytes(nonceBytes[2:])
}
}
This diff is collapsed.
This diff is collapsed.
package processor
import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
)
type L2ToL1MessagePasserMessagePassed struct {
*bindings.L2ToL1MessagePasserMessagePassed
RawEvent *database.ContractEvent
}
func L2ToL1MessagePasserMessagesPassed(events *ProcessedContractEvents) ([]L2ToL1MessagePasserMessagePassed, error) {
l2ToL1MessagePasserAbi, err := bindings.L2ToL1MessagePasserMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "MessagePassed"
processedMessagePassedEvents := events.eventsBySignature[l2ToL1MessagePasserAbi.Events[eventName].ID]
messagesPassed := make([]L2ToL1MessagePasserMessagePassed, len(processedMessagePassedEvents))
for i, messagePassedEvent := range processedMessagePassedEvents {
log := events.eventLog[messagePassedEvent.GUID]
var messagePassed bindings.L2ToL1MessagePasserMessagePassed
err := UnpackLog(&messagePassed, log, eventName, l2ToL1MessagePasserAbi)
if err != nil {
return nil, err
}
messagesPassed[i] = L2ToL1MessagePasserMessagePassed{&messagePassed, messagePassedEvent}
}
return messagesPassed, nil
}
......@@ -2,19 +2,32 @@ package processor
import (
"context"
"errors"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
)
type OptimismPortalTransactionDepositEvent struct {
*bindings.OptimismPortalTransactionDeposited
DepositTx *types.DepositTx
RawEvent *database.ContractEvent
}
type OptimismPortalWithdrawalProvenEvent struct {
*bindings.OptimismPortalWithdrawalProven
RawEvent *database.ContractEvent
}
type OptimismPortalWithdrawalFinalizedEvent struct {
*bindings.OptimismPortalWithdrawalFinalized
RawEvent *database.ContractEvent
}
......@@ -24,6 +37,39 @@ type OptimismPortalProvenWithdrawal struct {
L2OutputIndex *big.Int
}
func OptimismPortalTransactionDepositEvents(events *ProcessedContractEvents) ([]OptimismPortalTransactionDepositEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "TransactionDeposited"
if optimismPortalAbi.Events[eventName].ID != derive.DepositEventABIHash {
return nil, errors.New("op-node deposit event abi hash & optimism portal tx deposit mismatch")
}
processedTxDepositedEvents := events.eventsBySignature[derive.DepositEventABIHash]
txDeposits := make([]OptimismPortalTransactionDepositEvent, len(processedTxDepositedEvents))
for i, txDepositEvent := range processedTxDepositedEvents {
log := events.eventLog[txDepositEvent.GUID]
depositTx, err := derive.UnmarshalDepositLogEvent(log)
if err != nil {
return nil, err
}
var txDeposit bindings.OptimismPortalTransactionDeposited
err = UnpackLog(&txDeposit, log, eventName, optimismPortalAbi)
if err != nil {
return nil, err
}
txDeposits[i] = OptimismPortalTransactionDepositEvent{&txDeposit, depositTx, txDepositEvent}
}
return txDeposits, nil
}
func OptimismPortalWithdrawalProvenEvents(events *ProcessedContractEvents) ([]OptimismPortalWithdrawalProvenEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
......@@ -31,7 +77,6 @@ func OptimismPortalWithdrawalProvenEvents(events *ProcessedContractEvents) ([]Op
}
eventName := "WithdrawalProven"
processedWithdrawalProvenEvents := events.eventsBySignature[optimismPortalAbi.Events[eventName].ID]
provenEvents := make([]OptimismPortalWithdrawalProvenEvent, len(processedWithdrawalProvenEvents))
for i, provenEvent := range processedWithdrawalProvenEvents {
......@@ -49,6 +94,30 @@ func OptimismPortalWithdrawalProvenEvents(events *ProcessedContractEvents) ([]Op
return provenEvents, nil
}
func OptimismPortalWithdrawalFinalizedEvents(events *ProcessedContractEvents) ([]OptimismPortalWithdrawalFinalizedEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "WithdrawalFinalized"
processedWithdrawalFinalizedEvents := events.eventsBySignature[optimismPortalAbi.Events[eventName].ID]
finalizedEvents := make([]OptimismPortalWithdrawalFinalizedEvent, len(processedWithdrawalFinalizedEvents))
for i, finalizedEvent := range processedWithdrawalFinalizedEvents {
log := events.eventLog[finalizedEvent.GUID]
var withdrawalFinalized bindings.OptimismPortalWithdrawalFinalized
err := UnpackLog(&withdrawalFinalized, log, eventName, optimismPortalAbi)
if err != nil {
return nil, err
}
finalizedEvents[i] = OptimismPortalWithdrawalFinalizedEvent{&withdrawalFinalized, finalizedEvent}
}
return finalizedEvents, nil
}
func OptimismPortalQueryProvenWithdrawal(ethClient *ethclient.Client, portalAddress common.Address, withdrawalHash common.Hash) (OptimismPortalProvenWithdrawal, error) {
var provenWithdrawal OptimismPortalProvenWithdrawal
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/solc"
)
const StandardBridgeStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_0_0_20\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_address\"},{\"astId\":1001,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_1_0_20\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_address\"},{\"astId\":1002,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"deposits\",\"offset\":0,\"slot\":\"2\",\"type\":\"t_mapping(t_address,t_mapping(t_address,t_uint256))\"},{\"astId\":1003,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"3\",\"type\":\"t_array(t_uint256)47_storage\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)47_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[47]\",\"numberOfBytes\":\"1504\",\"base\":\"t_uint256\"},\"t_mapping(t_address,t_mapping(t_address,t_uint256))\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e mapping(address =\u003e uint256))\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_mapping(t_address,t_uint256)\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"}}}"
const StandardBridgeStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_0_2_20\",\"offset\":2,\"slot\":\"0\",\"type\":\"t_address\"},{\"astId\":1003,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_1_0_20\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"deposits\",\"offset\":0,\"slot\":\"2\",\"type\":\"t_mapping(t_address,t_mapping(t_address,t_uint256))\"},{\"astId\":1005,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"messenger\",\"offset\":0,\"slot\":\"3\",\"type\":\"t_contract(CrossDomainMessenger)1007\"},{\"astId\":1006,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"4\",\"type\":\"t_array(t_uint256)46_storage\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)46_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[46]\",\"numberOfBytes\":\"1472\",\"base\":\"t_uint256\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_contract(CrossDomainMessenger)1007\":{\"encoding\":\"inplace\",\"label\":\"contract CrossDomainMessenger\",\"numberOfBytes\":\"20\"},\"t_mapping(t_address,t_mapping(t_address,t_uint256))\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e mapping(address =\u003e uint256))\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_mapping(t_address,t_uint256)\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}"
var StandardBridgeStorageLayout = new(solc.StorageLayout)
......
......@@ -64,6 +64,15 @@ func (s *StorageLayout) GetStorageLayoutEntry(name string) (StorageLayoutEntry,
return StorageLayoutEntry{}, fmt.Errorf("%s not found", name)
}
// GetStorageLayoutType returns the StorageLayoutType where the label matches
// the provided name.
func (s *StorageLayout) GetStorageLayoutType(name string) (StorageLayoutType, error) {
if ty, ok := s.Types[name]; ok {
return ty, nil
}
return StorageLayoutType{}, fmt.Errorf("%s not found", name)
}
type StorageLayoutEntry struct {
AstId uint `json:"astId"`
Contract string `json:"contract"`
......
This diff is collapsed.
......@@ -470,6 +470,7 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) {
// L1Deployments represents a set of L1 contracts that are deployed.
type L1Deployments struct {
AddressManager common.Address `json:"AddressManager"`
BlockOracle common.Address `json:"BlockOracle"`
DisputeGameFactory common.Address `json:"DisputeGameFactory"`
DisputeGameFactoryProxy common.Address `json:"DisputeGameFactoryProxy"`
L1CrossDomainMessenger common.Address `json:"L1CrossDomainMessenger"`
......@@ -512,7 +513,7 @@ func (d *L1Deployments) Check() error {
for i := 0; i < val.NumField(); i++ {
name := val.Type().Field(i).Name
// Skip the non production ready contracts
if name == "DisputeGameFactory" || name == "DisputeGameFactoryProxy" {
if name == "DisputeGameFactory" || name == "DisputeGameFactoryProxy" || name == "BlockOracle" {
continue
}
if val.Field(i).Interface().(common.Address) == (common.Address{}) {
......@@ -655,6 +656,11 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
"xDomainMsgSender": "0x000000000000000000000000000000000000dEaD",
"msgNonce": 0,
}
storage["L2StandardBridge"] = state.StorageValues{
"_initialized": 2,
"_initializing": false,
"messenger": predeploys.L2CrossDomainMessengerAddr,
}
storage["L1Block"] = state.StorageValues{
"number": block.Number(),
"timestamp": block.Time(),
......@@ -684,6 +690,11 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
storage["ProxyAdmin"] = state.StorageValues{
"_owner": config.ProxyAdminOwner,
}
storage["L2ERC721Bridge"] = state.StorageValues{
"messenger": predeploys.L2CrossDomainMessengerAddr,
"_initialized": 2,
"_initializing": false,
}
return storage, nil
}
......
This diff is collapsed.
......@@ -52,7 +52,11 @@ func TestCode(t *testing.T) {
db.SetCode(addr, code)
post := db.GetCode(addr)
require.Equal(t, post, code)
if len(code) == 0 {
require.Nil(t, post)
} else {
require.Equal(t, post, code)
}
size := db.GetCodeSize(addr)
require.Equal(t, size, len(code))
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -13,7 +13,6 @@ import (
type localGameInputs struct {
l1Head common.Hash
l2ChainId *big.Int
l2Head common.Hash
l2OutputRoot common.Hash
l2Claim common.Hash
......@@ -39,10 +38,6 @@ func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameI
if err != nil {
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)
if err != nil {
......@@ -58,7 +53,6 @@ func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameI
return localGameInputs{
l1Head: l1Head,
l2ChainId: l2ChainId,
l2Head: l2Head,
l2OutputRoot: agreedOutput.OutputRoot,
l2Claim: claimedOutput.OutputRoot,
......
......@@ -40,7 +40,6 @@ func TestFetchLocalInputs(t *testing.T) {
require.NoError(t, err)
require.Equal(t, l1Client.l1Head, inputs.l1Head)
require.Equal(t, l2Client.chainID, inputs.l2ChainId)
require.Equal(t, l2Client.header.Hash(), inputs.l2Head)
require.EqualValues(t, l1Client.starting.OutputRoot, inputs.l2OutputRoot)
require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.l2Claim)
......
This diff is collapsed.
......@@ -64,7 +64,7 @@ func newTestCannonUpdater(t *testing.T, sendFails bool) (*cannonUpdater, *mockTx
from: mockFdgAddress,
sendFails: sendFails,
}
updater, err := NewOracleUpdater(logger, txMgr, mockFdgAddress, mockPreimageOracleAddress)
updater, err := NewOracleUpdaterWithOracle(logger, txMgr, mockFdgAddress, mockPreimageOracleAddress)
require.NoError(t, err)
return updater, txMgr
}
......
This diff is collapsed.
......@@ -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.
func (s *Solver) NextMove(ctx context.Context, claim types.Claim, agreeWithClaimLevel bool) (*types.Claim, error) {
if agreeWithClaimLevel {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -8,7 +8,6 @@ import (
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
......@@ -26,7 +25,7 @@ func SendDepositTx(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, l
applyL2Opts(l2Opts)
// Find deposit contract
depositContract, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client)
depositContract, err := bindings.NewOptimismPortal(cfg.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err)
// Finally send TX
......@@ -54,7 +53,7 @@ func SendDepositTx(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, l
tx = types.NewTx(reconstructedDep)
receipt, err = waitForTransaction(tx.Hash(), l2Client, 10*time.Duration(cfg.DeployConfig.L2BlockTime)*time.Second)
require.NoError(t, err)
require.Equal(t, l2Opts.ExpectedStatus, receipt.Status)
require.Equal(t, l2Opts.ExpectedStatus, receipt.Status, "l2 transaction status")
}
type DepositTxOptsFn func(l2Opts *DepositTxOpts)
......
......@@ -33,11 +33,8 @@ func setupL2OutputOracle() (common.Address, *bind.TransactOpts, *backends.Simula
backend,
big.NewInt(10),
big.NewInt(2),
big.NewInt(0),
big.NewInt(0),
from,
common.Address{0xdd},
big.NewInt(100))
big.NewInt(100),
)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -43,7 +43,7 @@
"eip1559Elasticity": 6,
"l1GenesisBlockTimestamp": "0x64c811bf",
"l2GenesisRegolithTimeOffset": "0x0",
"faultGameAbsolutePrestate": 96,
"faultGameAbsolutePrestate": "0x41c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98",
"faultGameMaxDepth": 4,
"faultGameMaxDuration": 120
"faultGameMaxDuration": 300
}
......@@ -94,9 +94,9 @@
}
},
"L1StandardBridge": {
"spacer_0_0_20": {
"spacer_0_2_20": {
"slot": 0,
"offset": 0,
"offset": 2,
"length": 20
},
"spacer_1_0_20": {
......
This diff is collapsed.
......@@ -14,18 +14,18 @@ import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol";
/// L2 on the L2 side. Users are generally encouraged to use this contract instead of lower
/// level message passing contracts.
contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
/// @custom:semver 1.4.1
/// @custom:semver 1.5.0
/// @notice Constructs the L2CrossDomainMessenger contract.
/// @param _l1CrossDomainMessenger Address of the L1CrossDomainMessenger contract.
constructor(address _l1CrossDomainMessenger)
Semver(1, 4, 1)
Semver(1, 5, 0)
CrossDomainMessenger(_l1CrossDomainMessenger)
{
initialize();
}
/// @notice Initializer.
function initialize() public initializer {
function initialize() public reinitializer(2) {
__CrossDomainMessenger_init();
}
......
......@@ -5,6 +5,7 @@ import { ERC721Bridge } from "../universal/ERC721Bridge.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { IOptimismMintableERC721 } from "../universal/IOptimismMintableERC721.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { Semver } from "../universal/Semver.sol";
/// @title L2ERC721Bridge
......@@ -17,14 +18,18 @@ import { Semver } from "../universal/Semver.sol";
/// wait for the one-week challenge period to elapse before their Optimism-native NFT
/// can be refunded on L2.
contract L2ERC721Bridge is ERC721Bridge, Semver {
/// @custom:semver 1.1.1
/// @custom:semver 1.2.0
/// @notice Constructs the L2ERC721Bridge contract.
/// @param _messenger Address of the CrossDomainMessenger on this network.
/// @param _otherBridge Address of the ERC721 bridge on the other network.
constructor(address _messenger, address _otherBridge)
Semver(1, 1, 1)
ERC721Bridge(_messenger, _otherBridge)
{}
constructor(address _otherBridge) Semver(1, 2, 0) ERC721Bridge(_otherBridge) {
initialize({ _messenger: CrossDomainMessenger(address(0)) });
}
/// @notice Initializes the contract.
/// @param _messenger Address of the CrossDomainMessenger on this network.
function initialize(CrossDomainMessenger _messenger) public reinitializer(2) {
__ERC721Bridge_init({ _messenger: _messenger });
}
/// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
/// recipient on this domain.
......@@ -109,7 +114,7 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
// Send message to L1 bridge
// slither-disable-next-line reentrancy-events
MESSENGER.sendMessage(OTHER_BRIDGE, message, _minGasLimit);
messenger.sendMessage(OTHER_BRIDGE, message, _minGasLimit);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeInitiated(_localToken, remoteToken, _from, _to, _tokenId, _extraData);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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