Skip to content

Commit 4dbcf23

Browse files
committed
fix(eth1_deposits): check for gaps in merkle tree index column
1 parent aa5b230 commit 4dbcf23

File tree

3 files changed

+87
-15
lines changed

3 files changed

+87
-15
lines changed

backend/pkg/commons/db/db.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,3 +2576,47 @@ func HasEventsForEpoch(epoch uint64) (bool, error) {
25762576

25772577
return count > 0, nil
25782578
}
2579+
2580+
func GetGapsInEth1DepositsTable() ([]types.GapInEth1DepositsTableRow, error) {
2581+
// the query converts the little endian encoded merkle tree index into a number and
2582+
// checks for gaps in the continuous series
2583+
query := `
2584+
WITH deposit_idx AS (
2585+
SELECT
2586+
(
2587+
SELECT SUM((get_byte(ed.merkletree_index, i)::bigint) << (8 * i))
2588+
FROM generate_series(0, length(ed.merkletree_index) - 1) AS gs(i)
2589+
) AS idx,
2590+
ed.block_number
2591+
FROM eth1_deposits AS ed
2592+
),
2593+
ordered AS (
2594+
SELECT
2595+
idx,
2596+
block_number,
2597+
lag(idx) OVER (ORDER BY idx DESC) AS prev_idx,
2598+
lag(block_number) OVER (ORDER BY idx DESC) AS prev_block_number
2599+
FROM deposit_idx
2600+
)
2601+
SELECT
2602+
prev_idx AS higher_idx,
2603+
prev_block_number AS to_block,
2604+
idx AS lower_idx,
2605+
block_number AS from_block,
2606+
(prev_idx - idx - 1) AS missing_count,
2607+
(idx + 1) AS missing_low, -- first missing value
2608+
(prev_idx - 1) AS missing_high -- last missing value
2609+
FROM ordered
2610+
WHERE prev_idx IS NOT NULL
2611+
AND (prev_idx - idx) > 1
2612+
ORDER BY higher_idx DESC;
2613+
`
2614+
var res []types.GapInEth1DepositsTableRow
2615+
2616+
err := ReaderDb.Select(&res, query)
2617+
if err != nil {
2618+
return nil, err
2619+
}
2620+
2621+
return res, nil
2622+
}

backend/pkg/commons/types/exporter.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,3 +777,18 @@ type SlashingInfo struct {
777777
SlashedValidatorPubkey []byte `db:"slashedvalidator_pubkey"`
778778
Reason string `db:"reason"`
779779
}
780+
781+
type GapInEth1DepositsTableRow struct {
782+
HigherIndex int64 `db:"higher_idx"`
783+
ToBlock int64 `db:"to_block"`
784+
LowerIndex int64 `db:"lower_idx"`
785+
FromBlock int64 `db:"from_block"`
786+
MissingCount int64 `db:"missing_count"`
787+
MissingLow int64 `db:"missing_low"`
788+
MissingHigh int64 `db:"missing_high"`
789+
}
790+
791+
func (gap GapInEth1DepositsTableRow) String() string {
792+
return fmt.Sprintf("{HigherIndex: %d, ToBlock: %d, Index: %d, FromBlock: %d, MissingCount: %d, MissingLow: %d, MissingHigh: %d}",
793+
gap.HigherIndex, gap.ToBlock, gap.LowerIndex, gap.FromBlock, gap.MissingCount, gap.MissingLow, gap.MissingHigh)
794+
}

backend/pkg/exporter/modules/execution_deposits_exporter.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"database/sql"
77
"encoding/binary"
88
"encoding/hex"
9+
"errors"
910
"fmt"
1011
"math/big"
1112
"sync/atomic"
@@ -101,27 +102,39 @@ func (d *executionDepositsExporter) Init() error {
101102
}
102103
d.DepositMethod = depositMethod
103104

104-
// check if any log_index is missing, if yes we have to do a soft re-export
105-
// ideally i would check for gaps in the merkletree-index column, but this is extremely annoying as its stored as little endian bytes in the db
106-
var isV2Check bool
107-
err = db.WriterDb.Get(&isV2Check, "select count(*) = count(log_index) as is_v2 from eth1_deposits")
105+
// check for gaps in the merkletree-index column
106+
log.Info("checking for gaps in eth1_deposits table")
107+
gapsInTable, err := db.GetGapsInEth1DepositsTable()
108108
if err != nil {
109109
return err
110110
}
111+
log.Infof("found %v gaps in eth1_deposits table", len(gapsInTable))
111112

112-
if isV2Check {
113-
// get latest block from db
114-
err = db.WriterDb.Get(&d.LastExportedBlock, "select block_number from eth1_deposits order by block_number desc limit 1")
113+
for _, gap := range gapsInTable {
114+
log.Infof("gap in eth1_deposits table: %v", gap)
115+
deposits, err := d.fetchDeposits(uint64(gap.FromBlock), uint64(gap.ToBlock))
115116
if err != nil {
116-
if err == sql.ErrNoRows {
117-
d.LastExportedBlock = utils.Config.Indexer.ELDepositContractFirstBlock
118-
} else {
119-
return err
120-
}
117+
return err
118+
}
119+
// can return more than the expected missing deposits as the start and end blocks are inclusive
120+
if int64(len(deposits)) < gap.MissingCount {
121+
return fmt.Errorf("only %d of %d expected deposits found for gap in eth1_deposits table: %v", len(deposits), gap.MissingCount, gap)
122+
}
123+
log.Infof("saving %v deposits", len(deposits))
124+
err = d.saveDeposits(deposits)
125+
if err != nil {
126+
return err
127+
}
128+
}
129+
130+
// get latest block from db
131+
err = db.WriterDb.Get(&d.LastExportedBlock, "select block_number from eth1_deposits order by block_number desc limit 1")
132+
if err != nil {
133+
if errors.Is(err, sql.ErrNoRows) {
134+
d.LastExportedBlock = utils.Config.Indexer.ELDepositContractFirstBlock
135+
} else {
136+
return err
121137
}
122-
} else {
123-
log.Warnf("log_index is missing in eth1_deposits table, starting from the beginning")
124-
d.LastExportedBlock = utils.Config.Indexer.ELDepositContractFirstBlock
125138
}
126139

127140
val, err := db.PersistentRedisDbClient.Get(context.Background(), d.LastExportedFinalizedBlockRedisKey).Uint64()

0 commit comments

Comments
 (0)