Commit 317914c0 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #2305 from tuxcanfly/tux/indexer-tests

indexer: add pagination metadata for deposit, withdrawal endpoints
parents 028ed8ee dd8b0071
......@@ -26,7 +26,7 @@ test:
lint:
golangci-lint run ./...
bindings: bindings-l1bridge bindings-l2bridge bindings-l1erc20 bindings-l2erc20 bindings-scc
bindings: bindings-l1bridge bindings-l2bridge bindings-l1erc20 bindings-l2erc20 bindings-scc bindings-address-manager
bindings-l1bridge:
$(eval temp := $(shell mktemp))
......@@ -130,6 +130,7 @@ bindings-address-manager:
bindings-l1erc20 \
bindings-l2erc20 \
bindings-scc \
bindings-address-manager
clean \
test \
lint
......@@ -96,6 +96,12 @@ type Config struct {
// batch.
MaxHeaderBatchSize uint64
// RESTHostname is the hostname at which the REST server is running.
RESTHostname string
// RESTPort is the port at which the REST server is running.
RESTPort uint64
// MetricsServerEnable if true, will create a metrics client and log to
// Prometheus.
MetricsServerEnable bool
......@@ -118,8 +124,8 @@ func NewConfig(ctx *cli.Context) (Config, error) {
BuildEnv: ctx.GlobalString(flags.BuildEnvFlag.Name),
EthNetworkName: ctx.GlobalString(flags.EthNetworkNameFlag.Name),
ChainID: ctx.GlobalInt64(flags.ChainIDFlag.Name),
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name),
L2EthRpc: ctx.GlobalString(flags.L2EthRpcFlag.Name),
L1EthRpc: ctx.GlobalString(flags.L1EthRPCFlag.Name),
L2EthRpc: ctx.GlobalString(flags.L2EthRPCFlag.Name),
L1AddressManagerAddress: ctx.GlobalString(flags.L1AddressManagerAddressFlag.Name),
L2GenesisBlockHash: ctx.GlobalString(flags.L2GenesisBlockHashFlag.Name),
DBHost: ctx.GlobalString(flags.DBHostFlag.Name),
......@@ -139,6 +145,8 @@ func NewConfig(ctx *cli.Context) (Config, error) {
ConfDepth: ctx.GlobalUint64(flags.ConfDepthFlag.Name),
MaxHeaderBatchSize: ctx.GlobalUint64(flags.MaxHeaderBatchSizeFlag.Name),
MetricsServerEnable: ctx.GlobalBool(flags.MetricsServerEnableFlag.Name),
RESTHostname: ctx.GlobalString(flags.RESTHostnameFlag.Name),
RESTPort: ctx.GlobalUint64(flags.RESTPortFlag.Name),
MetricsHostname: ctx.GlobalString(flags.MetricsHostnameFlag.Name),
MetricsPort: ctx.GlobalUint64(flags.MetricsPortFlag.Name),
}
......
......@@ -6,13 +6,15 @@ import (
"testing"
indexer "github.com/ethereum-optimism/optimism/go/indexer"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
// TestParseAddress asserts that ParseAddress correctly parses 40-characater
// hexidecimal strings with optional 0x prefix into valid 20-byte addresses.
func TestParseAddress(t *testing.T) {
// TestParseL1Address asserts that ParseL1Address correctly parses
// 40-characater hexidecimal strings with optional 0x prefix into valid 20-byte
// addresses for the L1 chain.
func TestParseL1Address(t *testing.T) {
tests := []struct {
name string
addr string
......@@ -44,7 +46,52 @@ func TestParseAddress(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
addr, err := indexer.ParseAddress(test.addr)
addr, err := indexer.ParseL1Address(test.addr)
require.Equal(t, err, test.expErr)
if test.expErr != nil {
return
}
require.Equal(t, addr, test.expAddr)
})
}
}
// TestParseL2Address asserts that ParseL2Address correctly parses
// 40-characater hexidecimal strings with optional 0x prefix into valid 20-byte
// addresses for the L2 chain.
func TestParseL2Address(t *testing.T) {
tests := []struct {
name string
addr string
expErr error
expAddr l2common.Address
}{
{
name: "empty address",
addr: "",
expErr: errors.New("invalid address: "),
},
{
name: "only 0x",
addr: "0x",
expErr: errors.New("invalid address: 0x"),
},
{
name: "non hex character",
addr: "0xaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
expErr: errors.New("invalid address: 0xaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
},
{
name: "valid address",
addr: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
expErr: nil,
expAddr: l2common.BytesToAddress(bytes.Repeat([]byte{170}, 20)),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
addr, err := indexer.ParseL2Address(test.addr)
require.Equal(t, err, test.expErr)
if test.expErr != nil {
return
......
This diff is collapsed.
package db
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// Deposit contains transaction data for deposits made via the L1 to L2 bridge.
type Deposit struct {
GUID string
TxHash common.Hash
L1Token common.Address
L2Token common.Address
FromAddress common.Address
ToAddress common.Address
Amount *big.Int
Data []byte
LogIndex uint
}
// String returns the tx hash for the deposit.
func (d Deposit) String() string {
return d.TxHash.String()
}
// DepositJSON contains Deposit data suitable for JSON serialization.
type DepositJSON struct {
GUID string `json:"guid"`
FromAddress string `json:"from"`
ToAddress string `json:"to"`
L1Token *Token `json:"l1Token"`
L2Token string `json:"l2Token"`
Amount string `json:"amount"`
Data []byte `json:"data"`
LogIndex uint64 `json:"logIndex"`
BlockNumber uint64 `json:"blockNumber"`
BlockTimestamp string `json:"blockTimestamp"`
TxHash string `json:"transactionHash"`
}
package db
import l2common "github.com/ethereum-optimism/optimism/l2geth/common"
// ETHL1Token is a placeholder token for differentiating ETH transactions from
// ERC20 transactions on L1.
var ETHL1Token = &Token{
Address: "0x0000000000000000000000000000000000000000",
Name: "Ethereum",
Symbol: "ETH",
Decimals: 18,
}
// ETHL2Address is a placeholder address for differentiating ETH transactions
// from ERC20 transactions on L2.
var ETHL2Address = l2common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
// ETHL2Token is a placeholder token for differentiating ETH transactions from
// ERC20 transactions on L2.
var ETHL2Token = &Token{
Address: "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
Name: "Ethereum",
Symbol: "ETH",
Decimals: 18,
}
package db
import "github.com/google/uuid"
// NewGUID returns a new guid.
func NewGUID() string {
return uuid.New().String()
}
package db
import (
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common"
)
// IndexedL1Block contains the L1 block including the deposits in it.
type IndexedL1Block struct {
Hash common.Hash
ParentHash common.Hash
Number uint64
Timestamp uint64
Deposits []Deposit
}
// String returns the block hash for the indexed l1 block.
func (b IndexedL1Block) String() string {
return b.Hash.String()
}
// IndexedL2Block contains the L2 block including the withdrawals in it.
type IndexedL2Block struct {
Hash l2common.Hash
ParentHash l2common.Hash
Number uint64
Timestamp uint64
Withdrawals []Withdrawal
}
// String returns the block hash for the indexed l2 block.
func (b IndexedL2Block) String() string {
return b.Hash.String()
}
package db
import (
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common"
)
// L1BlockLocator contains the block locator for a L1 block.
type L1BlockLocator struct {
Number uint64 `json:"number"`
Hash common.Hash `json:"hash"`
}
// L2BlockLocator contains the block locator for a L2 block.
type L2BlockLocator struct {
Number uint64 `json:"number"`
Hash l2common.Hash `json:"hash"`
}
package db
// PaginationParam holds the pagination fields passed through by the REST
// middleware and queried by the database to page through deposits and
// withdrawals.
type PaginationParam struct {
Limit uint64 `json:"limit"`
Offset uint64 `json:"offset"`
Total uint64 `json:"total"`
}
type PaginatedDeposits struct {
Param *PaginationParam `json:"pagination"`
Deposits []DepositJSON `json:"items"`
}
type PaginatedWithdrawals struct {
Param *PaginationParam `json:"pagination"`
Withdrawals []WithdrawalJSON `json:"items"`
}
package db
const createL1BlocksTable = `
CREATE TABLE IF NOT EXISTS l1_blocks (
hash VARCHAR NOT NULL PRIMARY KEY,
parent_hash VARCHAR NOT NULL,
number INTEGER NOT NULL,
timestamp INTEGER NOT NULL
)
`
const createL2BlocksTable = `
CREATE TABLE IF NOT EXISTS l2_blocks (
hash VARCHAR NOT NULL PRIMARY KEY,
parent_hash VARCHAR NOT NULL,
number INTEGER NOT NULL,
timestamp INTEGER NOT NULL
)
`
const createDepositsTable = `
CREATE TABLE IF NOT EXISTS deposits (
guid VARCHAR PRIMARY KEY NOT NULL,
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
l1_token VARCHAR NOT NULL REFERENCES l1_tokens(address),
l2_token VARCHAR NOT NULL,
amount VARCHAR NOT NULL,
data BYTEA NOT NULL,
log_index INTEGER NOT NULL,
block_hash VARCHAR NOT NULL REFERENCES l1_blocks(hash),
tx_hash VARCHAR NOT NULL
)
`
const createL1TokensTable = `
CREATE TABLE IF NOT EXISTS l1_tokens (
address VARCHAR NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
symbol VARCHAR NOT NULL,
decimals INTEGER NOT NULL
)
`
const createL2TokensTable = `
CREATE TABLE IF NOT EXISTS l2_tokens (
address TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
decimals INTEGER NOT NULL
)
`
const createStateBatchesTable = `
CREATE TABLE IF NOT EXISTS state_batches (
index INTEGER NOT NULL PRIMARY KEY,
root VARCHAR NOT NULL,
size INTEGER NOT NULL,
prev_total INTEGER NOT NULL,
extra_data BYTEA NOT NULL,
block_hash VARCHAR NOT NULL REFERENCES l1_blocks(hash)
);
CREATE INDEX IF NOT EXISTS state_batches_block_hash ON state_batches(block_hash);
CREATE INDEX IF NOT EXISTS state_batches_size ON state_batches(size);
CREATE INDEX IF NOT EXISTS state_batches_prev_total ON state_batches(prev_total);
`
const createWithdrawalsTable = `
CREATE TABLE IF NOT EXISTS withdrawals (
guid VARCHAR PRIMARY KEY NOT NULL,
from_address VARCHAR NOT NULL,
to_address VARCHAR NOT NULL,
l1_token VARCHAR NOT NULL,
l2_token VARCHAR NOT NULL REFERENCES l2_tokens(address),
amount VARCHAR NOT NULL,
data BYTEA NOT NULL,
log_index INTEGER NOT NULL,
block_hash VARCHAR NOT NULL REFERENCES l2_blocks(hash),
tx_hash VARCHAR NOT NULL,
state_batch INTEGER REFERENCES state_batches(index)
)
`
const insertETHL1Token = `
INSERT INTO l1_tokens
(address, name, symbol, decimals)
VALUES ('0x0000000000000000000000000000000000000000', 'Ethereum', 'ETH', 18)
ON CONFLICT (address) DO NOTHING;
`
// earlier transactions used 0x0000000000000000000000000000000000000000 as
// address of ETH so insert both that and
// 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000
const insertETHL2Token = `
INSERT INTO l2_tokens
(address, name, symbol, decimals)
VALUES ('0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', 'Ethereum', 'ETH', 18)
ON CONFLICT (address) DO NOTHING;
INSERT INTO l2_tokens
(address, name, symbol, decimals)
VALUES ('0x0000000000000000000000000000000000000000', 'Ethereum', 'ETH', 18)
ON CONFLICT (address) DO NOTHING;
`
const createL1L2NumberIndex = `
CREATE UNIQUE INDEX IF NOT EXISTS l1_blocks_number ON l1_blocks(number);
CREATE UNIQUE INDEX IF NOT EXISTS l2_blocks_number ON l2_blocks(number);
`
var schema = []string{
createL1BlocksTable,
createL2BlocksTable,
createL1TokensTable,
createL2TokensTable,
createStateBatchesTable,
insertETHL1Token,
insertETHL2Token,
createDepositsTable,
createWithdrawalsTable,
createL1L2NumberIndex,
}
package db
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// StateBatch is the state batch containing merkle root of the withdrawals
// periodically written to L1.
type StateBatch struct {
Index *big.Int
Root common.Hash
Size *big.Int
PrevTotal *big.Int
ExtraData []byte
BlockHash common.Hash
}
// StateBatchJSON contains StateBatch data suitable for JSON serialization.
type StateBatchJSON struct {
Index uint64 `json:"index"`
Root string `json:"root"`
Size uint64 `json:"size"`
PrevTotal uint64 `json:"prevTotal"`
ExtraData []byte `json:"extraData"`
BlockHash string `json:"blockHash"`
BlockNumber uint64 `json:"blockNumber"`
BlockTimestamp uint64 `json:"blockTimestamp"`
}
package db
// Token contains the token details of the ERC20 contract at the given address.
// NOTE: The Token address will almost definitely be different on L1 and L2, so
// we need to track it on both chains when handling transactions.
type Token struct {
Address string `json:"address"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals uint8 `json:"decimals"`
}
package db
import "database/sql"
func txn(db *sql.DB, apply func(*sql.Tx) error) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if p := recover(); p != nil {
// Ignore since we're panicking anyway
_ = tx.Rollback()
panic(p)
}
}()
err = apply(tx)
if err != nil {
// Don't swallow application error
_ = tx.Rollback()
return err
}
return tx.Commit()
}
package db
import (
"math/big"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
)
// Withdrawal contains transaction data for withdrawals made via the L2 to L1 bridge.
type Withdrawal struct {
GUID string
TxHash l2common.Hash
L1Token l2common.Address
L2Token l2common.Address
FromAddress l2common.Address
ToAddress l2common.Address
Amount *big.Int
Data []byte
LogIndex uint
}
// String returns the tx hash for the withdrawal.
func (w Withdrawal) String() string {
return w.TxHash.String()
}
// WithdrawalJSON contains Withdrawal data suitable for JSON serialization.
type WithdrawalJSON struct {
GUID string `json:"guid"`
FromAddress string `json:"from"`
ToAddress string `json:"to"`
L1Token string `json:"l1Token"`
L2Token *Token `json:"l2Token"`
Amount string `json:"amount"`
Data []byte `json:"data"`
LogIndex uint64 `json:"logIndex"`
BlockNumber uint64 `json:"blockNumber"`
BlockTimestamp string `json:"blockTimestamp"`
TxHash string `json:"transactionHash"`
Batch *StateBatchJSON `json:"batch"`
}
......@@ -29,28 +29,28 @@ var (
EnvVar: prefixEnvVar("ETH_NETWORK_NAME"),
}
ChainIDFlag = cli.StringFlag{
Name: "chain-id",
Usage: "Ethereum chain ID",
Name: "chain-id",
Usage: "Ethereum chain ID",
Required: true,
EnvVar: prefixEnvVar("CHAIN_ID"),
EnvVar: prefixEnvVar("CHAIN_ID"),
}
L1EthRpcFlag = cli.StringFlag{
L1EthRPCFlag = cli.StringFlag{
Name: "l1-eth-rpc",
Usage: "HTTP provider URL for L1",
Required: true,
EnvVar: prefixEnvVar("L1_ETH_RPC"),
}
L2EthRpcFlag = cli.StringFlag{
L2EthRPCFlag = cli.StringFlag{
Name: "l2-eth-rpc",
Usage: "HTTP provider URL for L2",
Required: true,
EnvVar: prefixEnvVar("L2_ETH_RPC"),
}
L1AddressManagerAddressFlag = cli.StringFlag{
Name: "l1-address-manager-address",
Usage: "Address of the L1 address manager",
Name: "l1-address-manager-address",
Usage: "Address of the L1 address manager",
Required: true,
EnvVar: prefixEnvVar("L1_ADDRESS_MANAGER_ADDRESS"),
EnvVar: prefixEnvVar("L1_ADDRESS_MANAGER_ADDRESS"),
}
L2GenesisBlockHashFlag = cli.StringFlag{
Name: "l2-genesis-block-hash",
......@@ -150,6 +150,18 @@ var (
Value: 2000,
EnvVar: prefixEnvVar("MAX_HEADER_BATCH_SIZE"),
}
RESTHostnameFlag = cli.StringFlag{
Name: "rest-hostname",
Usage: "The hostname of the REST server",
Value: "127.0.0.1",
EnvVar: prefixEnvVar("REST_HOSTNAME"),
}
RESTPortFlag = cli.Uint64Flag{
Name: "rest-port",
Usage: "The port of the REST server",
Value: 8080,
EnvVar: prefixEnvVar("REST_PORT"),
}
MetricsServerEnableFlag = cli.BoolFlag{
Name: "metrics-server-enable",
Usage: "Whether or not to run the embedded metrics server",
......@@ -173,8 +185,8 @@ var requiredFlags = []cli.Flag{
BuildEnvFlag,
EthNetworkNameFlag,
ChainIDFlag,
L1EthRpcFlag,
L2EthRpcFlag,
L1EthRPCFlag,
L2EthRPCFlag,
L1AddressManagerAddressFlag,
L2GenesisBlockHashFlag,
DBHostFlag,
......@@ -195,6 +207,8 @@ var optionalFlags = []cli.Flag{
MaxHeaderBatchSizeFlag,
StartBlockNumberFlag,
StartBlockHashFlag,
RESTHostnameFlag,
RESTPortFlag,
MetricsServerEnableFlag,
MetricsHostnameFlag,
MetricsPortFlag,
......
......@@ -6,6 +6,7 @@ require (
github.com/ethereum-optimism/optimism/l2geth v0.0.0-20220104205740-f39387287484
github.com/ethereum/go-ethereum v1.10.14
github.com/getsentry/sentry-go v0.12.0
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.0.0
github.com/prometheus/client_golang v1.0.0
......@@ -31,7 +32,6 @@ require (
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/huin/goupnp v1.0.2 // indirect
......
......@@ -238,7 +238,6 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
......
......@@ -6,6 +6,7 @@ import (
"math/big"
"net/http"
"os"
"strconv"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
......@@ -19,7 +20,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/getsentry/sentry-go"
sentry "github.com/getsentry/sentry-go"
"github.com/gorilla/mux"
"github.com/urfave/cli"
)
......@@ -201,7 +202,8 @@ func NewIndexer(cfg Config, gitVersion string) (*Indexer, error) {
}, nil
}
func (b *Indexer) Serve(ctx context.Context) {
// Serve spins up a REST API server at the given hostname and port.
func (b *Indexer) Serve() error {
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
})
......@@ -213,25 +215,41 @@ func (b *Indexer) Serve(ctx context.Context) {
b.router.HandleFunc("/v1/withdrawals/0x{address:[a-fA-F0-9]{40}}", b.l2IndexingService.GetWithdrawals).Methods("GET")
b.router.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, err := w.Write([]byte("OK"))
if err != nil {
log.Error("Error handling /healthz", "error", err)
}
})
middleware := server.LoggingMiddleware(log.New("service", "server"))
http.ListenAndServe(":8080", middleware(c.Handler(b.router)))
port := strconv.FormatUint(b.cfg.RESTPort, 10)
addr := fmt.Sprintf("%s:%s", b.cfg.RESTHostname, port)
log.Info("indexer REST server listening on", "addr", addr)
return http.ListenAndServe(addr, middleware(c.Handler(b.router)))
}
// Start starts the starts the indexing service on L1 and L2 chains and also
// starts the REST server.
func (b *Indexer) Start() error {
if b.cfg.DisableIndexer {
log.Info("indexer disabled, only serving data")
} else {
b.l1IndexingService.Start()
b.l2IndexingService.Start()
err := b.l1IndexingService.Start()
if err != nil {
return err
}
err = b.l2IndexingService.Start()
if err != nil {
return err
}
}
b.Serve(b.ctx)
return nil
return b.Serve()
}
// Stop stops the indexing service on L1 and L2 chains.
func (b *Indexer) Stop() {
if !b.cfg.DisableIndexer {
b.l1IndexingService.Stop()
......@@ -277,7 +295,3 @@ func traceRateToFloat64(rate time.Duration) float64 {
}
return rate64
}
func gasPriceFromGwei(gasPriceInGwei uint64) *big.Int {
return new(big.Int).SetUint64(gasPriceInGwei * 1e9)
}
......@@ -2,22 +2,25 @@ package server
import (
"encoding/json"
"github.com/ethereum/go-ethereum/log"
"net/http"
"runtime/debug"
"time"
"github.com/ethereum/go-ethereum/log"
)
// RespondWithError writes the given error code and message to the writer.
func RespondWithError(w http.ResponseWriter, code int, message string) {
RespondWithJSON(w, code, map[string]string{"error": message})
}
// RespondWithJSON writes the given payload marshalled as JSON to the writer.
func RespondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json")
w.Write(response)
_, _ = w.Write(response)
}
// responseWriter is a minimal wrapper for http.ResponseWriter that allows the
......@@ -44,8 +47,6 @@ func (rw *responseWriter) WriteHeader(code int) {
rw.status = code
rw.ResponseWriter.WriteHeader(code)
rw.wroteHeader = true
return
}
// LoggingMiddleware logs the incoming HTTP request & its duration.
......
......@@ -26,7 +26,6 @@ func FilterStateBatchAppendedWithRetry(filterer *scc.StateCommitmentChainFiltere
return res, err
default:
logger.Error("Error fetching filter", "err", err)
break
}
time.Sleep(clientRetryInterval)
}
......@@ -45,7 +44,6 @@ func FilterETHDepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFilte
return res, err
default:
logger.Error("Error fetching filter", "err", err)
break
}
time.Sleep(clientRetryInterval)
}
......@@ -64,7 +62,6 @@ func FilterERC20DepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFil
return res, err
default:
logger.Error("Error fetching filter", "err", err)
break
}
time.Sleep(clientRetryInterval)
}
......
......@@ -145,7 +145,7 @@ func HeaderByNumber(ctx context.Context, client *rpc.Client, height *big.Int) (*
if err == nil && head == nil {
err = ethereum.NotFound
}
return head, nil
return head, err
}
func (f *ConfirmedHeaderSelector) NewHead(
......
......@@ -35,10 +35,6 @@ var logger = log.New("service", "l1")
// and it cannot be remotely fetched
var errNoChainID = errors.New("no chain id provided")
// errWrongChainID represents the error when the configured chain id is not
// correct
var errWrongChainID = errors.New("wrong chain id provided")
var errNoNewBlocks = errors.New("no new blocks")
// clientRetryInterval is the interval to wait between retrying client API
......@@ -58,7 +54,6 @@ func HeaderByNumberWithRetry(ctx context.Context,
return res, err
default:
log.Error("Error fetching header", "err", err)
break
}
time.Sleep(clientRetryInterval)
}
......@@ -194,10 +189,12 @@ func (s *Service) Loop(ctx context.Context) {
atomic.StoreUint64(&s.latestHeader, header.Number.Uint64())
for {
err := s.Update(header)
if err != nil && err != errNoNewBlocks {
logger.Error("Unable to update indexer ", "err", err)
if err != nil {
if err != errNoNewBlocks {
logger.Error("Unable to update indexer ", "err", err)
}
break
}
break
}
case <-s.ctx.Done():
return
......@@ -509,11 +506,11 @@ func (s *Service) Start() error {
return nil
}
func (s *Service) Stop() error {
func (s *Service) Stop() {
s.cancel()
s.wg.Wait()
if err := s.cfg.DB.Close(); err != nil {
return err
err := s.cfg.DB.Close()
if err != nil {
logger.Error("Error closing db", "err", err)
}
return nil
}
......@@ -25,7 +25,6 @@ func FilterWithdrawalInitiatedWithRetry(filterer *l2bridge.L2StandardBridgeFilte
return res, err
default:
logger.Error("Error fetching filter", "err", err)
break
}
time.Sleep(clientRetryInterval)
}
......
......@@ -2,7 +2,6 @@ package l2
import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
......@@ -12,6 +11,7 @@ import (
"time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/prometheus/client_golang/prometheus"
"github.com/ethereum-optimism/optimism/go/indexer/db"
......@@ -50,24 +50,11 @@ func HeaderByNumberWithRetry(ctx context.Context,
return res, err
default:
log.Error("Error fetching header", "err", err)
break
}
time.Sleep(clientRetryInterval)
}
}
func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
type ServiceConfig struct {
Context context.Context
Metrics *metrics.Metrics
......@@ -177,10 +164,12 @@ func (s *Service) Loop(ctx context.Context) {
logger.Info("Received new header", "header", header.Hash)
for {
err := s.Update(header)
if err != nil && err != errNoNewBlocks {
logger.Error("Unable to update indexer ", "err", err)
if err != nil {
if err != errNoNewBlocks {
logger.Error("Unable to update indexer ", "err", err)
}
break
}
break
}
case <-s.ctx.Done():
return
......@@ -323,7 +312,7 @@ func (s *Service) Update(newHeader *types.Header) error {
func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) {
highestBlock, err := s.cfg.DB.GetHighestL2Block()
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
......@@ -337,7 +326,7 @@ func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) {
Highest: *highestBlock,
}
respondWithJSON(w, http.StatusOK, status)
server.RespondWithJSON(w, http.StatusOK, status)
}
func (s *Service) GetWithdrawalBatch(w http.ResponseWriter, r *http.Request) {
......@@ -345,11 +334,11 @@ func (s *Service) GetWithdrawalBatch(w http.ResponseWriter, r *http.Request) {
batch, err := s.cfg.DB.GetWithdrawalBatch(common.HexToHash(vars["hash"]))
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, batch)
server.RespondWithJSON(w, http.StatusOK, batch)
}
func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
......@@ -358,7 +347,7 @@ func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
limitStr := r.URL.Query().Get("limit")
limit, err := strconv.ParseUint(limitStr, 10, 64)
if err != nil && limitStr != "" {
respondWithError(w, http.StatusInternalServerError, err.Error())
server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
if limit == 0 {
......@@ -368,7 +357,7 @@ func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
offsetStr := r.URL.Query().Get("offset")
offset, err := strconv.ParseUint(offsetStr, 10, 64)
if err != nil && offsetStr != "" {
respondWithError(w, http.StatusInternalServerError, err.Error())
server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
......@@ -379,11 +368,11 @@ func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
withdrawals, err := s.cfg.DB.GetWithdrawalsByAddress(common.HexToAddress(vars["address"]), page)
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, withdrawals)
server.RespondWithJSON(w, http.StatusOK, withdrawals)
}
func (s *Service) subscribeNewHeads(ctx context.Context, heads chan *types.Header) {
......@@ -486,11 +475,11 @@ func (s *Service) Start() error {
return nil
}
func (s *Service) Stop() error {
func (s *Service) Stop() {
s.cancel()
s.wg.Wait()
if err := s.cfg.DB.Close(); err != nil {
return err
err := s.cfg.DB.Close()
if err != nil {
logger.Error("Error closing db", "err", err)
}
return nil
}
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