From 221bd638c0c1409d6acfeb8f997d09fb95dafbac Mon Sep 17 00:00:00 2001 From: pk910 Date: Wed, 12 Jun 2024 20:51:44 +0200 Subject: [PATCH] implement voluntary exit & slashing indexer --- .../20240506203555_exits-and-slashings.sql | 14 +- .../20240506203555_exits-and-slashings.sql | 14 +- db/slashings.go | 223 ++++++++++++++++++ db/voluntary_exits.go | 192 +++++++++++++++ dbtypes/dbtypes.go | 26 ++ dbtypes/other.go | 20 ++ indexer/cache_logic.go | 12 + indexer/write_db.go | 139 +++++++++++ 8 files changed, 614 insertions(+), 26 deletions(-) create mode 100644 db/slashings.go create mode 100644 db/voluntary_exits.go diff --git a/db/schema/pgsql/20240506203555_exits-and-slashings.sql b/db/schema/pgsql/20240506203555_exits-and-slashings.sql index 2501ce3e..b19d84cf 100644 --- a/db/schema/pgsql/20240506203555_exits-and-slashings.sql +++ b/db/schema/pgsql/20240506203555_exits-and-slashings.sql @@ -18,18 +18,6 @@ CREATE INDEX IF NOT EXISTS "voluntary_exits_slot_number_idx" ON public."voluntary_exits" ("slot_number" ASC NULLS FIRST); -CREATE INDEX IF NOT EXISTS "deposit_txs_publickey_idx" - ON public."deposit_txs" - ("publickey" ASC NULLS FIRST); - -CREATE INDEX IF NOT EXISTS "deposit_txs_tx_sender_idx" - ON public."deposit_txs" - ("tx_sender" ASC NULLS FIRST); - -CREATE INDEX IF NOT EXISTS "deposit_txs_tx_target_idx" - ON public."deposit_txs" - ("tx_target" ASC NULLS FIRST); - CREATE TABLE IF NOT EXISTS slashings ( slot_number INT NOT NULL, slot_index INT NOT NULL, @@ -38,7 +26,7 @@ CREATE TABLE IF NOT EXISTS slashings ( validator BIGINT NOT NULL, slasher BIGINT NOT NULL, reason INT NOT NULL, - CONSTRAINT deposit_pkey PRIMARY KEY (slot_root, validator) + CONSTRAINT slashings_pkey PRIMARY KEY (slot_root, slot_index, validator) ); CREATE INDEX IF NOT EXISTS "slashings_slot_number_idx" diff --git a/db/schema/sqlite/20240506203555_exits-and-slashings.sql b/db/schema/sqlite/20240506203555_exits-and-slashings.sql index 5e013f3e..946acd9c 100644 --- a/db/schema/sqlite/20240506203555_exits-and-slashings.sql +++ b/db/schema/sqlite/20240506203555_exits-and-slashings.sql @@ -18,18 +18,6 @@ CREATE INDEX IF NOT EXISTS "voluntary_exits_slot_number_idx" ON "voluntary_exits" ("slot_number" ASC); -CREATE INDEX IF NOT EXISTS "deposit_txs_publickey_idx" - ON "deposit_txs" - ("publickey" ASC); - -CREATE INDEX IF NOT EXISTS "deposit_txs_tx_sender_idx" - ON "deposit_txs" - ("tx_sender" ASC); - -CREATE INDEX IF NOT EXISTS "deposit_txs_tx_target_idx" - ON "deposit_txs" - ("tx_target" ASC); - CREATE TABLE IF NOT EXISTS slashings ( slot_number INT NOT NULL, slot_index INT NOT NULL, @@ -38,7 +26,7 @@ CREATE TABLE IF NOT EXISTS slashings ( validator BIGINT NOT NULL, slasher BIGINT NOT NULL, reason INT NOT NULL, - CONSTRAINT deposit_pkey PRIMARY KEY (slot_root, validator) + CONSTRAINT slashings_pkey PRIMARY KEY (slot_root, slot_index, validator) ); CREATE INDEX IF NOT EXISTS "slashings_slot_number_idx" diff --git a/db/slashings.go b/db/slashings.go new file mode 100644 index 00000000..c4791a14 --- /dev/null +++ b/db/slashings.go @@ -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 +} diff --git a/db/voluntary_exits.go b/db/voluntary_exits.go new file mode 100644 index 00000000..4c907664 --- /dev/null +++ b/db/voluntary_exits.go @@ -0,0 +1,192 @@ +package db + +import ( + "fmt" + "strings" + + "github.com/ethpandaops/dora/dbtypes" + "github.com/jmoiron/sqlx" +) + +func InsertVoluntaryExits(voluntaryExits []*dbtypes.VoluntaryExit, tx *sqlx.Tx) error { + var sql strings.Builder + fmt.Fprint(&sql, + EngineQuery(map[dbtypes.DBEngineType]string{ + dbtypes.DBEnginePgsql: "INSERT INTO voluntary_exits ", + dbtypes.DBEngineSqlite: "INSERT OR REPLACE INTO voluntary_exits ", + }), + "(slot_number, slot_index, slot_root, orphaned, validator)", + " VALUES ", + ) + argIdx := 0 + fieldCount := 5 + + args := make([]any, len(voluntaryExits)*fieldCount) + for i, voluntaryExit := range voluntaryExits { + 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] = voluntaryExit.SlotNumber + args[argIdx+1] = voluntaryExit.SlotIndex + args[argIdx+2] = voluntaryExit.SlotRoot + args[argIdx+3] = voluntaryExit.Orphaned + args[argIdx+4] = voluntaryExit.ValidatorIndex + argIdx += fieldCount + } + fmt.Fprint(&sql, EngineQuery(map[dbtypes.DBEngineType]string{ + dbtypes.DBEnginePgsql: " ON CONFLICT (slot_root, slot_index) DO UPDATE SET orphaned = excluded.orphaned", + dbtypes.DBEngineSqlite: "", + })) + + _, err := tx.Exec(sql.String(), args...) + if err != nil { + return err + } + return nil +} + +func GetVoluntaryExits(firstSlot uint64, limit uint32) []*dbtypes.VoluntaryExit { + var sql strings.Builder + args := []any{} + fmt.Fprint(&sql, ` + SELECT + slot_number, slot_index, slot_root, orphaned, validator + FROM voluntary_exits + `) + if firstSlot > 0 { + args = append(args, firstSlot) + fmt.Fprintf(&sql, " WHERE slot_number <= $%v ", len(args)) + } + + args = append(args, limit) + fmt.Fprintf(&sql, ` + ORDER BY slot_number DESC, slot_index DESC + LIMIT $%v + `, len(args)) + + voluntaryExits := []*dbtypes.VoluntaryExit{} + err := ReaderDb.Select(&voluntaryExits, sql.String(), args...) + if err != nil { + logger.Errorf("Error while fetching voluntary exits: %v", err) + return nil + } + return voluntaryExits +} + +func GetVoluntaryExitForValidator(validator uint64) *dbtypes.VoluntaryExit { + var sql strings.Builder + args := []any{ + validator, + } + fmt.Fprint(&sql, ` + SELECT + slot_number, slot_index, slot_root, orphaned, validator + FROM voluntary_exits + WHERE validator = $1 + `) + + voluntaryExit := &dbtypes.VoluntaryExit{} + err := ReaderDb.Get(&voluntaryExit, sql.String(), args...) + if err != nil { + return nil + } + return voluntaryExit +} + +func GetVoluntaryExitsFiltered(offset uint64, limit uint32, finalizedBlock uint64, filter *dbtypes.VoluntaryExitFilter) ([]*dbtypes.VoluntaryExit, uint64, error) { + var sql strings.Builder + args := []any{} + fmt.Fprint(&sql, ` + WITH cte AS ( + SELECT + slot_number, slot_index, slot_root, orphaned, validator + FROM voluntary_exits + `) + + if filter.ValidatorName != "" { + fmt.Fprint(&sql, ` + LEFT JOIN validator_names ON validator_names."index" = voluntary_exits.validator + `) + } + + 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.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" + } + + 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 + 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") + + voluntaryExits := []*dbtypes.VoluntaryExit{} + err := ReaderDb.Select(&voluntaryExits, sql.String(), args...) + if err != nil { + logger.Errorf("Error while fetching filtered voluntary exits: %v", err) + return nil, 0, err + } + + return voluntaryExits[1:], voluntaryExits[0].SlotNumber, nil +} diff --git a/dbtypes/dbtypes.go b/dbtypes/dbtypes.go index 11ceaa2e..e5162e87 100644 --- a/dbtypes/dbtypes.go +++ b/dbtypes/dbtypes.go @@ -154,3 +154,29 @@ type Deposit struct { WithdrawalCredentials []byte `db:"withdrawalcredentials"` Amount uint64 `db:"amount"` } + +type VoluntaryExit struct { + SlotNumber uint64 `db:"slot_number"` + SlotIndex uint64 `db:"slot_index"` + SlotRoot []byte `db:"slot_root"` + Orphaned bool `db:"orphaned"` + ValidatorIndex uint64 `db:"validator"` +} + +type SlashingReason uint8 + +const ( + UnspecifiedSlashing SlashingReason = iota + ProposerSlashing + AttesterSlashing +) + +type Slashing struct { + SlotNumber uint64 `db:"slot_number"` + SlotIndex uint64 `db:"slot_index"` + SlotRoot []byte `db:"slot_root"` + Orphaned bool `db:"orphaned"` + ValidatorIndex uint64 `db:"validator"` + SlasherIndex uint64 `db:"slasher"` + Reason SlashingReason `db:"reason"` +} diff --git a/dbtypes/other.go b/dbtypes/other.go index 7e758981..ffa4d0f2 100644 --- a/dbtypes/other.go +++ b/dbtypes/other.go @@ -47,3 +47,23 @@ type DepositFilter struct { MaxAmount uint64 WithOrphaned uint8 } + +type VoluntaryExitFilter struct { + MinSlot uint64 + MaxSlot uint64 + MinIndex uint64 + MaxIndex uint64 + ValidatorName string + WithOrphaned uint8 +} + +type SlashingFilter struct { + MinSlot uint64 + MaxSlot uint64 + MinIndex uint64 + MaxIndex uint64 + ValidatorName string + SlasherName string + WithOrphaned uint8 + WithReason SlashingReason +} diff --git a/indexer/cache_logic.go b/indexer/cache_logic.go index 598930ae..cde28e14 100644 --- a/indexer/cache_logic.go +++ b/indexer/cache_logic.go @@ -411,6 +411,18 @@ func (cache *indexerCache) processCachePersistence() error { return err } + err = persistBlockVoluntaryExits(block, true, tx) + if err != nil { + logger.Errorf("error persisting unfinalized voluntary exits: %v", err) + return err + } + + err = persistBlockSlashings(block, true, tx) + if err != nil { + logger.Errorf("error persisting unfinalized slashings: %v", err) + return err + } + block.isInUnfinalizedDb = true } } diff --git a/indexer/write_db.go b/indexer/write_db.go index 682c19e7..799fc693 100644 --- a/indexer/write_db.go +++ b/indexer/write_db.go @@ -7,6 +7,7 @@ import ( "github.com/ethpandaops/dora/dbtypes" "github.com/ethpandaops/dora/utils" "github.com/jmoiron/sqlx" + "github.com/juliangruber/go-intersect" ) func persistSlotAssignments(epochStats *EpochStats, tx *sqlx.Tx) error { @@ -73,6 +74,18 @@ func persistBlockData(block *CacheBlock, epochStats *EpochStats, depositIndex *u return err } + // insert voluntary exits + err = persistBlockVoluntaryExits(block, orphaned, tx) + if err != nil { + return err + } + + // insert slashings + err = persistBlockSlashings(block, orphaned, tx) + if err != nil { + return err + } + return nil } @@ -340,3 +353,129 @@ func buildDbDeposits(block *CacheBlock, depositIndex *uint64) []*dbtypes.Deposit return dbDeposits } + +func persistBlockVoluntaryExits(block *CacheBlock, orphaned bool, tx *sqlx.Tx) error { + // insert voluntary exits + dbVoluntaryExits := buildDbVoluntaryExits(block) + if orphaned { + for idx := range dbVoluntaryExits { + dbVoluntaryExits[idx].Orphaned = true + } + } + + if len(dbVoluntaryExits) > 0 { + err := db.InsertVoluntaryExits(dbVoluntaryExits, tx) + if err != nil { + return fmt.Errorf("error inserting voluntary exits: %v", err) + } + } + + return nil +} + +func buildDbVoluntaryExits(block *CacheBlock) []*dbtypes.VoluntaryExit { + blockBody := block.GetBlockBody() + if blockBody == nil { + return nil + } + + voluntaryExits, err := blockBody.VoluntaryExits() + if err != nil { + return nil + } + + dbVoluntaryExits := make([]*dbtypes.VoluntaryExit, len(voluntaryExits)) + for idx, voluntaryExit := range voluntaryExits { + dbVoluntaryExit := &dbtypes.VoluntaryExit{ + SlotNumber: block.Slot, + SlotIndex: uint64(idx), + SlotRoot: block.Root, + Orphaned: false, + ValidatorIndex: uint64(voluntaryExit.Message.ValidatorIndex), + } + + dbVoluntaryExits[idx] = dbVoluntaryExit + } + + return dbVoluntaryExits +} + +func persistBlockSlashings(block *CacheBlock, orphaned bool, tx *sqlx.Tx) error { + // insert slashings + dbSlashings := buildDbSlashings(block) + if orphaned { + for idx := range dbSlashings { + dbSlashings[idx].Orphaned = true + } + } + + if len(dbSlashings) > 0 { + err := db.InsertSlashings(dbSlashings, tx) + if err != nil { + return fmt.Errorf("error inserting slashings: %v", err) + } + } + + return nil +} + +func buildDbSlashings(block *CacheBlock) []*dbtypes.Slashing { + blockBody := block.GetBlockBody() + if blockBody == nil { + return nil + } + + proposerSlashings, err := blockBody.ProposerSlashings() + if err != nil { + return nil + } + + attesterSlashings, err := blockBody.AttesterSlashings() + if err != nil { + return nil + } + + proposerIndex, err := blockBody.ProposerIndex() + if err != nil { + return nil + } + + dbSlashings := []*dbtypes.Slashing{} + slashingIndex := 0 + + for _, proposerSlashing := range proposerSlashings { + dbSlashing := &dbtypes.Slashing{ + SlotNumber: block.Slot, + SlotIndex: uint64(slashingIndex), + SlotRoot: block.Root, + Orphaned: false, + ValidatorIndex: uint64(proposerSlashing.SignedHeader1.Message.ProposerIndex), + SlasherIndex: uint64(proposerIndex), + Reason: dbtypes.ProposerSlashing, + } + slashingIndex++ + dbSlashings = append(dbSlashings, dbSlashing) + } + + for _, attesterSlashing := range attesterSlashings { + inter := intersect.Simple(attesterSlashing.Attestation1.AttestingIndices, attesterSlashing.Attestation2.AttestingIndices) + for _, j := range inter { + valIdx := j.(uint64) + + dbSlashing := &dbtypes.Slashing{ + SlotNumber: block.Slot, + SlotIndex: uint64(slashingIndex), + SlotRoot: block.Root, + Orphaned: false, + ValidatorIndex: uint64(valIdx), + SlasherIndex: uint64(proposerIndex), + Reason: dbtypes.AttesterSlashing, + } + dbSlashings = append(dbSlashings, dbSlashing) + } + + slashingIndex++ + } + + return dbSlashings +}