-
-
Notifications
You must be signed in to change notification settings - Fork 479
feat: produce proposer slashings from observed equivocations #9787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e4ba525
ba17322
17b1cf1
f23f425
d660c20
0e4dd91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,6 +130,7 @@ import {CPStateDatastore} from "./stateCache/datastore/types.js"; | |
| import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js"; | ||
| import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js"; | ||
| import {CheckpointStateCache} from "./stateCache/types.js"; | ||
| import {validateApiProposerSlashing} from "./validation/proposerSlashing.js"; | ||
| import {ValidatorMonitor} from "./validatorMonitor.js"; | ||
|
|
||
| /** | ||
|
|
@@ -200,6 +201,8 @@ export class BeaconChain implements IBeaconChain { | |
| readonly seenAggregatedAttestations: SeenAggregatedAttestations; | ||
| readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids(); | ||
| readonly seenBlockProposers = new SeenBlockProposers(); | ||
| /** Proposer indexes with an in-flight proposer slashing production */ | ||
| private readonly producingProposerSlashing = new Set<ValidatorIndex>(); | ||
| readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages(); | ||
| readonly seenContributionAndProof: SeenContributionAndProof; | ||
| readonly seenAttestationDatas: SeenAttestationDatas; | ||
|
|
@@ -1163,6 +1166,59 @@ export class BeaconChain implements IBeaconChain { | |
| return this.payloadEnvelopeProcessor.processPayloadEnvelopeJob(payloadInput, opts); | ||
| } | ||
|
|
||
| processProposerEquivocation(blockSlot: Slot, proposerIndex: ValidatorIndex): void { | ||
| this.produceProposerSlashing(blockSlot, proposerIndex).catch((e) => { | ||
| this.logger.debug("Error producing proposer slashing", {slot: blockSlot, proposerIndex}, e as Error); | ||
| }); | ||
| } | ||
|
|
||
| /** Produce a proposer slashing from two conflicting signed block headers observed for the same slot and proposer */ | ||
| private async produceProposerSlashing(blockSlot: Slot, proposerIndex: ValidatorIndex): Promise<void> { | ||
| if ( | ||
| this.opts.disableProposerSlashings === true || | ||
| this.opPool.hasSeenProposerSlashing(proposerIndex) || | ||
| this.producingProposerSlashing.has(proposerIndex) | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const headers = this.seenBlockProposers.getEquivocationHeaders(blockSlot, proposerIndex); | ||
| if (headers === null) { | ||
| return; | ||
| } | ||
|
|
||
| this.producingProposerSlashing.add(proposerIndex); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be great to log header roots here
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| try { | ||
| const [header1, header2] = headers; | ||
| // ProposerSlashing uses the bigint variant of the signed block header, see types package for details | ||
| const proposerSlashing: phase0.ProposerSlashing = { | ||
| signedHeader1: { | ||
| message: {...header1.message, slot: BigInt(header1.message.slot)}, | ||
| signature: header1.signature, | ||
| }, | ||
| signedHeader2: { | ||
| message: {...header2.message, slot: BigInt(header2.message.slot)}, | ||
| signature: header2.signature, | ||
| }, | ||
| }; | ||
|
|
||
| try { | ||
| await validateApiProposerSlashing(this, proposerSlashing); | ||
| } catch (e) { | ||
| this.logger.debug("Produced proposer slashing is not valid", {slot: blockSlot, proposerIndex}, e as Error); | ||
| return; | ||
| } | ||
|
|
||
| this.opPool.insertProposerSlashing(proposerSlashing); | ||
| this.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing); | ||
| this.emitter.emit(ChainEvent.publishProposerSlashing, proposerSlashing); | ||
| this.metrics?.opPool.proposerSlashingsProduced.inc(); | ||
| this.logger.info("Produced proposer slashing from observed equivocation", {slot: blockSlot, proposerIndex}); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could consider using |
||
| } finally { | ||
| this.producingProposerSlashing.delete(proposerIndex); | ||
| } | ||
| } | ||
|
|
||
| getStatus(): Status { | ||
| const head = this.forkChoice.getHead(); | ||
| const finalizedCheckpoint = this.forkChoice.getFinalizedCheckpoint(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrap in
callInNextEventLoop()?this feels like a follow up task, not the main thing of this verifyBlock flow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this might be good, I don't think it's a lot of work to create the slashing but it doesn't need to be produced timely at all, ideally the next proposer receives it in-time but that should be the case most of the time, wrapped inside of
callInNextEventLoop()in #9795