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

Merge branch 'develop' into aj/atomic-put

parents de92c9f8 c9b69597
...@@ -86,6 +86,9 @@ nuke: clean devnet-clean ...@@ -86,6 +86,9 @@ nuke: clean devnet-clean
.PHONY: nuke .PHONY: nuke
devnet-up: devnet-up:
@if [ ! -e op-program/bin ]; then \
make cannon-prestate; \
fi
$(shell ./ops/scripts/newer-file.sh .devnet/allocs-l1.json ./packages/contracts-bedrock) $(shell ./ops/scripts/newer-file.sh .devnet/allocs-l1.json ./packages/contracts-bedrock)
if [ $(.SHELLSTATUS) -ne 0 ]; then \ if [ $(.SHELLSTATUS) -ne 0 ]; then \
make devnet-allocs; \ make devnet-allocs; \
......
...@@ -33,3 +33,28 @@ TODO add indexer to the optimism devnet compose file (previously removed for bre ...@@ -33,3 +33,28 @@ TODO add indexer to the optimism devnet compose file (previously removed for bre
`docker-compose.dev.yml` is git ignored. Fill in your own docker-compose file here. `docker-compose.dev.yml` is git ignored. Fill in your own docker-compose file here.
## Architecture
![Architectural Diagram](./assets/architecture.png)
The indexer application supports two separate services for collective operation:
**Indexer API** - Provides a lightweight API service that supports paginated lookups for bridge events.
**Indexer Service** - A polling based service that constantly reads and persists OP Stack chain data (i.e, block meta, system contract events, synchronized bridge events) from a L1 and L2 chain.
### Indexer API
TBD
### Indexer Service
![Service Component Diagram](./assets/indexer-service.png)
The indexer service is responsible for polling and processing real-time batches of L1 and L2 chain data. The indexer service is currently composed of the following key components:
- **Poller Routines** - Individually polls the L1/L2 chain for new blocks and OP Stack system contract events.
- **Insertion Routines** - Awaits new batches from the poller routines and inserts them into the database upon retrieval.
- **Bridge Routine** - Polls the database directly for new L1 blocks and bridge events. Upon retrieval, the bridge routine will:
* Process and persist new bridge events
* Synchronize L1 proven/finalized withdrawals with their L2 initialization counterparts
### Database
The indexer service currently supports a Postgres database for storing L1/L2 OP Stack chain data. The most up-to-date database schemas can be found in the `./migrations` directory.
**NOTE:** The indexer service implementation currently does not natively support database migration. Because of this a database must be manually updated to ensure forward compatibility with the latest indexer service implementation.
\ No newline at end of file
...@@ -2,6 +2,7 @@ package database ...@@ -2,6 +2,7 @@ package database
import ( import (
"errors" "errors"
"fmt"
"gorm.io/gorm" "gorm.io/gorm"
...@@ -133,21 +134,31 @@ func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, c ...@@ -133,21 +134,31 @@ func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, c
limit = defaultLimit limit = defaultLimit
} }
cursorClause := ""
if cursor != "" {
sourceHash := common.HexToHash(cursor)
txDeposit := new(L1TransactionDeposit)
result := db.gorm.Model(&L1TransactionDeposit{}).Where(&L1TransactionDeposit{SourceHash: sourceHash}).Take(txDeposit)
if result.Error != nil || errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("unable to find transaction with supplied cursor source hash %s: %w", sourceHash, result.Error)
}
cursorClause = fmt.Sprintf("l1_transaction_deposits.timestamp <= %d", txDeposit.Tx.Timestamp)
}
// TODO join with l1_bridged_tokens and l2_bridged_tokens // TODO join with l1_bridged_tokens and l2_bridged_tokens
ethAddressString := predeploys.LegacyERC20ETHAddr.String() ethAddressString := predeploys.LegacyERC20ETHAddr.String()
// Coalesce l1 transaction deposits that are simply ETH sends // Coalesce l1 transaction deposits that are simply ETH sends
ethTransactionDeposits := db.gorm.Model(&L1TransactionDeposit{}) ethTransactionDeposits := db.gorm.Model(&L1TransactionDeposit{})
ethTransactionDeposits = ethTransactionDeposits.Where(Transaction{FromAddress: address}).Where(`data = '0x' AND amount > 0`) ethTransactionDeposits = ethTransactionDeposits.Where(Transaction{FromAddress: address}).Where("data = '0x' AND amount > 0")
ethTransactionDeposits = ethTransactionDeposits.Joins("INNER JOIN l1_contract_events ON l1_contract_events.guid = initiated_l1_event_guid") ethTransactionDeposits = ethTransactionDeposits.Joins("INNER JOIN l1_contract_events ON l1_contract_events.guid = initiated_l1_event_guid")
ethTransactionDeposits = ethTransactionDeposits.Select(` ethTransactionDeposits = ethTransactionDeposits.Select(`
from_address, to_address, amount, data, source_hash AS transaction_source_hash, from_address, to_address, amount, data, source_hash AS transaction_source_hash,
l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash, l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash,
l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString) l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString)
ethTransactionDeposits = ethTransactionDeposits.Order("timestamp DESC").Limit(limit + 1)
if cursor != "" { if cursorClause != "" {
// Probably need to fix this and compare timestamps ethTransactionDeposits = ethTransactionDeposits.Where(cursorClause)
ethTransactionDeposits = ethTransactionDeposits.Where("source_hash < ?", cursor)
} }
depositsQuery := db.gorm.Model(&L1BridgeDeposit{}) depositsQuery := db.gorm.Model(&L1BridgeDeposit{})
...@@ -157,10 +168,9 @@ l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local ...@@ -157,10 +168,9 @@ l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local
l1_bridge_deposits.from_address, l1_bridge_deposits.to_address, l1_bridge_deposits.amount, l1_bridge_deposits.data, transaction_source_hash, l1_bridge_deposits.from_address, l1_bridge_deposits.to_address, l1_bridge_deposits.amount, l1_bridge_deposits.data, transaction_source_hash,
l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash, l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash,
l1_bridge_deposits.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`) l1_bridge_deposits.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`)
depositsQuery = depositsQuery.Order("timestamp DESC").Limit(limit + 1)
if cursor != "" { if cursorClause != "" {
// Probably need to fix this and compare timestamps depositsQuery = depositsQuery.Where(cursorClause)
depositsQuery = depositsQuery.Where("source_hash < ?", cursor)
} }
query := db.gorm.Table("(?) AS deposits", depositsQuery) query := db.gorm.Table("(?) AS deposits", depositsQuery)
...@@ -179,16 +189,11 @@ l1_bridge_deposits.timestamp, cross_domain_message_hash, local_token_address, re ...@@ -179,16 +189,11 @@ l1_bridge_deposits.timestamp, cross_domain_message_hash, local_token_address, re
hasNextPage := false hasNextPage := false
if len(deposits) > limit { if len(deposits) > limit {
hasNextPage = true hasNextPage = true
nextCursor = deposits[limit].L1BridgeDeposit.TransactionSourceHash.String()
deposits = deposits[:limit] deposits = deposits[:limit]
nextCursor = deposits[limit].L1TransactionHash.String()
}
response := &L1BridgeDepositsResponse{
Deposits: deposits,
Cursor: nextCursor,
HasNextPage: hasNextPage,
} }
response := &L1BridgeDepositsResponse{Deposits: deposits, Cursor: nextCursor, HasNextPage: hasNextPage}
return response, nil return response, nil
} }
...@@ -242,6 +247,17 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address ...@@ -242,6 +247,17 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address
limit = defaultLimit limit = defaultLimit
} }
cursorClause := ""
if cursor != "" {
withdrawalHash := common.HexToHash(cursor)
var txWithdrawal L2TransactionWithdrawal
result := db.gorm.Model(&L2TransactionWithdrawal{}).Where(&L2TransactionWithdrawal{WithdrawalHash: withdrawalHash}).Take(&txWithdrawal)
if result.Error != nil || errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("unable to find transaction with supplied cursor withdrawal hash %s: %w", withdrawalHash, result.Error)
}
cursorClause = fmt.Sprintf("l2_transaction_withdrawals.timestamp <= %d", txWithdrawal.Tx.Timestamp)
}
// TODO join with l1_bridged_tokens and l2_bridged_tokens // TODO join with l1_bridged_tokens and l2_bridged_tokens
ethAddressString := predeploys.LegacyERC20ETHAddr.String() ethAddressString := predeploys.LegacyERC20ETHAddr.String()
...@@ -255,10 +271,9 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address ...@@ -255,10 +271,9 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address
from_address, to_address, amount, data, withdrawal_hash AS transaction_withdrawal_hash, from_address, to_address, amount, data, withdrawal_hash AS transaction_withdrawal_hash,
l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash, l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash,
l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString) l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString)
ethTransactionWithdrawals = ethTransactionWithdrawals.Order("timestamp DESC").Limit(limit + 1)
if cursor != "" { if cursorClause != "" {
// Probably need to fix this and compare timestamps ethTransactionWithdrawals = ethTransactionWithdrawals.Where(cursorClause)
ethTransactionWithdrawals = ethTransactionWithdrawals.Where("withdrawal_hash < ?", cursor)
} }
withdrawalsQuery := db.gorm.Model(&L2BridgeWithdrawal{}) withdrawalsQuery := db.gorm.Model(&L2BridgeWithdrawal{})
...@@ -270,17 +285,16 @@ l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS lo ...@@ -270,17 +285,16 @@ l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS lo
l2_bridge_withdrawals.from_address, l2_bridge_withdrawals.to_address, l2_bridge_withdrawals.amount, l2_bridge_withdrawals.data, transaction_withdrawal_hash, l2_bridge_withdrawals.from_address, l2_bridge_withdrawals.to_address, l2_bridge_withdrawals.amount, l2_bridge_withdrawals.data, transaction_withdrawal_hash,
l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash, l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash,
l2_bridge_withdrawals.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`) l2_bridge_withdrawals.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`)
withdrawalsQuery = withdrawalsQuery.Order("timestamp DESC").Limit(limit + 1)
if cursor != "" { if cursorClause != "" {
// Probably need to fix this and compare timestamps withdrawalsQuery = withdrawalsQuery.Where(cursorClause)
withdrawalsQuery = withdrawalsQuery.Where("withdrawal_hash < ?", cursor)
} }
query := db.gorm.Table("(?) AS withdrawals", withdrawalsQuery) query := db.gorm.Table("(?) AS withdrawals", withdrawalsQuery)
query = query.Joins("UNION (?)", ethTransactionWithdrawals) query = query.Joins("UNION (?)", ethTransactionWithdrawals)
query = query.Select("*").Order("timestamp DESC").Limit(limit + 1) query = query.Select("*").Order("timestamp DESC").Limit(limit + 1)
withdrawals := []L2BridgeWithdrawalWithTransactionHashes{} withdrawals := []L2BridgeWithdrawalWithTransactionHashes{}
result := query.Scan(&withdrawals) result := query.Find(&withdrawals)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
...@@ -292,21 +306,10 @@ l2_bridge_withdrawals.timestamp, cross_domain_message_hash, local_token_address, ...@@ -292,21 +306,10 @@ l2_bridge_withdrawals.timestamp, cross_domain_message_hash, local_token_address,
hasNextPage := false hasNextPage := false
if len(withdrawals) > limit { if len(withdrawals) > limit {
hasNextPage = true hasNextPage = true
nextCursor = withdrawals[limit].L2BridgeWithdrawal.TransactionWithdrawalHash.String()
withdrawals = withdrawals[:limit] withdrawals = withdrawals[:limit]
nextCursor = withdrawals[limit].L2TransactionHash.String()
}
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
response := &L2BridgeWithdrawalsResponse{
Withdrawals: withdrawals,
Cursor: nextCursor,
HasNextPage: hasNextPage,
} }
response := &L2BridgeWithdrawalsResponse{Withdrawals: withdrawals, Cursor: nextCursor, HasNextPage: hasNextPage}
return response, nil return response, nil
} }
...@@ -2,12 +2,14 @@ package e2e_tests ...@@ -2,12 +2,14 @@ package e2e_tests
import ( import (
"context" "context"
"fmt"
"math/big" "math/big"
"testing" "testing"
"time" "time"
e2etest_utils "github.com/ethereum-optimism/optimism/indexer/e2e_tests/utils" e2etest_utils "github.com/ethereum-optimism/optimism/indexer/e2e_tests/utils"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/withdrawals" "github.com/ethereum-optimism/optimism/op-node/withdrawals"
...@@ -86,118 +88,6 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) { ...@@ -86,118 +88,6 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) {
require.NotNil(t, crossDomainBridgeMessage.RelayedMessageEventGUID) require.NotNil(t, crossDomainBridgeMessage.RelayedMessageEventGUID)
} }
/*
TODO make this test work:
Error Trace: /root/project/indexer/e2e_tests/bridge_transfers_e2e_test.go:116
Error: Received unexpected error:
expected status 1, but got 0
tx trace unavailable: websocket: read limit exceeded
Test: TestE2EBridgeTransfersPagination
func TestE2EBridgeTransfersPagination(t *testing.T) {
testSuite := createE2ETestSuite(t)
l1StandardBridge, err := bindings.NewL1StandardBridge(testSuite.OpCfg.L1Deployments.L1StandardBridgeProxy, testSuite.L1Client)
require.NoError(t, err)
// 1 ETH transfer
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
// (1) Test Deposit Initiation
var deposits []struct {
Tx *types.Transaction
Receipt *types.Receipt
Info *e2etest_utils.DepositInfo
}
for i := 0; i < 3; i++ {
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
l1Opts.Value = big.NewInt(params.Ether)
depositTx, err := l1StandardBridge.DepositETH(l1Opts, 200_000, []byte{byte(i)})
require.NoError(t, err)
depositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L1Client, depositTx.Hash())
require.NoError(t, err)
depositInfo, err := e2etest_utils.ParseDepositInfo(depositReceipt)
require.NoError(t, err)
// wait for processor catchup
err = wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil
})
require.NoError(t, err)
deposits = append(deposits, struct {
Tx *types.Transaction
Receipt *types.Receipt
Info *e2etest_utils.DepositInfo
}{
Tx: depositTx,
Receipt: depositReceipt,
Info: depositInfo,
})
// wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader()
return l1Header != nil && l1Header.Number.Uint64() >= deposits[i].Receipt.BlockNumber.Uint64(), nil
}))
}
// Test no cursor or limit
cursor := ""
limit := 0
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, cursor, limit)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 3)
require.Equal(t, deposits[0].Tx.Hash(), aliceDeposits.Deposits[0].L1TransactionHash)
require.Equal(t, deposits[1].Tx.Hash(), aliceDeposits.Deposits[1].L1TransactionHash)
require.Equal(t, deposits[2].Tx.Hash(), aliceDeposits.Deposits[2].L1TransactionHash)
require.Equal(t, "", aliceDeposits.Cursor)
require.Equal(t, false, aliceDeposits.HasNextPage)
// test cursor with no limit
cursor = deposits[1].Tx.Hash().String()
limit = 0
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, cursor, limit)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 2)
require.Equal(t, deposits[1].Tx.Hash().String(), aliceDeposits.Deposits[0].L1TransactionHash)
require.Equal(t, deposits[2].Tx.Hash().String(), aliceDeposits.Deposits[1].L1TransactionHash)
require.Equal(t, "", aliceDeposits.Cursor)
require.Equal(t, false, aliceDeposits.HasNextPage)
// test no cursor with limit and hasNext page is true
cursor = ""
limit = 2
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, cursor, limit)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, limit)
require.Equal(t, deposits[0].Tx.Hash().String(), aliceDeposits.Deposits[0].L1TransactionHash)
require.Equal(t, deposits[1].Tx.Hash().String(), aliceDeposits.Deposits[1].L1TransactionHash)
require.Equal(t, deposits[2].Tx.Hash().String(), aliceDeposits.Cursor)
require.Equal(t, true, aliceDeposits.HasNextPage)
// test cursor with limit and hasNext page is true
cursor = deposits[1].Tx.Hash().String()
limit = 1
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, cursor, limit)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 1)
require.Equal(t, deposits[1].Tx.Hash().String(), aliceDeposits.Deposits[1].L1TransactionHash)
require.Equal(t, deposits[2].Tx.Hash().String(), aliceDeposits.Cursor)
require.Equal(t, true, aliceDeposits.HasNextPage)
// limit bigger than the total amount
cursor = ""
limit = 10
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, cursor, limit)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 3)
}
*/
func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) { func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {
testSuite := createE2ETestSuite(t) testSuite := createE2ETestSuite(t)
...@@ -257,6 +147,75 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) { ...@@ -257,6 +147,75 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {
require.Nil(t, aliceDeposits.Deposits[0].L1BridgeDeposit.CrossDomainMessageHash) require.Nil(t, aliceDeposits.Deposits[0].L1BridgeDeposit.CrossDomainMessageHash)
} }
func TestE2EBridgeTransfersCursoredDeposits(t *testing.T) {
testSuite := createE2ETestSuite(t)
l1StandardBridge, err := bindings.NewL1StandardBridge(testSuite.OpCfg.L1Deployments.L1StandardBridgeProxy, testSuite.L1Client)
require.NoError(t, err)
optimismPortal, err := bindings.NewOptimismPortal(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client)
require.NoError(t, err)
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
l1Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L1ChainIDBig())
require.NoError(t, err)
// Deposit 1/2/3 ETH (second deposit via the optimism portal)
var depositReceipts [3]*types.Receipt
for i := 0; i < 3; i++ {
var depositTx *types.Transaction
l1Opts.Value = big.NewInt(int64((i + 1)) * params.Ether)
if i != 1 {
depositTx, err = transactions.PadGasEstimate(l1Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { return l1StandardBridge.Receive(opts) })
require.NoError(t, err)
} else {
depositTx, err = transactions.PadGasEstimate(l1Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { return optimismPortal.Receive(opts) })
require.NoError(t, err)
}
depositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L1Client, depositTx.Hash())
require.NoError(t, err, fmt.Sprintf("failed on deposit %d", i))
depositReceipts[i] = depositReceipt
}
// wait for processor catchup of the latest tx
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= depositReceipts[2].BlockNumber.Uint64(), nil
}))
// Get All
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 0)
require.NotNil(t, aliceDeposits)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 3)
require.False(t, aliceDeposits.HasNextPage)
// Respects Limits & Supplied Cursors
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 2)
require.NotNil(t, aliceDeposits)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 2)
require.True(t, aliceDeposits.HasNextPage)
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, aliceDeposits.Cursor, 2)
require.NoError(t, err)
require.NotNil(t, aliceDeposits)
require.Len(t, aliceDeposits.Deposits, 1)
require.False(t, aliceDeposits.HasNextPage)
// Returns the results in the right order
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 100)
require.NotNil(t, aliceDeposits)
require.NoError(t, err)
for i := 0; i < 3; i++ {
deposit := aliceDeposits.Deposits[i]
// DESCENDING order
require.Equal(t, depositReceipts[2-i].TxHash, deposit.L1TransactionHash)
require.Equal(t, int64(3-i)*params.Ether, deposit.L1BridgeDeposit.Tx.Amount.Int64())
}
}
func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) { func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) {
testSuite := createE2ETestSuite(t) testSuite := createE2ETestSuite(t)
...@@ -415,90 +374,72 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserETHReceive(t *testing.T) { ...@@ -415,90 +374,72 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserETHReceive(t *testing.T) {
require.Equal(t, finalizeReceipt.TxHash, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash) require.Equal(t, finalizeReceipt.TxHash, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash)
} }
/** func TestE2EBridgeTransfersCursoredWithdrawals(t *testing.T) {
THIS test will work after we order transactions correctly
func TestE2EBridgeTransfersPaginationWithdrawals(t *testing.T) {
testSuite := createE2ETestSuite(t) testSuite := createE2ETestSuite(t)
l2StandardBridge, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, testSuite.L2Client) l2StandardBridge, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, testSuite.L2Client)
require.NoError(t, err) require.NoError(t, err)
l2ToL1MP, err := bindings.NewOptimismPortal(predeploys.L2ToL1MessagePasserAddr, testSuite.L2Client)
require.NoError(t, err)
// 1 ETH transfer
aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice aliceAddr := testSuite.OpCfg.Secrets.Addresses().Alice
l2Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L2ChainIDBig()) l2Opts, err := bind.NewKeyedTransactorWithChainID(testSuite.OpCfg.Secrets.Alice, testSuite.OpCfg.L2ChainIDBig())
require.NoError(t, err) require.NoError(t, err)
l2Opts.Value = big.NewInt(params.Ether)
var withdrawals []struct {
Tx *types.Transaction
Receipt *types.Receipt
}
// Withdraw 1/2/3 ETH (second deposit via the l2ToL1MP). We dont ever finalize these withdrawals on
// L1 so we dont have to worry about funding the OptimismPortal contract with ETH
var withdrawReceipts [3]*types.Receipt
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
withdrawTx, err := l2StandardBridge.Withdraw(l2Opts, predeploys.LegacyERC20ETHAddr, l2Opts.Value, 200_000, []byte{byte(i)}) var withdrawTx *types.Transaction
require.NoError(t, err) l2Opts.Value = big.NewInt(int64((i + 1)) * params.Ether)
if i != 1 {
withdrawTx, err = transactions.PadGasEstimate(l2Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { return l2StandardBridge.Receive(opts) })
require.NoError(t, err)
} else {
withdrawTx, err = transactions.PadGasEstimate(l2Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { return l2ToL1MP.Receive(opts) })
require.NoError(t, err)
}
withdrawReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, withdrawTx.Hash()) withdrawReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, withdrawTx.Hash())
require.NoError(t, err) require.NoError(t, err, fmt.Sprintf("failed on withdrawal %d", i))
withdrawReceipts[i] = withdrawReceipt
err = wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader()
return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipt.BlockNumber.Uint64(), nil
})
require.NoError(t, err)
withdrawals = append(withdrawals, struct {
Tx *types.Transaction
Receipt *types.Receipt
}{
Tx: withdrawTx,
Receipt: withdrawReceipt,
})
} }
cursor := "" // wait for processor catchup of the latest tx
limit := 0 require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
aliceWithdrawals, err := testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, cursor, limit) l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
require.NoError(t, err) return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipts[2].BlockNumber.Uint64(), nil
require.Len(t, aliceWithdrawals.Withdrawals, 3) }))
require.Equal(t, withdrawals[0].Tx.Hash().String(), aliceWithdrawals.Withdrawals[0].L2TransactionHash.String())
require.Equal(t, withdrawals[1].Tx.Hash().String(), aliceWithdrawals.Withdrawals[1].L2TransactionHash.String())
require.Equal(t, withdrawals[2].Tx.Hash().String(), aliceWithdrawals.Withdrawals[2].L2TransactionHash.String())
cursor = withdrawals[1].Tx.Hash().String() // Get All
limit = 0 aliceWithdrawals, err := testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, "", 0)
aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, cursor, limit) require.NotNil(t, aliceWithdrawals)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, aliceWithdrawals, 2) require.Len(t, aliceWithdrawals.Withdrawals, 3)
require.Equal(t, withdrawals[1].Tx.Hash().String(), aliceWithdrawals.Withdrawals[0].L2TransactionHash.String()) require.False(t, aliceWithdrawals.HasNextPage)
require.Equal(t, withdrawals[2].Tx.Hash().String(), aliceWithdrawals.Withdrawals[1].L2TransactionHash.String())
cursor = "" // Respects Limits & Supplied Cursors
limit = 2 aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, "", 2)
aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, cursor, limit) require.NotNil(t, aliceWithdrawals)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, aliceWithdrawals, limit) require.Len(t, aliceWithdrawals.Withdrawals, 2)
require.Equal(t, withdrawals[0].Tx.Hash().String(), aliceWithdrawals.Withdrawals[0].L2TransactionHash.String()) require.True(t, aliceWithdrawals.HasNextPage)
require.Equal(t, withdrawals[1].Tx.Hash().String(), aliceWithdrawals.Withdrawals[1].L2TransactionHash.String())
cursor = withdrawals[1].Tx.Hash().String() aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, aliceWithdrawals.Cursor, 2)
limit = 1 require.NotNil(t, aliceWithdrawals)
aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, cursor, limit)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, aliceWithdrawals, 1) require.Len(t, aliceWithdrawals.Withdrawals, 1)
require.Equal(t, withdrawals[1].Tx.Hash().String(), aliceWithdrawals.Withdrawals[0].L2TransactionHash.String()) require.False(t, aliceWithdrawals.HasNextPage)
require.Equal(t, true, aliceWithdrawals.HasNextPage)
require.Equal(t, withdrawals[2].Tx.Hash().String(), aliceWithdrawals.Cursor)
cursor = "" // Returns the results in the right order
limit = 10 aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, "", 100)
aliceWithdrawals, err = testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, cursor, limit) require.NotNil(t, aliceWithdrawals)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, proveReceipt.TxHash, aliceWithdrawals.Withdrawals[0].ProvenL1TransactionHash) for i := 0; i < 3; i++ {
require.Equal(t, finalizeReceipt.TxHash, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash) withdrawal := aliceWithdrawals.Withdrawals[i]
// Still nil as the withdrawal did not occur through the standard bridge // DESCENDING order
require.Nil(t, aliceWithdrawals.Withdrawals[0].L2BridgeWithdrawal.CrossDomainMessageHash) require.Equal(t, withdrawReceipts[2-i].TxHash, withdrawal.L2TransactionHash)
require.Equal(t, int64(3-i)*params.Ether, withdrawal.L2BridgeWithdrawal.Tx.Amount.Int64())
}
} }
*/
...@@ -43,9 +43,9 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -43,9 +43,9 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
dbUser := os.Getenv("DB_USER") dbUser := os.Getenv("DB_USER")
dbName := setupTestDatabase(t) dbName := setupTestDatabase(t)
// Replace the handler of the global logger with the testlog // Discard the Global Logger as each component
logger := testlog.Logger(t, log.LvlInfo) // has its own configured logger
log.Root().SetHandler(logger.GetHandler()) log.Root().SetHandler(log.DiscardHandler())
// Rollup System Configuration and Start // Rollup System Configuration and Start
opCfg := op_e2e.DefaultSystemConfig(t) opCfg := op_e2e.DefaultSystemConfig(t)
...@@ -90,7 +90,8 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -90,7 +90,8 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { db.Close() }) t.Cleanup(func() { db.Close() })
indexer, err := indexer.NewIndexer(logger, db, indexerCfg.Chain, indexerCfg.RPCs, indexerCfg.Metrics) indexerLog := testlog.Logger(t, log.LvlInfo).New("role", "indexer")
indexer, err := indexer.NewIndexer(indexerLog, db, indexerCfg.Chain, indexerCfg.RPCs, indexerCfg.Metrics)
require.NoError(t, err) require.NoError(t, err)
indexerCtx, indexerStop := context.WithCancel(context.Background()) indexerCtx, indexerStop := context.WithCancel(context.Background())
......
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