-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #62 from ethpandaops/exit-and-slashing-index
Index voluntary exits & slashings
- Loading branch information
Showing
16 changed files
with
1,830 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
-- +goose Up | ||
-- +goose StatementBegin | ||
|
||
CREATE TABLE IF NOT EXISTS voluntary_exits ( | ||
slot_number INT NOT NULL, | ||
slot_index INT NOT NULL, | ||
slot_root bytea NOT NULL, | ||
orphaned bool NOT NULL DEFAULT FALSE, | ||
validator BIGINT NOT NULL, | ||
CONSTRAINT voluntary_exits_pkey PRIMARY KEY (slot_root, slot_index) | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS "voluntary_exits_validator_idx" | ||
ON public."voluntary_exits" | ||
("validator" ASC NULLS FIRST); | ||
|
||
CREATE INDEX IF NOT EXISTS "voluntary_exits_slot_number_idx" | ||
ON public."voluntary_exits" | ||
("slot_number" ASC NULLS FIRST); | ||
|
||
CREATE TABLE IF NOT EXISTS slashings ( | ||
slot_number INT NOT NULL, | ||
slot_index INT NOT NULL, | ||
slot_root bytea NOT NULL, | ||
orphaned bool NOT NULL DEFAULT FALSE, | ||
validator BIGINT NOT NULL, | ||
slasher BIGINT NOT NULL, | ||
reason INT NOT NULL, | ||
CONSTRAINT slashings_pkey PRIMARY KEY (slot_root, slot_index, validator) | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_slot_number_idx" | ||
ON public."slashings" | ||
("slot_number" ASC NULLS FIRST); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_reason_slot_number_idx" | ||
ON public."slashings" | ||
( | ||
"reason" ASC NULLS FIRST, | ||
"slot_number" ASC NULLS FIRST | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_validator_idx" | ||
ON public."slashings" | ||
("validator" ASC NULLS FIRST); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_slasher_idx" | ||
ON public."slashings" | ||
("slasher" ASC NULLS FIRST); | ||
|
||
-- +goose StatementEnd | ||
-- +goose Down | ||
-- +goose StatementBegin | ||
SELECT 'NOT SUPPORTED'; | ||
-- +goose StatementEnd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
-- +goose Up | ||
-- +goose StatementBegin | ||
|
||
CREATE TABLE IF NOT EXISTS voluntary_exits ( | ||
slot_number INT NOT NULL, | ||
slot_index INT NOT NULL, | ||
slot_root BLOB NOT NULL, | ||
orphaned bool NOT NULL DEFAULT FALSE, | ||
validator BIGINT NOT NULL, | ||
CONSTRAINT voluntary_exits_pkey PRIMARY KEY (slot_root, slot_index) | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS "voluntary_exits_validator_idx" | ||
ON "voluntary_exits" | ||
("validator" ASC); | ||
|
||
CREATE INDEX IF NOT EXISTS "voluntary_exits_slot_number_idx" | ||
ON "voluntary_exits" | ||
("slot_number" ASC); | ||
|
||
CREATE TABLE IF NOT EXISTS slashings ( | ||
slot_number INT NOT NULL, | ||
slot_index INT NOT NULL, | ||
slot_root BLOB NOT NULL, | ||
orphaned bool NOT NULL DEFAULT FALSE, | ||
validator BIGINT NOT NULL, | ||
slasher BIGINT NOT NULL, | ||
reason INT NOT NULL, | ||
CONSTRAINT slashings_pkey PRIMARY KEY (slot_root, slot_index, validator) | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_slot_number_idx" | ||
ON "slashings" | ||
("slot_number" ASC); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_reason_slot_number_idx" | ||
ON "slashings" | ||
( | ||
"reason" ASC, | ||
"slot_number" ASC | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_validator_idx" | ||
ON "slashings" | ||
("validator" ASC); | ||
|
||
CREATE INDEX IF NOT EXISTS "slashings_slasher_idx" | ||
ON "slashings" | ||
("slasher" ASC); | ||
|
||
-- +goose StatementEnd | ||
-- +goose Down | ||
-- +goose StatementBegin | ||
SELECT 'NOT SUPPORTED'; | ||
-- +goose StatementEnd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
package db | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/ethpandaops/dora/dbtypes" | ||
"github.com/jmoiron/sqlx" | ||
) | ||
|
||
func InsertSlashings(slashings []*dbtypes.Slashing, tx *sqlx.Tx) error { | ||
var sql strings.Builder | ||
fmt.Fprint(&sql, | ||
EngineQuery(map[dbtypes.DBEngineType]string{ | ||
dbtypes.DBEnginePgsql: "INSERT INTO slashings ", | ||
dbtypes.DBEngineSqlite: "INSERT OR REPLACE INTO slashings ", | ||
}), | ||
"(slot_number, slot_index, slot_root, orphaned, validator, slasher, reason)", | ||
" VALUES ", | ||
) | ||
argIdx := 0 | ||
fieldCount := 7 | ||
|
||
args := make([]any, len(slashings)*fieldCount) | ||
for i, slashing := range slashings { | ||
if i > 0 { | ||
fmt.Fprintf(&sql, ", ") | ||
} | ||
fmt.Fprintf(&sql, "(") | ||
for f := 0; f < fieldCount; f++ { | ||
if f > 0 { | ||
fmt.Fprintf(&sql, ", ") | ||
} | ||
fmt.Fprintf(&sql, "$%v", argIdx+f+1) | ||
|
||
} | ||
fmt.Fprintf(&sql, ")") | ||
|
||
args[argIdx+0] = slashing.SlotNumber | ||
args[argIdx+1] = slashing.SlotIndex | ||
args[argIdx+2] = slashing.SlotRoot | ||
args[argIdx+3] = slashing.Orphaned | ||
args[argIdx+4] = slashing.ValidatorIndex | ||
args[argIdx+5] = slashing.SlasherIndex | ||
args[argIdx+6] = slashing.Reason | ||
argIdx += fieldCount | ||
} | ||
fmt.Fprint(&sql, EngineQuery(map[dbtypes.DBEngineType]string{ | ||
dbtypes.DBEnginePgsql: " ON CONFLICT (slot_root, slot_index, validator) DO UPDATE SET orphaned = excluded.orphaned", | ||
dbtypes.DBEngineSqlite: "", | ||
})) | ||
|
||
_, err := tx.Exec(sql.String(), args...) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func GetSlashings(firstSlot uint64, limit uint32, reason dbtypes.SlashingReason) []*dbtypes.Slashing { | ||
var sql strings.Builder | ||
args := []any{} | ||
fmt.Fprint(&sql, ` | ||
SELECT | ||
slot_number, slot_index, slot_root, orphaned, validator, slasher, reason | ||
FROM slashings | ||
`) | ||
filterOp := "WHERE" | ||
if firstSlot > 0 { | ||
args = append(args, firstSlot) | ||
fmt.Fprintf(&sql, " %v slot_number <= $%v ", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if reason > 0 { | ||
args = append(args, reason) | ||
fmt.Fprintf(&sql, " %v reason <= $%v ", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
|
||
args = append(args, limit) | ||
fmt.Fprintf(&sql, ` | ||
ORDER BY slot_number DESC, slot_index DESC | ||
LIMIT $%v | ||
`, len(args)) | ||
|
||
slashings := []*dbtypes.Slashing{} | ||
err := ReaderDb.Select(&slashings, sql.String(), args...) | ||
if err != nil { | ||
logger.Errorf("Error while fetching slashings: %v", err) | ||
return nil | ||
} | ||
return slashings | ||
} | ||
|
||
func GetSlashingForValidator(validator uint64) *dbtypes.Slashing { | ||
var sql strings.Builder | ||
args := []any{ | ||
validator, | ||
} | ||
fmt.Fprint(&sql, ` | ||
SELECT | ||
slot_number, slot_index, slot_root, orphaned, validator, slasher, reason | ||
FROM slashings | ||
WHERE validator = $1 | ||
`) | ||
|
||
slashing := &dbtypes.Slashing{} | ||
err := ReaderDb.Get(&slashing, sql.String(), args...) | ||
if err != nil { | ||
return nil | ||
} | ||
return slashing | ||
} | ||
|
||
func GetSlashingsFiltered(offset uint64, limit uint32, finalizedBlock uint64, filter *dbtypes.SlashingFilter) ([]*dbtypes.Slashing, uint64, error) { | ||
var sql strings.Builder | ||
args := []any{} | ||
fmt.Fprint(&sql, ` | ||
WITH cte AS ( | ||
SELECT | ||
slot_number, slot_index, slot_root, orphaned, validator, slasher, reason | ||
FROM slashings | ||
`) | ||
|
||
if filter.ValidatorName != "" { | ||
fmt.Fprint(&sql, ` | ||
LEFT JOIN validator_names ON validator_names."index" = slashings.validator | ||
`) | ||
} | ||
if filter.SlasherName != "" { | ||
fmt.Fprint(&sql, ` | ||
LEFT JOIN validator_names AS slasher_names ON slasher_names."index" = slashings.slasher | ||
`) | ||
} | ||
|
||
filterOp := "WHERE" | ||
if filter.MinSlot > 0 { | ||
args = append(args, filter.MinSlot) | ||
fmt.Fprintf(&sql, " %v slot_number >= $%v", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if filter.MaxSlot > 0 { | ||
args = append(args, filter.MaxSlot) | ||
fmt.Fprintf(&sql, " %v slot_number <= $%v", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if filter.MinIndex > 0 { | ||
args = append(args, filter.MinIndex) | ||
fmt.Fprintf(&sql, " %v validator >= $%v", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if filter.MaxIndex > 0 { | ||
args = append(args, filter.MaxIndex) | ||
fmt.Fprintf(&sql, " %v validator <= $%v", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if filter.WithReason > 0 { | ||
args = append(args, filter.WithReason) | ||
fmt.Fprintf(&sql, " %v reason = $%v", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if filter.WithOrphaned == 0 { | ||
args = append(args, finalizedBlock) | ||
fmt.Fprintf(&sql, " %v (slot_number > $%v OR orphaned = false)", filterOp, len(args)) | ||
filterOp = "AND" | ||
} else if filter.WithOrphaned == 2 { | ||
args = append(args, finalizedBlock) | ||
fmt.Fprintf(&sql, " %v (slot_number < $%v AND orphaned = true)", filterOp, len(args)) | ||
filterOp = "AND" | ||
} | ||
if filter.ValidatorName != "" { | ||
args = append(args, "%"+filter.ValidatorName+"%") | ||
fmt.Fprintf(&sql, " %v ", filterOp) | ||
fmt.Fprintf(&sql, EngineQuery(map[dbtypes.DBEngineType]string{ | ||
dbtypes.DBEnginePgsql: ` validator_names.name ilike $%v `, | ||
dbtypes.DBEngineSqlite: ` validator_names.name LIKE $%v `, | ||
}), len(args)) | ||
|
||
filterOp = "AND" | ||
} | ||
if filter.SlasherName != "" { | ||
args = append(args, "%"+filter.SlasherName+"%") | ||
fmt.Fprintf(&sql, " %v ", filterOp) | ||
fmt.Fprintf(&sql, EngineQuery(map[dbtypes.DBEngineType]string{ | ||
dbtypes.DBEnginePgsql: ` slasher_names.name ilike $%v `, | ||
dbtypes.DBEngineSqlite: ` slasher_names.name LIKE $%v `, | ||
}), len(args)) | ||
|
||
filterOp = "AND" | ||
} | ||
|
||
args = append(args, limit) | ||
fmt.Fprintf(&sql, `) | ||
SELECT | ||
count(*) AS slot_number, | ||
0 AS slot_index, | ||
null AS slot_root, | ||
false AS orphaned, | ||
0 AS validator, | ||
0 AS slasher, | ||
0 AS reason | ||
FROM cte | ||
UNION ALL SELECT * FROM ( | ||
SELECT * FROM cte | ||
ORDER BY slot_number DESC, slot_index DESC | ||
LIMIT $%v | ||
`, len(args)) | ||
|
||
if offset > 0 { | ||
args = append(args, offset) | ||
fmt.Fprintf(&sql, " OFFSET $%v ", len(args)) | ||
} | ||
fmt.Fprintf(&sql, ") AS t1") | ||
|
||
slashings := []*dbtypes.Slashing{} | ||
err := ReaderDb.Select(&slashings, sql.String(), args...) | ||
if err != nil { | ||
logger.Errorf("Error while fetching filtered slashings: %v", err) | ||
return nil, 0, err | ||
} | ||
|
||
return slashings[1:], slashings[0].SlotNumber, nil | ||
} |
Oops, something went wrong.