UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

102 lines 5.22 kB
import { MapDef } from "@lodestar/utils"; import { InsertOutcome } from "../opPools/types.js"; export var RejectReason; (function (RejectReason) { // attestation data reaches MAX_CACHE_SIZE_PER_SLOT RejectReason["reached_limit"] = "reached_limit"; // attestation data is too old RejectReason["too_old"] = "too_old"; // attestation data is already known RejectReason["already_known"] = "already_known"; })(RejectReason || (RejectReason = {})); // For pre-electra, there is no committeeIndex in SingleAttestation, so we hard code it to 0 // AttDataBase64 has committeeIndex instead export const PRE_ELECTRA_SINGLE_ATTESTATION_COMMITTEE_INDEX = 0; /** * There are maximum 64 committees per slot, assuming 1 committee may have up to 3 different data due to some nodes * are not up to date, we can have up to 192 different attestation data per slot. */ const DEFAULT_MAX_CACHE_SIZE_PER_SLOT = 200; /** * It takes less than 300kb to cache 200 attestation data per slot, so we can cache 3 slots worth of attestation data. */ const DEFAULT_CACHE_SLOT_DISTANCE = 2; /** * Cached seen AttestationData to improve gossip validation. For Electra, this still take into account attestationIndex * even through it is moved outside of AttestationData. * As of April 2023, validating gossip attestation takes ~12% of cpu time for a node subscribing to all subnets on mainnet. * Having this cache help saves a lot of cpu time since most of the gossip attestations are on the same slot. */ export class SeenAttestationDatas { constructor(metrics, cacheSlotDistance = DEFAULT_CACHE_SLOT_DISTANCE, // mainly for unit test maxCacheSizePerSlot = DEFAULT_MAX_CACHE_SIZE_PER_SLOT) { this.metrics = metrics; this.cacheSlotDistance = cacheSlotDistance; this.maxCacheSizePerSlot = maxCacheSizePerSlot; this.cacheEntryByAttDataByIndexBySlot = new MapDef(() => new MapDef(() => new Map())); this.lowestPermissibleSlot = 0; metrics?.seenCache.attestationData.totalSlot.addCollect(() => this.onScrapeLodestarMetrics(metrics)); } /** * Add an AttestationDataCacheEntry to the cache. * - preElectra: add(slot, PRE_ELECTRA_SINGLE_ATTESTATION_COMMITTEE_INDEX, attDataBase64, cacheEntry) * - electra: add(slot, committeeIndex, attDataBase64, cacheEntry) */ add(slot, committeeIndex, attDataBase64, cacheEntry) { if (slot < this.lowestPermissibleSlot) { this.metrics?.seenCache.attestationData.reject.inc({ reason: RejectReason.too_old }); return InsertOutcome.Old; } const cacheEntryByAttDataByIndex = this.cacheEntryByAttDataByIndexBySlot.getOrDefault(slot); const cacheEntryByAttData = cacheEntryByAttDataByIndex.getOrDefault(committeeIndex); if (cacheEntryByAttData.has(attDataBase64)) { this.metrics?.seenCache.attestationData.reject.inc({ reason: RejectReason.already_known }); return InsertOutcome.AlreadyKnown; } if (cacheEntryByAttData.size >= this.maxCacheSizePerSlot) { this.metrics?.seenCache.attestationData.reject.inc({ reason: RejectReason.reached_limit }); return InsertOutcome.ReachLimit; } cacheEntryByAttData.set(attDataBase64, cacheEntry); return InsertOutcome.NewData; } /** * Get an AttestationDataCacheEntry from the cache. * - preElectra: get(slot, PRE_ELECTRA_SINGLE_ATTESTATION_COMMITTEE_INDEX, attDataBase64) * - electra: get(slot, committeeIndex, attDataBase64) */ get(slot, committeeIndex, attDataBase64) { const cacheEntryByAttDataByIndex = this.cacheEntryByAttDataByIndexBySlot.get(slot); const cacheEntryByAttData = cacheEntryByAttDataByIndex?.get(committeeIndex); const cacheEntry = cacheEntryByAttData?.get(attDataBase64); if (cacheEntry) { this.metrics?.seenCache.attestationData.hit.inc(); } else { this.metrics?.seenCache.attestationData.miss.inc(); } return cacheEntry ?? null; } onSlot(clockSlot) { this.lowestPermissibleSlot = Math.max(clockSlot - this.cacheSlotDistance, 0); for (const slot of this.cacheEntryByAttDataByIndexBySlot.keys()) { if (slot < this.lowestPermissibleSlot) { this.cacheEntryByAttDataByIndexBySlot.delete(slot); } } } onScrapeLodestarMetrics(metrics) { metrics?.seenCache.attestationData.totalSlot.set(this.cacheEntryByAttDataByIndexBySlot.size); // tracking number of attestation data at current slot may not be correct if scrape time is not at the end of slot // so we track it at the previous slot const previousSlot = this.lowestPermissibleSlot + this.cacheSlotDistance - 1; const cacheEntryByAttDataByIndex = this.cacheEntryByAttDataByIndexBySlot.get(previousSlot); let count = 0; for (const cacheEntryByAttDataBase64 of cacheEntryByAttDataByIndex?.values() ?? []) { count += cacheEntryByAttDataBase64.size; } metrics?.seenCache.attestationData.countPerSlot.set(count); } } //# sourceMappingURL=seenAttestationData.js.map