Commit dd8b0071 authored by Javed Khan's avatar Javed Khan

feat (indexer): cleanup; rpc flags; pagination

parent 0f042da8
...@@ -26,7 +26,7 @@ test: ...@@ -26,7 +26,7 @@ test:
lint: lint:
golangci-lint run ./... 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: bindings-l1bridge:
$(eval temp := $(shell mktemp)) $(eval temp := $(shell mktemp))
...@@ -130,6 +130,7 @@ bindings-address-manager: ...@@ -130,6 +130,7 @@ bindings-address-manager:
bindings-l1erc20 \ bindings-l1erc20 \
bindings-l2erc20 \ bindings-l2erc20 \
bindings-scc \ bindings-scc \
bindings-address-manager
clean \ clean \
test \ test \
lint lint
...@@ -96,6 +96,12 @@ type Config struct { ...@@ -96,6 +96,12 @@ type Config struct {
// batch. // batch.
MaxHeaderBatchSize uint64 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 // MetricsServerEnable if true, will create a metrics client and log to
// Prometheus. // Prometheus.
MetricsServerEnable bool MetricsServerEnable bool
...@@ -118,8 +124,8 @@ func NewConfig(ctx *cli.Context) (Config, error) { ...@@ -118,8 +124,8 @@ func NewConfig(ctx *cli.Context) (Config, error) {
BuildEnv: ctx.GlobalString(flags.BuildEnvFlag.Name), BuildEnv: ctx.GlobalString(flags.BuildEnvFlag.Name),
EthNetworkName: ctx.GlobalString(flags.EthNetworkNameFlag.Name), EthNetworkName: ctx.GlobalString(flags.EthNetworkNameFlag.Name),
ChainID: ctx.GlobalInt64(flags.ChainIDFlag.Name), ChainID: ctx.GlobalInt64(flags.ChainIDFlag.Name),
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name), L1EthRpc: ctx.GlobalString(flags.L1EthRPCFlag.Name),
L2EthRpc: ctx.GlobalString(flags.L2EthRpcFlag.Name), L2EthRpc: ctx.GlobalString(flags.L2EthRPCFlag.Name),
L1AddressManagerAddress: ctx.GlobalString(flags.L1AddressManagerAddressFlag.Name), L1AddressManagerAddress: ctx.GlobalString(flags.L1AddressManagerAddressFlag.Name),
L2GenesisBlockHash: ctx.GlobalString(flags.L2GenesisBlockHashFlag.Name), L2GenesisBlockHash: ctx.GlobalString(flags.L2GenesisBlockHashFlag.Name),
DBHost: ctx.GlobalString(flags.DBHostFlag.Name), DBHost: ctx.GlobalString(flags.DBHostFlag.Name),
...@@ -139,6 +145,8 @@ func NewConfig(ctx *cli.Context) (Config, error) { ...@@ -139,6 +145,8 @@ func NewConfig(ctx *cli.Context) (Config, error) {
ConfDepth: ctx.GlobalUint64(flags.ConfDepthFlag.Name), ConfDepth: ctx.GlobalUint64(flags.ConfDepthFlag.Name),
MaxHeaderBatchSize: ctx.GlobalUint64(flags.MaxHeaderBatchSizeFlag.Name), MaxHeaderBatchSize: ctx.GlobalUint64(flags.MaxHeaderBatchSizeFlag.Name),
MetricsServerEnable: ctx.GlobalBool(flags.MetricsServerEnableFlag.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), MetricsHostname: ctx.GlobalString(flags.MetricsHostnameFlag.Name),
MetricsPort: ctx.GlobalUint64(flags.MetricsPortFlag.Name), MetricsPort: ctx.GlobalUint64(flags.MetricsPortFlag.Name),
} }
......
...@@ -6,13 +6,15 @@ import ( ...@@ -6,13 +6,15 @@ import (
"testing" "testing"
indexer "github.com/ethereum-optimism/optimism/go/indexer" indexer "github.com/ethereum-optimism/optimism/go/indexer"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestParseAddress asserts that ParseAddress correctly parses 40-characater // TestParseL1Address asserts that ParseL1Address correctly parses
// hexidecimal strings with optional 0x prefix into valid 20-byte addresses. // 40-characater hexidecimal strings with optional 0x prefix into valid 20-byte
func TestParseAddress(t *testing.T) { // addresses for the L1 chain.
func TestParseL1Address(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
addr string addr string
...@@ -44,7 +46,52 @@ func TestParseAddress(t *testing.T) { ...@@ -44,7 +46,52 @@ func TestParseAddress(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { 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) require.Equal(t, err, test.expErr)
if test.expErr != nil { if test.expErr != nil {
return 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 ( ...@@ -29,28 +29,28 @@ var (
EnvVar: prefixEnvVar("ETH_NETWORK_NAME"), EnvVar: prefixEnvVar("ETH_NETWORK_NAME"),
} }
ChainIDFlag = cli.StringFlag{ ChainIDFlag = cli.StringFlag{
Name: "chain-id", Name: "chain-id",
Usage: "Ethereum chain ID", Usage: "Ethereum chain ID",
Required: true, Required: true,
EnvVar: prefixEnvVar("CHAIN_ID"), EnvVar: prefixEnvVar("CHAIN_ID"),
} }
L1EthRpcFlag = cli.StringFlag{ L1EthRPCFlag = cli.StringFlag{
Name: "l1-eth-rpc", Name: "l1-eth-rpc",
Usage: "HTTP provider URL for L1", Usage: "HTTP provider URL for L1",
Required: true, Required: true,
EnvVar: prefixEnvVar("L1_ETH_RPC"), EnvVar: prefixEnvVar("L1_ETH_RPC"),
} }
L2EthRpcFlag = cli.StringFlag{ L2EthRPCFlag = cli.StringFlag{
Name: "l2-eth-rpc", Name: "l2-eth-rpc",
Usage: "HTTP provider URL for L2", Usage: "HTTP provider URL for L2",
Required: true, Required: true,
EnvVar: prefixEnvVar("L2_ETH_RPC"), EnvVar: prefixEnvVar("L2_ETH_RPC"),
} }
L1AddressManagerAddressFlag = cli.StringFlag{ L1AddressManagerAddressFlag = cli.StringFlag{
Name: "l1-address-manager-address", Name: "l1-address-manager-address",
Usage: "Address of the L1 address manager", Usage: "Address of the L1 address manager",
Required: true, Required: true,
EnvVar: prefixEnvVar("L1_ADDRESS_MANAGER_ADDRESS"), EnvVar: prefixEnvVar("L1_ADDRESS_MANAGER_ADDRESS"),
} }
L2GenesisBlockHashFlag = cli.StringFlag{ L2GenesisBlockHashFlag = cli.StringFlag{
Name: "l2-genesis-block-hash", Name: "l2-genesis-block-hash",
...@@ -150,6 +150,18 @@ var ( ...@@ -150,6 +150,18 @@ var (
Value: 2000, Value: 2000,
EnvVar: prefixEnvVar("MAX_HEADER_BATCH_SIZE"), 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{ MetricsServerEnableFlag = cli.BoolFlag{
Name: "metrics-server-enable", Name: "metrics-server-enable",
Usage: "Whether or not to run the embedded metrics server", Usage: "Whether or not to run the embedded metrics server",
...@@ -173,8 +185,8 @@ var requiredFlags = []cli.Flag{ ...@@ -173,8 +185,8 @@ var requiredFlags = []cli.Flag{
BuildEnvFlag, BuildEnvFlag,
EthNetworkNameFlag, EthNetworkNameFlag,
ChainIDFlag, ChainIDFlag,
L1EthRpcFlag, L1EthRPCFlag,
L2EthRpcFlag, L2EthRPCFlag,
L1AddressManagerAddressFlag, L1AddressManagerAddressFlag,
L2GenesisBlockHashFlag, L2GenesisBlockHashFlag,
DBHostFlag, DBHostFlag,
...@@ -195,6 +207,8 @@ var optionalFlags = []cli.Flag{ ...@@ -195,6 +207,8 @@ var optionalFlags = []cli.Flag{
MaxHeaderBatchSizeFlag, MaxHeaderBatchSizeFlag,
StartBlockNumberFlag, StartBlockNumberFlag,
StartBlockHashFlag, StartBlockHashFlag,
RESTHostnameFlag,
RESTPortFlag,
MetricsServerEnableFlag, MetricsServerEnableFlag,
MetricsHostnameFlag, MetricsHostnameFlag,
MetricsPortFlag, MetricsPortFlag,
......
...@@ -6,6 +6,7 @@ require ( ...@@ -6,6 +6,7 @@ require (
github.com/ethereum-optimism/optimism/l2geth v0.0.0-20220104205740-f39387287484 github.com/ethereum-optimism/optimism/l2geth v0.0.0-20220104205740-f39387287484
github.com/ethereum/go-ethereum v1.10.14 github.com/ethereum/go-ethereum v1.10.14
github.com/getsentry/sentry-go v0.12.0 github.com/getsentry/sentry-go v0.12.0
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.0.0 github.com/lib/pq v1.0.0
github.com/prometheus/client_golang v1.0.0 github.com/prometheus/client_golang v1.0.0
...@@ -31,7 +32,6 @@ require ( ...@@ -31,7 +32,6 @@ require (
github.com/go-stack/stack v1.8.0 // indirect github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect github.com/golang/protobuf v1.4.3 // indirect
github.com/golang/snappy v0.0.4 // 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/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/huin/goupnp v1.0.2 // 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 ...@@ -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-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/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/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.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 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"math/big" "math/big"
"net/http" "net/http"
"os" "os"
"strconv"
"time" "time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics" "github.com/ethereum-optimism/optimism/go/indexer/metrics"
...@@ -19,7 +20,7 @@ import ( ...@@ -19,7 +20,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/getsentry/sentry-go" sentry "github.com/getsentry/sentry-go"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
...@@ -201,7 +202,8 @@ func NewIndexer(cfg Config, gitVersion string) (*Indexer, error) { ...@@ -201,7 +202,8 @@ func NewIndexer(cfg Config, gitVersion string) (*Indexer, error) {
}, nil }, 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{ c := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
}) })
...@@ -213,25 +215,41 @@ func (b *Indexer) Serve(ctx context.Context) { ...@@ -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("/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) { b.router.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200) 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")) 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 { func (b *Indexer) Start() error {
if b.cfg.DisableIndexer { if b.cfg.DisableIndexer {
log.Info("indexer disabled, only serving data") log.Info("indexer disabled, only serving data")
} else { } else {
b.l1IndexingService.Start() err := b.l1IndexingService.Start()
b.l2IndexingService.Start() if err != nil {
return err
}
err = b.l2IndexingService.Start()
if err != nil {
return err
}
} }
b.Serve(b.ctx) return b.Serve()
return nil
} }
// Stop stops the indexing service on L1 and L2 chains.
func (b *Indexer) Stop() { func (b *Indexer) Stop() {
if !b.cfg.DisableIndexer { if !b.cfg.DisableIndexer {
b.l1IndexingService.Stop() b.l1IndexingService.Stop()
...@@ -277,7 +295,3 @@ func traceRateToFloat64(rate time.Duration) float64 { ...@@ -277,7 +295,3 @@ func traceRateToFloat64(rate time.Duration) float64 {
} }
return rate64 return rate64
} }
func gasPriceFromGwei(gasPriceInGwei uint64) *big.Int {
return new(big.Int).SetUint64(gasPriceInGwei * 1e9)
}
...@@ -2,22 +2,25 @@ package server ...@@ -2,22 +2,25 @@ package server
import ( import (
"encoding/json" "encoding/json"
"github.com/ethereum/go-ethereum/log"
"net/http" "net/http"
"runtime/debug" "runtime/debug"
"time" "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) { func RespondWithError(w http.ResponseWriter, code int, message string) {
RespondWithJSON(w, code, map[string]string{"error": message}) 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{}) { func RespondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload) response, _ := json.Marshal(payload)
w.WriteHeader(code) w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Write(response) _, _ = w.Write(response)
} }
// responseWriter is a minimal wrapper for http.ResponseWriter that allows the // responseWriter is a minimal wrapper for http.ResponseWriter that allows the
...@@ -44,8 +47,6 @@ func (rw *responseWriter) WriteHeader(code int) { ...@@ -44,8 +47,6 @@ func (rw *responseWriter) WriteHeader(code int) {
rw.status = code rw.status = code
rw.ResponseWriter.WriteHeader(code) rw.ResponseWriter.WriteHeader(code)
rw.wroteHeader = true rw.wroteHeader = true
return
} }
// LoggingMiddleware logs the incoming HTTP request & its duration. // LoggingMiddleware logs the incoming HTTP request & its duration.
......
...@@ -26,7 +26,6 @@ func FilterStateBatchAppendedWithRetry(filterer *scc.StateCommitmentChainFiltere ...@@ -26,7 +26,6 @@ func FilterStateBatchAppendedWithRetry(filterer *scc.StateCommitmentChainFiltere
return res, err return res, err
default: default:
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
break
} }
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
...@@ -45,7 +44,6 @@ func FilterETHDepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFilte ...@@ -45,7 +44,6 @@ func FilterETHDepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFilte
return res, err return res, err
default: default:
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
break
} }
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
...@@ -64,7 +62,6 @@ func FilterERC20DepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFil ...@@ -64,7 +62,6 @@ func FilterERC20DepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFil
return res, err return res, err
default: default:
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
break
} }
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
......
...@@ -145,7 +145,7 @@ func HeaderByNumber(ctx context.Context, client *rpc.Client, height *big.Int) (* ...@@ -145,7 +145,7 @@ func HeaderByNumber(ctx context.Context, client *rpc.Client, height *big.Int) (*
if err == nil && head == nil { if err == nil && head == nil {
err = ethereum.NotFound err = ethereum.NotFound
} }
return head, nil return head, err
} }
func (f *ConfirmedHeaderSelector) NewHead( func (f *ConfirmedHeaderSelector) NewHead(
......
...@@ -35,10 +35,6 @@ var logger = log.New("service", "l1") ...@@ -35,10 +35,6 @@ var logger = log.New("service", "l1")
// and it cannot be remotely fetched // and it cannot be remotely fetched
var errNoChainID = errors.New("no chain id provided") 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") var errNoNewBlocks = errors.New("no new blocks")
// clientRetryInterval is the interval to wait between retrying client API // clientRetryInterval is the interval to wait between retrying client API
...@@ -58,7 +54,6 @@ func HeaderByNumberWithRetry(ctx context.Context, ...@@ -58,7 +54,6 @@ func HeaderByNumberWithRetry(ctx context.Context,
return res, err return res, err
default: default:
log.Error("Error fetching header", "err", err) log.Error("Error fetching header", "err", err)
break
} }
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
...@@ -194,10 +189,12 @@ func (s *Service) Loop(ctx context.Context) { ...@@ -194,10 +189,12 @@ func (s *Service) Loop(ctx context.Context) {
atomic.StoreUint64(&s.latestHeader, header.Number.Uint64()) atomic.StoreUint64(&s.latestHeader, header.Number.Uint64())
for { for {
err := s.Update(header) err := s.Update(header)
if err != nil && err != errNoNewBlocks { if err != nil {
logger.Error("Unable to update indexer ", "err", err) if err != errNoNewBlocks {
logger.Error("Unable to update indexer ", "err", err)
}
break
} }
break
} }
case <-s.ctx.Done(): case <-s.ctx.Done():
return return
...@@ -509,11 +506,11 @@ func (s *Service) Start() error { ...@@ -509,11 +506,11 @@ func (s *Service) Start() error {
return nil return nil
} }
func (s *Service) Stop() error { func (s *Service) Stop() {
s.cancel() s.cancel()
s.wg.Wait() s.wg.Wait()
if err := s.cfg.DB.Close(); err != nil { err := s.cfg.DB.Close()
return err if err != nil {
logger.Error("Error closing db", "err", err)
} }
return nil
} }
...@@ -25,7 +25,6 @@ func FilterWithdrawalInitiatedWithRetry(filterer *l2bridge.L2StandardBridgeFilte ...@@ -25,7 +25,6 @@ func FilterWithdrawalInitiatedWithRetry(filterer *l2bridge.L2StandardBridgeFilte
return res, err return res, err
default: default:
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
break
} }
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
......
...@@ -2,7 +2,6 @@ package l2 ...@@ -2,7 +2,6 @@ package l2
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
...@@ -12,6 +11,7 @@ import ( ...@@ -12,6 +11,7 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics" "github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/ethereum-optimism/optimism/go/indexer/db" "github.com/ethereum-optimism/optimism/go/indexer/db"
...@@ -50,24 +50,11 @@ func HeaderByNumberWithRetry(ctx context.Context, ...@@ -50,24 +50,11 @@ func HeaderByNumberWithRetry(ctx context.Context,
return res, err return res, err
default: default:
log.Error("Error fetching header", "err", err) log.Error("Error fetching header", "err", err)
break
} }
time.Sleep(clientRetryInterval) 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 { type ServiceConfig struct {
Context context.Context Context context.Context
Metrics *metrics.Metrics Metrics *metrics.Metrics
...@@ -177,10 +164,12 @@ func (s *Service) Loop(ctx context.Context) { ...@@ -177,10 +164,12 @@ func (s *Service) Loop(ctx context.Context) {
logger.Info("Received new header", "header", header.Hash) logger.Info("Received new header", "header", header.Hash)
for { for {
err := s.Update(header) err := s.Update(header)
if err != nil && err != errNoNewBlocks { if err != nil {
logger.Error("Unable to update indexer ", "err", err) if err != errNoNewBlocks {
logger.Error("Unable to update indexer ", "err", err)
}
break
} }
break
} }
case <-s.ctx.Done(): case <-s.ctx.Done():
return return
...@@ -323,7 +312,7 @@ func (s *Service) Update(newHeader *types.Header) error { ...@@ -323,7 +312,7 @@ func (s *Service) Update(newHeader *types.Header) error {
func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) { func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) {
highestBlock, err := s.cfg.DB.GetHighestL2Block() highestBlock, err := s.cfg.DB.GetHighestL2Block()
if err != nil { if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error()) server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return return
} }
...@@ -337,7 +326,7 @@ func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) { ...@@ -337,7 +326,7 @@ func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) {
Highest: *highestBlock, Highest: *highestBlock,
} }
respondWithJSON(w, http.StatusOK, status) server.RespondWithJSON(w, http.StatusOK, status)
} }
func (s *Service) GetWithdrawalBatch(w http.ResponseWriter, r *http.Request) { func (s *Service) GetWithdrawalBatch(w http.ResponseWriter, r *http.Request) {
...@@ -345,11 +334,11 @@ 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"])) batch, err := s.cfg.DB.GetWithdrawalBatch(common.HexToHash(vars["hash"]))
if err != nil { if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error()) server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return return
} }
respondWithJSON(w, http.StatusOK, batch) server.RespondWithJSON(w, http.StatusOK, batch)
} }
func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) { func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
...@@ -358,7 +347,7 @@ 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") limitStr := r.URL.Query().Get("limit")
limit, err := strconv.ParseUint(limitStr, 10, 64) limit, err := strconv.ParseUint(limitStr, 10, 64)
if err != nil && limitStr != "" { if err != nil && limitStr != "" {
respondWithError(w, http.StatusInternalServerError, err.Error()) server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return return
} }
if limit == 0 { if limit == 0 {
...@@ -368,7 +357,7 @@ func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) { ...@@ -368,7 +357,7 @@ func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
offsetStr := r.URL.Query().Get("offset") offsetStr := r.URL.Query().Get("offset")
offset, err := strconv.ParseUint(offsetStr, 10, 64) offset, err := strconv.ParseUint(offsetStr, 10, 64)
if err != nil && offsetStr != "" { if err != nil && offsetStr != "" {
respondWithError(w, http.StatusInternalServerError, err.Error()) server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return return
} }
...@@ -379,11 +368,11 @@ func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) { ...@@ -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) withdrawals, err := s.cfg.DB.GetWithdrawalsByAddress(common.HexToAddress(vars["address"]), page)
if err != nil { if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error()) server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return return
} }
respondWithJSON(w, http.StatusOK, withdrawals) server.RespondWithJSON(w, http.StatusOK, withdrawals)
} }
func (s *Service) subscribeNewHeads(ctx context.Context, heads chan *types.Header) { func (s *Service) subscribeNewHeads(ctx context.Context, heads chan *types.Header) {
...@@ -486,11 +475,11 @@ func (s *Service) Start() error { ...@@ -486,11 +475,11 @@ func (s *Service) Start() error {
return nil return nil
} }
func (s *Service) Stop() error { func (s *Service) Stop() {
s.cancel() s.cancel()
s.wg.Wait() s.wg.Wait()
if err := s.cfg.DB.Close(); err != nil { err := s.cfg.DB.Close()
return err 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