UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

80 lines 3.95 kB
import { MapDef, toRootHex } from "@lodestar/utils"; import { isSuperSetOrEqual } from "../../util/bitArray.js"; import { insertDesc } from "./seenAggregateAndProof.js"; /** * SyncCommittee aggregates are only useful for the next block they have signed. */ const MAX_SLOTS_IN_CACHE = 8; /** * Cache SyncCommitteeContribution and seen ContributionAndProof. * This is used for SignedContributionAndProof validation and block factory. * This stays in-memory and should be pruned per slot. */ export class SeenContributionAndProof { metrics; seenAggregatorBySlot = new MapDef(() => new Set()); seenContributionBySlot = new MapDef(() => new MapDef(() => [])); constructor(metrics) { this.metrics = metrics; } /** * _[IGNORE]_ A valid sync committee contribution with equal `slot`, `beacon_block_root` and `subcommittee_index` whose * `aggregation_bits` is non-strict superset has _not_ already been seen. */ participantsKnown(contribution) { const { aggregationBits, slot } = contribution; const contributionMap = this.seenContributionBySlot.getOrDefault(slot); const seenAggregationInfoArr = contributionMap.getOrDefault(toContributionDataKey(contribution)); this.metrics?.seenCache.committeeContributions.isKnownCalls.inc(); // seenAttestingIndicesArr is sorted by trueBitCount desc for (let i = 0; i < seenAggregationInfoArr.length; i++) { if (isSuperSetOrEqual(seenAggregationInfoArr[i].aggregationBits, aggregationBits)) { this.metrics?.seenCache.committeeContributions.isKnownHits.inc(); this.metrics?.seenCache.committeeContributions.superSetCheckTotal.observe(i + 1); return true; } } this.metrics?.seenCache.committeeContributions.superSetCheckTotal.observe(seenAggregationInfoArr.length); return false; } /** * Gossip validation requires to check: * The sync committee contribution is the first valid contribution received for the aggregator with index * contribution_and_proof.aggregator_index for the slot contribution.slot and subcommittee index contribution.subcommittee_index. */ isAggregatorKnown(slot, subcommitteeIndex, aggregatorIndex) { return this.seenAggregatorBySlot.get(slot)?.has(seenAggregatorKey(subcommitteeIndex, aggregatorIndex)) === true; } /** Register item as seen in the cache */ add(contributionAndProof, trueBitCount) { const { contribution, aggregatorIndex } = contributionAndProof; const { subcommitteeIndex, slot, aggregationBits } = contribution; // add to seenAggregatorBySlot this.seenAggregatorBySlot.getOrDefault(slot).add(seenAggregatorKey(subcommitteeIndex, aggregatorIndex)); // add to seenContributionBySlot const contributionMap = this.seenContributionBySlot.getOrDefault(slot); const seenAggregationInfoArr = contributionMap.getOrDefault(toContributionDataKey(contribution)); insertDesc(seenAggregationInfoArr, { aggregationBits, trueBitCount }); } /** Prune per head slot */ prune(headSlot) { for (const slot of this.seenAggregatorBySlot.keys()) { if (slot < headSlot - MAX_SLOTS_IN_CACHE) { this.seenAggregatorBySlot.delete(slot); } } for (const slot of this.seenContributionBySlot.keys()) { if (slot < headSlot - MAX_SLOTS_IN_CACHE) { this.seenContributionBySlot.delete(slot); } } } } function seenAggregatorKey(subcommitteeIndex, aggregatorIndex) { return `${subcommitteeIndex}-${aggregatorIndex}`; } function toContributionDataKey(contribution) { const { slot, beaconBlockRoot, subcommitteeIndex } = contribution; return `${slot} - ${toRootHex(beaconBlockRoot)} - ${subcommitteeIndex}`; } //# sourceMappingURL=seenCommitteeContribution.js.map