Commit 7e0450b5 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

indexer: Update DB package (#3659)

parent 0a6166f8
......@@ -193,20 +193,10 @@ func (d *Database) AddIndexedL1Block(block *IndexedL1Block) error {
const insertDepositStatement = `
INSERT INTO deposits
(guid, from_address, to_address, l1_token, l2_token, amount, tx_hash, log_index, l1_block_hash, data)
(guid, from_address, to_address, l1_token, l2_token, amount, tx_hash, log_index, block_hash, data)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
`
const insertWithdrawalStatement = `
INSERT INTO withdrawals
(guid, from_address, to_address, l1_token, l2_token, amount, tx_hash, log_index, l1_block_hash, data)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT (tx_hash)
DO UPDATE SET l1_block_hash = $9;
`
return txn(d.db, func(tx *sql.Tx) error {
_, err := tx.Exec(
insertBlockStatement,
......@@ -242,29 +232,6 @@ func (d *Database) AddIndexedL1Block(block *IndexedL1Block) error {
}
}
if len(block.Withdrawals) == 0 {
return nil
}
for _, withdrawal := range block.Withdrawals {
_, err = tx.Exec(
insertWithdrawalStatement,
NewGUID(),
withdrawal.FromAddress.String(),
withdrawal.ToAddress.String(),
withdrawal.L1Token.String(),
withdrawal.L2Token.String(),
withdrawal.Amount.String(),
withdrawal.TxHash.String(),
withdrawal.LogIndex,
block.Hash.String(),
withdrawal.Data,
)
if err != nil {
return err
}
}
return nil
})
}
......@@ -282,9 +249,9 @@ func (d *Database) AddIndexedL2Block(block *IndexedL2Block) error {
const insertWithdrawalStatement = `
INSERT INTO withdrawals
(guid, from_address, to_address, l1_token, l2_token, amount, tx_hash, log_index, l2_block_hash, data)
(guid, from_address, to_address, l1_token, l2_token, amount, tx_hash, log_index, block_hash, data, br_withdrawal_hash)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
`
return txn(d.db, func(tx *sql.Tx) error {
_, err := tx.Exec(
......@@ -315,6 +282,37 @@ func (d *Database) AddIndexedL2Block(block *IndexedL2Block) error {
withdrawal.LogIndex,
block.Hash.String(),
withdrawal.Data,
nullableHash(withdrawal.BedrockHash),
)
if err != nil {
return err
}
}
return nil
})
}
// AddStateBatch inserts the state batches into the known state batches
// database.
func (d *Database) AddStateBatch(batches []StateBatch) error {
const insertStateBatchStatement = `
INSERT INTO state_batches
(index, root, size, prev_total, extra_data, block_hash)
VALUES
($1, $2, $3, $4, $5, $6)
`
return txn(d.db, func(tx *sql.Tx) error {
for _, sb := range batches {
_, err := tx.Exec(
insertStateBatchStatement,
sb.Index.Uint64(),
sb.Root.String(),
sb.Size.Uint64(),
sb.PrevTotal.Uint64(),
sb.ExtraData,
sb.BlockHash.String(),
)
if err != nil {
return err
......@@ -336,7 +334,7 @@ func (d *Database) GetDepositsByAddress(address common.Address, page PaginationP
l1_tokens.name, l1_tokens.symbol, l1_tokens.decimals,
l1_blocks.number, l1_blocks.timestamp
FROM deposits
INNER JOIN l1_blocks ON deposits.l1_block_hash=l1_blocks.hash
INNER JOIN l1_blocks ON deposits.block_hash=l1_blocks.hash
INNER JOIN l1_tokens ON deposits.l1_token=l1_tokens.address
WHERE deposits.from_address = $1 ORDER BY l1_blocks.timestamp LIMIT $2 OFFSET $3;
`
......@@ -375,7 +373,7 @@ func (d *Database) GetDepositsByAddress(address common.Address, page PaginationP
SELECT
count(*)
FROM deposits
INNER JOIN l1_blocks ON deposits.l1_block_hash=l1_blocks.hash
INNER JOIN l1_blocks ON deposits.block_hash=l1_blocks.hash
INNER JOIN l1_tokens ON deposits.l1_token=l1_tokens.address
WHERE deposits.from_address = $1;
`
......@@ -401,43 +399,54 @@ func (d *Database) GetDepositsByAddress(address common.Address, page PaginationP
}, nil
}
// GetWithdrawalStatus returns the finalization status corresponding to the
// given withdrawal transaction hash.
func (d *Database) GetWithdrawalStatus(hash common.Hash) (*WithdrawalJSON, error) {
const selectWithdrawalStatement = `
// GetWithdrawalBatch returns the StateBatch corresponding to the given
// withdrawal transaction hash.
func (d *Database) GetWithdrawalBatch(hash common.Hash) (*StateBatchJSON, error) {
const selectWithdrawalBatchStatement = `
SELECT
withdrawals.guid, withdrawals.from_address, withdrawals.to_address,
withdrawals.amount, withdrawals.tx_hash, withdrawals.data,
withdrawals.l1_token, withdrawals.l2_token,
l2_tokens.name, l2_tokens.symbol, l2_tokens.decimals,
l1_blocks.number, l1_blocks.timestamp,
l2_blocks.number, l2_blocks.timestamp
FROM withdrawals
INNER JOIN l1_blocks ON withdrawals.l1_block_hash=l1_blocks.hash
INNER JOIN l2_blocks ON withdrawals.l2_block_hash=l2_blocks.hash
INNER JOIN l2_tokens ON withdrawals.l2_token=l2_tokens.address
WHERE withdrawals.tx_hash = $1;
state_batches.index, state_batches.root, state_batches.size, state_batches.prev_total, state_batches.extra_data, state_batches.block_hash,
l1_blocks.number, l1_blocks.timestamp
FROM state_batches
INNER JOIN l1_blocks ON state_batches.block_hash = l1_blocks.hash
WHERE size + prev_total >= (
SELECT
number
FROM
withdrawals
INNER JOIN l2_blocks ON withdrawals.block_hash = l2_blocks.hash where tx_hash=$1
) ORDER BY "index" LIMIT 1;
`
var withdrawal *WithdrawalJSON
var batch *StateBatchJSON
err := txn(d.db, func(tx *sql.Tx) error {
row := tx.QueryRow(selectWithdrawalStatement, hash.String())
row := tx.QueryRow(selectWithdrawalBatchStatement, hash.String())
if row.Err() != nil {
return row.Err()
}
var l2Token Token
if err := row.Scan(
&withdrawal.GUID, &withdrawal.FromAddress, &withdrawal.ToAddress,
&withdrawal.Amount, &withdrawal.TxHash, &withdrawal.Data,
&withdrawal.L1Token, &l2Token.Address,
&l2Token.Name, &l2Token.Symbol, &l2Token.Decimals,
&withdrawal.L1BlockNumber, &withdrawal.L1BlockTimestamp,
&withdrawal.L2BlockNumber, &withdrawal.L2BlockTimestamp,
); err != nil {
var index, size, prevTotal, blockNumber, blockTimestamp uint64
var root, blockHash string
var extraData []byte
err := row.Scan(&index, &root, &size, &prevTotal, &extraData, &blockHash,
&blockNumber, &blockTimestamp)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
batch = nil
return nil
}
return err
}
withdrawal.L2Token = &l2Token
batch = &StateBatchJSON{
Index: index,
Root: root,
Size: size,
PrevTotal: prevTotal,
ExtraData: extraData,
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockTimestamp: blockTimestamp,
}
return nil
})
......@@ -445,24 +454,24 @@ func (d *Database) GetWithdrawalStatus(hash common.Hash) (*WithdrawalJSON, error
return nil, err
}
return withdrawal, nil
return batch, nil
}
// GetWithdrawalsByAddress returns the list of Withdrawals indexed for the given
// address paginated by the given params.
func (d *Database) GetWithdrawalsByAddress(address common.Address, page PaginationParam) (*PaginatedWithdrawals, error) {
const selectWithdrawalsStatement = `
selectWithdrawalsStatement := fmt.Sprintf(`
SELECT
withdrawals.guid, withdrawals.from_address, withdrawals.to_address,
withdrawals.amount, withdrawals.tx_hash, withdrawals.data,
withdrawals.l1_token, withdrawals.l2_token,
l2_tokens.name, l2_tokens.symbol, l2_tokens.decimals,
l2_blocks.number, l2_blocks.timestamp
l2_blocks.number, l2_blocks.timestamp, withdrawals.br_withdrawal_hash
FROM withdrawals
INNER JOIN l2_blocks ON withdrawals.l2_block_hash=l2_blocks.hash
INNER JOIN l2_blocks ON withdrawals.block_hash=l2_blocks.hash
INNER JOIN l2_tokens ON withdrawals.l2_token=l2_tokens.address
WHERE withdrawals.from_address = $1 ORDER BY l2_blocks.timestamp LIMIT $2 OFFSET $3;
`
WHERE withdrawals.from_address = $1 %s ORDER BY l2_blocks.timestamp LIMIT $2 OFFSET $3;
`, FinalizationStateAny.SQL())
var withdrawals []WithdrawalJSON
err := txn(d.db, func(tx *sql.Tx) error {
......@@ -475,16 +484,21 @@ func (d *Database) GetWithdrawalsByAddress(address common.Address, page Paginati
for rows.Next() {
var withdrawal WithdrawalJSON
var l2Token Token
var wdHash sql.NullString
if err := rows.Scan(
&withdrawal.GUID, &withdrawal.FromAddress, &withdrawal.ToAddress,
&withdrawal.Amount, &withdrawal.TxHash, &withdrawal.Data,
&withdrawal.L1Token, &l2Token.Address,
&l2Token.Name, &l2Token.Symbol, &l2Token.Decimals,
&withdrawal.L2BlockNumber, &withdrawal.L2BlockTimestamp,
&withdrawal.BlockNumber, &withdrawal.BlockTimestamp,
&wdHash,
); err != nil {
return err
}
withdrawal.L2Token = &l2Token
if wdHash.Valid {
withdrawal.BedrockWithdrawalHash = &wdHash.String
}
withdrawals = append(withdrawals, withdrawal)
}
......@@ -495,11 +509,16 @@ func (d *Database) GetWithdrawalsByAddress(address common.Address, page Paginati
return nil, err
}
for i := range withdrawals {
batch, _ := d.GetWithdrawalBatch(common.HexToHash(withdrawals[i].TxHash))
withdrawals[i].Batch = batch
}
const selectWithdrawalCountStatement = `
SELECT
count(*)
FROM withdrawals
INNER JOIN l2_blocks ON withdrawals.l2_block_hash=l2_blocks.hash
INNER JOIN l2_blocks ON withdrawals.block_hash=l2_blocks.hash
INNER JOIN l2_tokens ON withdrawals.l2_token=l2_tokens.address
WHERE withdrawals.from_address = $1;
`
......@@ -681,3 +700,12 @@ func (d *Database) GetAirdrop(address common.Address) (*Airdrop, error) {
}
return airdrop, nil
}
func nullableHash(in *common.Hash) *string {
if in == nil {
return nil
}
out := in.String()
return &out
}
......@@ -2,10 +2,12 @@ package db
import "github.com/ethereum/go-ethereum/common"
var ETHL1Address common.Address
// ETHL1Token is a placeholder token for differentiating ETH transactions from
// ERC20 transactions on L1.
var ETHL1Token = &Token{
Address: "0x0000000000000000000000000000000000000000",
Address: ETHL1Address.String(),
Name: "Ethereum",
Symbol: "ETH",
Decimals: 18,
......@@ -18,7 +20,7 @@ var ETHL2Address = common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD00
// ETHL2Token is a placeholder token for differentiating ETH transactions from
// ERC20 transactions on L2.
var ETHL2Token = &Token{
Address: "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
Address: ETHL2Address.String(),
Name: "Ethereum",
Symbol: "ETH",
Decimals: 18,
......
......@@ -11,7 +11,6 @@ type IndexedL1Block struct {
Number uint64
Timestamp uint64
Deposits []Deposit
Withdrawals []Withdrawal
}
// String returns the block hash for the indexed l1 block.
......@@ -25,7 +24,6 @@ type IndexedL2Block struct {
ParentHash common.Hash
Number uint64
Timestamp uint64
Deposits []Deposit
Withdrawals []Withdrawal
}
......
......@@ -28,10 +28,8 @@ CREATE TABLE IF NOT EXISTS deposits (
amount VARCHAR NOT NULL,
data BYTEA NOT NULL,
log_index INTEGER NOT NULL,
l1_block_hash VARCHAR NOT NULL REFERENCES l1_blocks(hash),
l2_block_hash VARCHAR REFERENCES l2_blocks(hash),
tx_hash VARCHAR NOT NULL,
failed BOOLEAN NOT NULL DEFAULT false
block_hash VARCHAR NOT NULL REFERENCES l1_blocks(hash),
tx_hash VARCHAR NOT NULL
)
`
......@@ -53,6 +51,20 @@ CREATE TABLE IF NOT EXISTS l2_tokens (
)
`
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,
......@@ -63,9 +75,9 @@ CREATE TABLE IF NOT EXISTS withdrawals (
amount VARCHAR NOT NULL,
data BYTEA NOT NULL,
log_index INTEGER NOT NULL,
l1_block_hash VARCHAR REFERENCES l1_blocks(hash),
l2_block_hash VARCHAR NOT NULL REFERENCES l2_blocks(hash),
tx_hash VARCHAR NOT NULL
block_hash VARCHAR NOT NULL REFERENCES l2_blocks(hash),
tx_hash VARCHAR NOT NULL,
state_batch INTEGER REFERENCES state_batches(index)
)
`
......@@ -110,15 +122,25 @@ CREATE TABLE IF NOT EXISTS airdrops (
)
`
const updateWithdrawalsTable = `
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_hash VARCHAR NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_finalized_tx_hash VARCHAR NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_finalized_log_index BOOLEAN NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_success BOOLEAN NULL;
CREATE INDEX IF NOT EXISTS withdrawals_br_withdrawal_hash ON withdrawals(br_withdrawal_hash);
`
var schema = []string{
createL1BlocksTable,
createL2BlocksTable,
createL1TokensTable,
createL2TokensTable,
createStateBatchesTable,
insertETHL1Token,
insertETHL2Token,
createDepositsTable,
createWithdrawalsTable,
createL1L2NumberIndex,
createAirdropsTable,
updateWithdrawalsTable,
}
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"`
}
......@@ -17,6 +17,7 @@ type Withdrawal struct {
Amount *big.Int
Data []byte
LogIndex uint
BedrockHash *common.Hash
}
// String returns the tx hash for the withdrawal.
......@@ -34,9 +35,46 @@ type WithdrawalJSON struct {
Amount string `json:"amount"`
Data []byte `json:"data"`
LogIndex uint64 `json:"logIndex"`
L1BlockNumber uint64 `json:"l1BlockNumber"`
L1BlockTimestamp string `json:"l1BlockTimestamp"`
L2BlockNumber uint64 `json:"l2BlockNumber"`
L2BlockTimestamp string `json:"l2BlockTimestamp"`
BlockNumber uint64 `json:"blockNumber"`
BlockTimestamp string `json:"blockTimestamp"`
TxHash string `json:"transactionHash"`
Batch *StateBatchJSON `json:"batch"`
BedrockWithdrawalHash *string `json:"bedrockWithdrawalHash"`
}
type FinalizationState int
const (
FinalizationStateAny FinalizationState = iota
FinalizationStateFinalized
FinalizationStateUnfinalized
)
func ParseFinalizationState(in string) FinalizationState {
switch in {
case "true":
return FinalizationStateFinalized
case "false":
return FinalizationStateUnfinalized
default:
return FinalizationStateAny
}
}
func (f FinalizationState) SQL() string {
switch f {
case FinalizationStateFinalized:
return "AND withdrawals.l1_block_hash IS NOT NULL"
case FinalizationStateUnfinalized:
return "AND withdrawals.l2_block_hash IS NULL"
}
return ""
}
type FinalizedWithdrawal struct {
WithdrawalHash common.Hash
TxHash common.Hash
Success bool
LogIndex uint
}
......@@ -303,7 +303,6 @@ func (s *Service) Update(newHeader *types.Header) error {
Number: number,
Timestamp: header.Time,
Deposits: deposits,
Withdrawals: withdrawals,
}
err := s.cfg.DB.AddIndexedL1Block(block)
......
......@@ -286,7 +286,6 @@ func (s *Service) Update(newHeader *types.Header) error {
for i, header := range headers {
blockHash := header.Hash()
number := header.Number.Uint64()
deposits := depositsByBlockHash[blockHash]
withdrawals := withdrawalsByBlockHash[blockHash]
if len(withdrawals) == 0 && i != len(headers)-1 {
......@@ -298,7 +297,6 @@ func (s *Service) Update(newHeader *types.Header) error {
ParentHash: header.ParentHash,
Number: number,
Timestamp: header.Time,
Deposits: deposits,
Withdrawals: withdrawals,
}
......@@ -359,15 +357,7 @@ func (s *Service) GetIndexerStatus(w http.ResponseWriter, r *http.Request) {
}
func (s *Service) GetWithdrawalStatus(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
withdrawal, err := s.cfg.DB.GetWithdrawalStatus(common.HexToHash(vars["hash"]))
if err != nil {
server.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
server.RespondWithJSON(w, http.StatusOK, withdrawal)
// Temporary stub until rest of indexer is landed
}
func (s *Service) GetWithdrawals(w http.ResponseWriter, r *http.Request) {
......
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