Skip to content
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

fix: duplicated block attestations #7455

Merged
merged 5 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export type AttestationsConsolidation = {
byCommittee: Map<CommitteeIndex, AttestationNonParticipant>;
attData: phase0.AttestationData;
totalNotSeenCount: number;
score: number;
};

/**
Expand Down Expand Up @@ -307,7 +306,8 @@ export class AggregatedAttestationPool {
const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);

const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
const consolidations: AttestationsConsolidation[] = [];
// Track score of each `AttestationsConsolidation`
const consolidations = new Map<AttestationsConsolidation, number>();
let minScore = Number.MAX_SAFE_INTEGER;
let slotCount = 0;
slot: for (const slot of slots) {
Expand Down Expand Up @@ -344,7 +344,7 @@ export class AggregatedAttestationPool {

if (
slotCount > 2 &&
consolidations.length >= MAX_ATTESTATIONS_ELECTRA &&
consolidations.size >= MAX_ATTESTATIONS_ELECTRA &&
notSeenAttestingIndices.size / slotDelta < minScore
) {
// after 2 slots, there are a good chance that we have 2 * MAX_ATTESTATIONS_ELECTRA attestations and break the for loop early
Expand Down Expand Up @@ -373,8 +373,6 @@ export class AggregatedAttestationPool {
byCommittee: new Map(),
attData: attestationNonParticipation.attestation.data,
totalNotSeenCount: 0,
// only update score after we have full data
score: 0,
};
}
sameAttDataCons[i].byCommittee.set(committeeIndex, attestationNonParticipation);
Expand All @@ -385,19 +383,22 @@ export class AggregatedAttestationPool {
if (score < minScore) {
minScore = score;
}
consolidations.push({...consolidation, score});

consolidations.set(consolidation, score);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this would override the record? might be better to just continue if it already exists.

But why do we have duplicates in sameAttDataCons shouldn't this be aggregated earlier into a single one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this would override the record? might be better to just continue if it already exists.

That's the whole point. The current implementation would insert the same consolidation with different score into consolidations, hence the duplication.

But why do we have duplicates in sameAttDataCons shouldn't this be aggregated earlier into a single one?

The aggregation earlier only aggregates the SignedAggregateAndProofs from the same committee. Notice how they are stored by committee index and MatchingDataAttestationGroup only store aggregated attestations from the same committee.

    private readonly attestationGroupByIndexByDataHexBySlot = new MapDef<
    Slot,
    Map<DataRootHex, Map<CommitteeIndex, MatchingDataAttestationGroup>>>


// Stop accumulating attestations there are enough that may have good scoring
if (consolidations.length >= MAX_ATTESTATIONS_ELECTRA * 2) {
if (consolidations.size >= MAX_ATTESTATIONS_ELECTRA * 2) {
break slot;
}
}
}
}
}

const sortedConsolidationsByScore = consolidations
.sort((a, b) => b.score - a.score)
const sortedConsolidationsByScore = [...consolidations.entries()]
.sort((a, b) => b[1] - a[1])
.map(([consolidation, _]) => consolidation)
.slice(0, MAX_ATTESTATIONS_ELECTRA);

Copy link
Member

@nflaig nflaig Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed we are not cloning here

// attestations could be modified in this op pool, so we need to clone for block
attestationsForBlock.push(ssz.phase0.Attestation.clone(attestationWithScore.attestation));

but might be fine due to aggregateConsolidation

// on chain aggregation is expensive, only do it after all
return sortedConsolidationsByScore.map(aggregateConsolidation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,7 @@ describe("aggregateConsolidation", () => {
const consolidation: AttestationsConsolidation = {
byCommittee: new Map(),
attData: attData,
totalNotSeenCount: 0,
score: 0,
totalNotSeenCount: 0
};
// to simplify, instead of signing the signingRoot, just sign the attData root
const sigArr = skArr.map((sk) => sk.sign(ssz.phase0.AttestationData.hashTreeRoot(attData)));
Expand Down
Loading