import {BLSPubkey, Epoch, Root} from "@lodestar/types"; import {Logger, toPubkeyHex} from "@lodestar/utils"; import {uniqueVectorArr} from "../slashingProtection/utils.js"; import {LodestarValidatorDatabaseController} from "../types.js"; import { AttestationByTargetRepository, AttestationLowerBoundRepository, SlashingProtectionAttestationService, } from "./attestation/index.js"; import {BlockBySlotRepository, SlashingProtectionBlockService} from "./block/index.js"; import { Interchange, InterchangeFormatVersion, InterchangeLodestar, parseInterchange, serializeInterchange, } from "./interchange/index.js"; import {ISlashingProtection} from "./interface.js"; import {DistanceStoreRepository, MinMaxSurround} from "./minMaxSurround/index.js"; import {SlashingProtectionAttestation, SlashingProtectionBlock} from "./types.js"; export {InvalidAttestationError, InvalidAttestationErrorCode} from "./attestation/index.js"; export {InvalidBlockError, InvalidBlockErrorCode} from "./block/index.js"; export type {Interchange, InterchangeFormat} from "./interchange/index.js"; export {InterchangeError, InterchangeErrorErrorCode} from "./interchange/index.js"; export type {ISlashingProtection, InterchangeFormatVersion, SlashingProtectionBlock, SlashingProtectionAttestation}; /** * Handles slashing protection for validator proposer and attester duties as well as slashing protection * during a validator interchange import/export process. */ export class SlashingProtection implements ISlashingProtection { private blockService: SlashingProtectionBlockService; private attestationService: SlashingProtectionAttestationService; constructor(protected db: LodestarValidatorDatabaseController) { const blockBySlotRepository = new BlockBySlotRepository(db); const attestationByTargetRepository = new AttestationByTargetRepository(db); const attestationLowerBoundRepository = new AttestationLowerBoundRepository(db); const distanceStoreRepository = new DistanceStoreRepository(db); const minMaxSurround = new MinMaxSurround(distanceStoreRepository); this.blockService = new SlashingProtectionBlockService(blockBySlotRepository); this.attestationService = new SlashingProtectionAttestationService( attestationByTargetRepository, attestationLowerBoundRepository, minMaxSurround ); } async checkAndInsertBlockProposal(pubKey: BLSPubkey, block: SlashingProtectionBlock): Promise { await this.blockService.checkAndInsertBlockProposal(pubKey, block); } async checkAndInsertAttestation(pubKey: BLSPubkey, attestation: SlashingProtectionAttestation): Promise { await this.attestationService.checkAndInsertAttestation(pubKey, attestation); } async hasAttestedInEpoch(pubKey: BLSPubkey, epoch: Epoch): Promise { return (await this.attestationService.getAttestationForEpoch(pubKey, epoch)) !== null; } async importInterchange(interchange: Interchange, genesisValidatorsRoot: Root, logger?: Logger): Promise { const {data} = parseInterchange(interchange, genesisValidatorsRoot); for (const validator of data) { logger?.info("Importing slashing protection", {pubkey: toPubkeyHex(validator.pubkey)}); await this.blockService.importBlocks(validator.pubkey, validator.signedBlocks); await this.attestationService.importAttestations(validator.pubkey, validator.signedAttestations); } } async exportInterchange( genesisValidatorsRoot: Root, pubkeys: BLSPubkey[], formatVersion: InterchangeFormatVersion, logger?: Logger ): Promise { const validatorData: InterchangeLodestar["data"] = []; for (const pubkey of pubkeys) { logger?.info("Exporting slashing protection", {pubkey: toPubkeyHex(pubkey)}); validatorData.push({ pubkey, signedBlocks: await this.blockService.exportBlocks(pubkey), signedAttestations: await this.attestationService.exportAttestations(pubkey), }); } logger?.verbose("Serializing Interchange"); return serializeInterchange({data: validatorData, genesisValidatorsRoot}, formatVersion); } async listPubkeys(): Promise { const pubkeysAtt = await this.attestationService.listPubkeys(); const pubkeysBlk = await this.blockService.listPubkeys(); return uniqueVectorArr([...pubkeysAtt, ...pubkeysBlk]); } }