@lodestar/beacon-node 1.45.0-dev.f535421f29 → 1.45.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/index.d.ts +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +6 -43
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +48 -5
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +13 -3
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +48 -4
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +2 -2
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +2 -2
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/proof/index.d.ts +1 -1
- package/lib/api/impl/proof/index.d.ts.map +1 -1
- package/lib/api/impl/proof/index.js +2 -2
- package/lib/api/impl/proof/index.js.map +1 -1
- package/lib/api/impl/utils.d.ts +23 -0
- package/lib/api/impl/utils.d.ts.map +1 -1
- package/lib/api/impl/utils.js +44 -1
- package/lib/api/impl/utils.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +0 -14
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +67 -62
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +8 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +2 -5
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/builderCircuitBreaker.d.ts +31 -0
- package/lib/chain/builderCircuitBreaker.d.ts.map +1 -0
- package/lib/chain/builderCircuitBreaker.js +59 -0
- package/lib/chain/builderCircuitBreaker.js.map +1 -0
- package/lib/chain/chain.d.ts +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +4 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +3 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +2 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +3 -0
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +5 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +12 -7
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -12
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +0 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +0 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +11 -10
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +14 -5
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/execution/builder/http.d.ts +18 -0
- package/lib/execution/builder/http.d.ts.map +1 -1
- package/lib/execution/builder/http.js +20 -11
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +6 -0
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +18 -0
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/network/core/metrics.d.ts +65 -0
- package/lib/network/core/metrics.d.ts.map +1 -1
- package/lib/network/core/metrics.js +94 -0
- package/lib/network/core/metrics.js.map +1 -1
- package/lib/network/discv5/utils.d.ts.map +1 -1
- package/lib/network/discv5/utils.js +12 -3
- package/lib/network/discv5/utils.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +7 -0
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +2 -1
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +765 -3
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +23 -3
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts.map +1 -1
- package/lib/network/peers/peerManager.js +10 -0
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/peers/score/score.d.ts +3 -1
- package/lib/network/peers/score/score.d.ts.map +1 -1
- package/lib/network/peers/score/score.js +6 -1
- package/lib/network/peers/score/score.js.map +1 -1
- package/lib/network/peers/score/store.d.ts.map +1 -1
- package/lib/network/peers/score/store.js +6 -1
- package/lib/network/peers/score/store.js.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.js +14 -0
- package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/util/graffiti.d.ts +15 -0
- package/lib/util/graffiti.d.ts.map +1 -1
- package/lib/util/graffiti.js +55 -0
- package/lib/util/graffiti.js.map +1 -1
- package/package.json +19 -19
- package/src/api/impl/beacon/index.ts +1 -1
- package/src/api/impl/beacon/pool/index.ts +5 -53
- package/src/api/impl/beacon/state/index.ts +55 -3
- package/src/api/impl/beacon/state/utils.ts +68 -4
- package/src/api/impl/debug/index.ts +3 -2
- package/src/api/impl/lodestar/index.ts +2 -2
- package/src/api/impl/proof/index.ts +2 -2
- package/src/api/impl/utils.ts +51 -1
- package/src/api/impl/validator/index.ts +75 -67
- package/src/api/rest/base.ts +9 -0
- package/src/chain/blocks/importBlock.ts +2 -7
- package/src/chain/builderCircuitBreaker.ts +84 -0
- package/src/chain/chain.ts +7 -3
- package/src/chain/interface.ts +2 -2
- package/src/chain/opPools/proposerPreferencesPool.ts +5 -1
- package/src/chain/options.ts +6 -0
- package/src/chain/prepareNextSlot.ts +11 -7
- package/src/chain/produceBlock/produceBlockBody.ts +9 -17
- package/src/chain/seenCache/index.ts +0 -1
- package/src/chain/validation/executionPayloadBid.ts +14 -9
- package/src/chain/validation/proposerPreferences.ts +15 -5
- package/src/execution/builder/http.ts +30 -17
- package/src/metrics/metrics/beacon.ts +19 -0
- package/src/network/core/metrics.ts +94 -0
- package/src/network/discv5/utils.ts +11 -3
- package/src/network/gossip/encoding.ts +7 -0
- package/src/network/gossip/gossipsub.ts +2 -1
- package/src/network/gossip/topic.ts +28 -4
- package/src/network/peers/peerManager.ts +11 -0
- package/src/network/peers/score/score.ts +8 -1
- package/src/network/peers/score/store.ts +8 -1
- package/src/network/peers/utils/prioritizePeers.ts +15 -0
- package/src/network/processor/gossipHandlers.ts +6 -2
- package/src/util/graffiti.ts +78 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
- package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
- package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
|
@@ -246,7 +246,7 @@ export function parseGossipTopic(forkDigestContext: ForkDigestContext, topicStr:
|
|
|
246
246
|
export function getCoreTopicsAtFork(
|
|
247
247
|
networkConfig: NetworkConfig,
|
|
248
248
|
fork: ForkName,
|
|
249
|
-
opts: {subscribeAllSubnets?: boolean; disableLightClientServer?: boolean}
|
|
249
|
+
opts: {subscribeAllSubnets?: boolean; subscribeAllColumnSubnets?: boolean; disableLightClientServer?: boolean}
|
|
250
250
|
): GossipTopicTypeMap[keyof GossipTopicTypeMap][] {
|
|
251
251
|
// Common topics for all forks
|
|
252
252
|
const topics: GossipTopicTypeMap[keyof GossipTopicTypeMap][] = [
|
|
@@ -266,7 +266,7 @@ export function getCoreTopicsAtFork(
|
|
|
266
266
|
|
|
267
267
|
// After fulu also track data_column_sidecar_{index}
|
|
268
268
|
if (ForkSeq[fork] >= ForkSeq.fulu) {
|
|
269
|
-
topics.push(...getDataColumnSidecarTopics(networkConfig));
|
|
269
|
+
topics.push(...getDataColumnSidecarTopics(networkConfig, opts.subscribeAllColumnSubnets));
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
// After Deneb and before Fulu also track blob_sidecar_{subnet_id}
|
|
@@ -309,15 +309,39 @@ export function getCoreTopicsAtFork(
|
|
|
309
309
|
return topics;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Build the complete set of valid gossip topic strings across every fork boundary.
|
|
314
|
+
*/
|
|
315
|
+
export function getAllowedTopics(networkConfig: NetworkConfig): Set<string> {
|
|
316
|
+
const {config} = networkConfig;
|
|
317
|
+
const allowedTopics = new Set<string>();
|
|
318
|
+
|
|
319
|
+
for (const boundary of config.forkBoundariesAscendingEpochOrder) {
|
|
320
|
+
const topics = getCoreTopicsAtFork(networkConfig, boundary.fork, {
|
|
321
|
+
subscribeAllSubnets: true,
|
|
322
|
+
subscribeAllColumnSubnets: true,
|
|
323
|
+
disableLightClientServer: false,
|
|
324
|
+
});
|
|
325
|
+
for (const topic of topics) {
|
|
326
|
+
allowedTopics.add(stringifyGossipTopic(config, {...topic, boundary}));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return allowedTopics;
|
|
331
|
+
}
|
|
332
|
+
|
|
312
333
|
/**
|
|
313
334
|
* Pick data column subnets to subscribe to post-fulu.
|
|
314
335
|
*/
|
|
315
336
|
export function getDataColumnSidecarTopics(
|
|
316
|
-
networkConfig: NetworkConfig
|
|
337
|
+
networkConfig: NetworkConfig,
|
|
338
|
+
subscribeAllColumnSubnets = false
|
|
317
339
|
): GossipTopicTypeMap[keyof GossipTopicTypeMap][] {
|
|
318
340
|
const topics: GossipTopicTypeMap[keyof GossipTopicTypeMap][] = [];
|
|
319
341
|
|
|
320
|
-
const subnets =
|
|
342
|
+
const subnets = subscribeAllColumnSubnets
|
|
343
|
+
? Array.from({length: networkConfig.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT}, (_, i) => i)
|
|
344
|
+
: networkConfig.custodyConfig.sampledSubnets;
|
|
321
345
|
for (const subnet of subnets) {
|
|
322
346
|
topics.push({type: GossipType.data_column_sidecar, subnet});
|
|
323
347
|
}
|
|
@@ -415,8 +415,10 @@ export class PeerManager {
|
|
|
415
415
|
);
|
|
416
416
|
if (irrelevantReasonType === null) {
|
|
417
417
|
isIrrelevant = false;
|
|
418
|
+
this.metrics?.peerManager.relevanceCheck.inc({result: "relevant"});
|
|
418
419
|
} else {
|
|
419
420
|
isIrrelevant = true;
|
|
421
|
+
this.metrics?.peerManager.relevanceCheck.inc({result: irrelevantReasonType.code});
|
|
420
422
|
this.logger.debug("Irrelevant peer", {
|
|
421
423
|
peer: prettyPrintPeerId(peer),
|
|
422
424
|
reason: renderIrrelevantPeerType(irrelevantReasonType),
|
|
@@ -424,6 +426,7 @@ export class PeerManager {
|
|
|
424
426
|
}
|
|
425
427
|
} catch (e) {
|
|
426
428
|
this.logger.error("Irrelevant peer - unexpected error", {peer: prettyPrintPeerId(peer)}, e as Error);
|
|
429
|
+
this.metrics?.peerManager.relevanceCheck.inc({result: "error"});
|
|
427
430
|
isIrrelevant = true;
|
|
428
431
|
}
|
|
429
432
|
|
|
@@ -552,15 +555,18 @@ export class PeerManager {
|
|
|
552
555
|
for (const peer of connectedPeers) {
|
|
553
556
|
switch (this.peerRpcScores.getScoreState(peer)) {
|
|
554
557
|
case ScoreState.Banned:
|
|
558
|
+
this.metrics?.peerManager.peersPruned.inc({reason: "banned"});
|
|
555
559
|
void this.goodbyeAndDisconnect(peer, GoodByeReasonCode.BANNED);
|
|
556
560
|
break;
|
|
557
561
|
case ScoreState.Disconnected:
|
|
562
|
+
this.metrics?.peerManager.peersPruned.inc({reason: "score_too_low"});
|
|
558
563
|
void this.goodbyeAndDisconnect(peer, GoodByeReasonCode.SCORE_TOO_LOW);
|
|
559
564
|
break;
|
|
560
565
|
case ScoreState.Healthy:
|
|
561
566
|
connectedHealthyPeers.push(peer);
|
|
562
567
|
}
|
|
563
568
|
}
|
|
569
|
+
this.metrics?.peerManager.peersEvaluated.observe(connectedHealthyPeers.length);
|
|
564
570
|
|
|
565
571
|
const status = this.statusCache.get();
|
|
566
572
|
const starved =
|
|
@@ -572,6 +578,7 @@ export class PeerManager {
|
|
|
572
578
|
this.metrics?.peerManager.starved.set(starved ? 1 : 0);
|
|
573
579
|
const forkSeq = this.config.getForkSeq(this.clock.currentSlot);
|
|
574
580
|
|
|
581
|
+
const prioritizeTimer = this.metrics?.peerManager.prioritizePeersDuration.startTimer();
|
|
575
582
|
const {peersToDisconnect, peersToConnect, attnetQueries, syncnetQueries, custodyGroupQueries} = prioritizePeers(
|
|
576
583
|
connectedHealthyPeers.map((peer) => {
|
|
577
584
|
const peerData = this.connectedPeers.get(peer.toString());
|
|
@@ -601,6 +608,7 @@ export class PeerManager {
|
|
|
601
608
|
this.config,
|
|
602
609
|
this.metrics
|
|
603
610
|
);
|
|
611
|
+
prioritizeTimer?.();
|
|
604
612
|
|
|
605
613
|
const queriesMerged: SubnetDiscvQueryMs[] = [];
|
|
606
614
|
for (const {type, queries} of [
|
|
@@ -632,6 +640,7 @@ export class PeerManager {
|
|
|
632
640
|
// disconnect first to have more slots before we dial new peers
|
|
633
641
|
for (const [reason, peers] of peersToDisconnect) {
|
|
634
642
|
this.metrics?.peersRequestedToDisconnect.inc({reason}, peers.length);
|
|
643
|
+
this.metrics?.peerManager.peersPruned.inc({reason}, peers.length);
|
|
635
644
|
for (const peer of peers) {
|
|
636
645
|
void this.goodbyeAndDisconnect(peer, GoodByeReasonCode.TOO_MANY_PEERS);
|
|
637
646
|
}
|
|
@@ -659,6 +668,8 @@ export class PeerManager {
|
|
|
659
668
|
}
|
|
660
669
|
}
|
|
661
670
|
|
|
671
|
+
this.metrics?.peerManager.connectedPeersMapSize.set(this.connectedPeers.size);
|
|
672
|
+
|
|
662
673
|
timer?.();
|
|
663
674
|
|
|
664
675
|
this.logger.debug("peerManager heartbeat result", {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {GoodByeReasonCode} from "../../../constants/network.js";
|
|
2
|
+
import {NetworkCoreMetrics} from "../../core/metrics.js";
|
|
2
3
|
import {
|
|
3
4
|
COOL_DOWN_BEFORE_DECAY_MS,
|
|
4
5
|
DEFAULT_SCORE,
|
|
@@ -23,13 +24,15 @@ export class RealScore implements IPeerScore {
|
|
|
23
24
|
/** The final score, computed from the above */
|
|
24
25
|
private score: number;
|
|
25
26
|
private lastUpdate: number;
|
|
27
|
+
private readonly metrics: NetworkCoreMetrics | null;
|
|
26
28
|
|
|
27
|
-
constructor() {
|
|
29
|
+
constructor(metrics: NetworkCoreMetrics | null = null) {
|
|
28
30
|
this.lodestarScore = DEFAULT_SCORE;
|
|
29
31
|
this.gossipScore = DEFAULT_SCORE;
|
|
30
32
|
this.score = DEFAULT_SCORE;
|
|
31
33
|
this.ignoreNegativeGossipScore = false;
|
|
32
34
|
this.lastUpdate = Date.now();
|
|
35
|
+
this.metrics = metrics;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
isCoolingDown(): boolean {
|
|
@@ -136,6 +139,10 @@ export class RealScore implements IPeerScore {
|
|
|
136
139
|
this.recomputeScore();
|
|
137
140
|
const newState = scoreToState(this.score);
|
|
138
141
|
|
|
142
|
+
if (prevState !== newState) {
|
|
143
|
+
this.metrics?.peerManager.scoreStateTransitions.inc({from: prevState, to: newState});
|
|
144
|
+
}
|
|
145
|
+
|
|
139
146
|
if (prevState !== ScoreState.Banned && newState === ScoreState.Banned) {
|
|
140
147
|
// ban this peer for at least BANNED_BEFORE_DECAY_MS seconds
|
|
141
148
|
this.lastUpdate = Date.now() + COOL_DOWN_BEFORE_DECAY_MS;
|
|
@@ -32,7 +32,9 @@ export class PeerRpcScoreStore implements IPeerRpcScoreStore {
|
|
|
32
32
|
constructor(opts: PeerRpcScoreOpts = {}, metrics: NetworkCoreMetrics | null = null, logger: Logger | null = null) {
|
|
33
33
|
this.metrics = metrics;
|
|
34
34
|
this.logger = logger;
|
|
35
|
-
this.scores = opts.disablePeerScoring
|
|
35
|
+
this.scores = opts.disablePeerScoring
|
|
36
|
+
? new MapDef(() => new MaxScore())
|
|
37
|
+
: new MapDef(() => new RealScore(this.metrics));
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
getScore(peer: PeerId): number {
|
|
@@ -75,6 +77,8 @@ export class PeerRpcScoreStore implements IPeerRpcScoreStore {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
update(): void {
|
|
80
|
+
const timer = this.metrics?.peerManager.scoreUpdateDuration.startTimer();
|
|
81
|
+
|
|
78
82
|
// Bound size of data structures
|
|
79
83
|
pruneSetToMax(this.scores, MAX_ENTRIES);
|
|
80
84
|
|
|
@@ -86,6 +90,9 @@ export class PeerRpcScoreStore implements IPeerRpcScoreStore {
|
|
|
86
90
|
this.scores.delete(peerIdStr);
|
|
87
91
|
}
|
|
88
92
|
}
|
|
93
|
+
|
|
94
|
+
this.metrics?.peerManager.scoreMapSize.set(this.scores.size);
|
|
95
|
+
timer?.();
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
updateGossipsubScore(peerId: PeerIdStr, newScore: number, ignore: boolean): void {
|
|
@@ -7,6 +7,7 @@ import {MapDef} from "@lodestar/utils";
|
|
|
7
7
|
import {shuffle} from "../../../util/shuffle.js";
|
|
8
8
|
import {sortBy} from "../../../util/sortBy.js";
|
|
9
9
|
import {NetworkCoreMetrics} from "../../core/metrics.js";
|
|
10
|
+
import {SubnetType} from "../../metadata.js";
|
|
10
11
|
import {RequestedSubnet} from "./subnetMap.js";
|
|
11
12
|
|
|
12
13
|
/** Target number of peers we'd like to have connected to a given long-lived subnet */
|
|
@@ -189,6 +190,18 @@ export function prioritizePeers(
|
|
|
189
190
|
|
|
190
191
|
const connectedPeerCount = connectedPeers.length;
|
|
191
192
|
|
|
193
|
+
// Track the outbound peers invariant (we keep at least OUTBOUND_PEERS_RATIO outbound peers).
|
|
194
|
+
// Report 0 when there are no peers so the gauge doesn't go stale at its last value.
|
|
195
|
+
if (metrics) {
|
|
196
|
+
let outboundPeerCount = 0;
|
|
197
|
+
for (const peer of connectedPeers) {
|
|
198
|
+
if (peer.direction === "outbound") {
|
|
199
|
+
outboundPeerCount++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
metrics.peerManager.outboundPeersRatio.set(connectedPeerCount > 0 ? outboundPeerCount / connectedPeerCount : 0);
|
|
203
|
+
}
|
|
204
|
+
|
|
192
205
|
if (connectedPeerCount < targetPeers) {
|
|
193
206
|
// Need more peers.
|
|
194
207
|
// Instead of attempting to connect the exact amount necessary this will overshoot a little since the success
|
|
@@ -256,6 +269,7 @@ function requestSubnetPeers(
|
|
|
256
269
|
|
|
257
270
|
for (const {subnet, toSlot} of activeAttnets) {
|
|
258
271
|
const peersInSubnet = peersPerSubnet.get(subnet) ?? 0;
|
|
272
|
+
metrics?.peerManager.peersPerActiveSubnet.observe({type: SubnetType.attnets}, peersInSubnet);
|
|
259
273
|
if (peersInSubnet < targetSubnetPeers) {
|
|
260
274
|
// We need more peers
|
|
261
275
|
attnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers - peersInSubnet});
|
|
@@ -282,6 +296,7 @@ function requestSubnetPeers(
|
|
|
282
296
|
|
|
283
297
|
for (const {subnet, toSlot} of activeSyncnets) {
|
|
284
298
|
const peersInSubnet = peersPerSubnet.get(subnet) ?? 0;
|
|
299
|
+
metrics?.peerManager.peersPerActiveSubnet.observe({type: SubnetType.syncnets}, peersInSubnet);
|
|
285
300
|
if (peersInSubnet < targetSubnetPeers) {
|
|
286
301
|
// We need more peers
|
|
287
302
|
syncnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers - peersInSubnet});
|
|
@@ -1241,6 +1241,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1241
1241
|
payloadAttestationMessage.data.payloadPresent,
|
|
1242
1242
|
payloadAttestationMessage.data.blobDataAvailable
|
|
1243
1243
|
);
|
|
1244
|
+
|
|
1245
|
+
chain.emitter.emit(routes.events.EventType.payloadAttestationMessage, {
|
|
1246
|
+
version: config.getForkName(payloadAttestationMessage.data.slot),
|
|
1247
|
+
data: payloadAttestationMessage,
|
|
1248
|
+
});
|
|
1244
1249
|
},
|
|
1245
1250
|
[GossipType.execution_payload_bid]: async ({
|
|
1246
1251
|
gossipData,
|
|
@@ -1273,9 +1278,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1273
1278
|
const signedProposerPreferences = sszDeserialize(topic, serializedData);
|
|
1274
1279
|
await validateGossipProposerPreferences(chain, signedProposerPreferences);
|
|
1275
1280
|
|
|
1276
|
-
chain.proposerPreferencesPool.add(signedProposerPreferences);
|
|
1277
1281
|
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1278
|
-
version:
|
|
1282
|
+
version: config.getForkName(signedProposerPreferences.message.proposalSlot),
|
|
1279
1283
|
data: signedProposerPreferences,
|
|
1280
1284
|
});
|
|
1281
1285
|
},
|
package/src/util/graffiti.ts
CHANGED
|
@@ -37,3 +37,81 @@ export function getDefaultGraffiti(
|
|
|
37
37
|
// No EL client info available. We still want to include CL info albeit not spec compliant
|
|
38
38
|
return `${consensusClientVersion.code}${consensusClientVersion.commit.slice(0, 4)}`;
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
function appendLongestFittingSuffix(userGraffiti: string, suffixes: string[]): string {
|
|
42
|
+
const userGraffitiBytes = Buffer.byteLength(userGraffiti, "utf8");
|
|
43
|
+
if (userGraffitiBytes >= GRAFFITI_SIZE) {
|
|
44
|
+
return userGraffiti;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const availableBytes = GRAFFITI_SIZE - userGraffitiBytes;
|
|
48
|
+
|
|
49
|
+
for (const suffix of suffixes) {
|
|
50
|
+
if (Buffer.byteLength(suffix, "utf8") <= availableBytes) {
|
|
51
|
+
return `${userGraffiti}${suffix}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return userGraffiti;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Appends the richest available client watermark that fits after user graffiti.
|
|
60
|
+
*
|
|
61
|
+
* Tiers are:
|
|
62
|
+
* - full EL/CL watermark, e.g. " BU9b0eLS80c2"
|
|
63
|
+
* - EL/CL client codes, e.g. " BULS"
|
|
64
|
+
* - CL client code, e.g. " LS"
|
|
65
|
+
*/
|
|
66
|
+
export function appendClientInfoToGraffiti(
|
|
67
|
+
userGraffiti: string,
|
|
68
|
+
consensusClientVersion: ClientVersion,
|
|
69
|
+
executionClientVersion: ClientVersion | null | undefined,
|
|
70
|
+
opts: {private?: boolean} = {}
|
|
71
|
+
): string {
|
|
72
|
+
if (opts.private) {
|
|
73
|
+
return userGraffiti;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Graffiti supplied via the beacon API is decoded from a fixed 32-byte field (see
|
|
77
|
+
// fromGraffitiHex) and arrives right-padded with NUL bytes. Trim only trailing padding
|
|
78
|
+
// NULs; a NUL that appears in the middle of the string is data, not padding.
|
|
79
|
+
let end = userGraffiti.length;
|
|
80
|
+
while (end > 0 && userGraffiti.charCodeAt(end - 1) === 0) {
|
|
81
|
+
end--;
|
|
82
|
+
}
|
|
83
|
+
const graffiti = userGraffiti.slice(0, end);
|
|
84
|
+
|
|
85
|
+
const fullClientInfo = getDefaultGraffiti(consensusClientVersion, executionClientVersion, {private: false});
|
|
86
|
+
if (graffiti.length === 0) {
|
|
87
|
+
return fullClientInfo;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const suffixes =
|
|
91
|
+
executionClientVersion != null
|
|
92
|
+
? [
|
|
93
|
+
` ${fullClientInfo}`,
|
|
94
|
+
` ${executionClientVersion.code}${consensusClientVersion.code}`,
|
|
95
|
+
` ${consensusClientVersion.code}`,
|
|
96
|
+
]
|
|
97
|
+
: [` ${fullClientInfo}`, ` ${consensusClientVersion.code}`];
|
|
98
|
+
|
|
99
|
+
return appendLongestFittingSuffix(graffiti, suffixes);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getBlockGraffiti(
|
|
103
|
+
userGraffiti: string | undefined,
|
|
104
|
+
consensusClientVersion: ClientVersion,
|
|
105
|
+
executionClientVersion: ClientVersion | null | undefined,
|
|
106
|
+
opts: {private?: boolean; graffitiAppend?: boolean}
|
|
107
|
+
): string {
|
|
108
|
+
if (userGraffiti === undefined) {
|
|
109
|
+
return getDefaultGraffiti(consensusClientVersion, executionClientVersion, opts);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (opts.graffitiAppend === false) {
|
|
113
|
+
return userGraffiti;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return appendClientInfoToGraffiti(userGraffiti, consensusClientVersion, executionClientVersion, opts);
|
|
117
|
+
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { RootHex, Slot, ValidatorIndex } from "@lodestar/types";
|
|
2
|
-
/**
|
|
3
|
-
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
4
|
-
*/
|
|
5
|
-
export declare class SeenProposerPreferences {
|
|
6
|
-
private readonly validatorByDependentRootBySlot;
|
|
7
|
-
isKnown(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean;
|
|
8
|
-
add(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): void;
|
|
9
|
-
/**
|
|
10
|
-
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
11
|
-
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
12
|
-
* on each slot tick.
|
|
13
|
-
*/
|
|
14
|
-
prune(currentSlot: Slot): void;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=seenProposerPreferences.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"seenProposerPreferences.d.ts","sourceRoot":"","sources":["../../../src/chain/seenCache/seenProposerPreferences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAG9D;;GAEG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAE7C;IAEF,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAE3F;IAED,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,GAAG,IAAI,CAEpF;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,EAAE,IAAI,GAAG,IAAI,CAM7B;CACF"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { MapDef } from "@lodestar/utils";
|
|
2
|
-
/**
|
|
3
|
-
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
4
|
-
*/
|
|
5
|
-
export class SeenProposerPreferences {
|
|
6
|
-
validatorByDependentRootBySlot = new MapDef(() => new Map());
|
|
7
|
-
isKnown(dependentRoot, proposalSlot, validatorIndex) {
|
|
8
|
-
return this.validatorByDependentRootBySlot.get(proposalSlot)?.get(dependentRoot) === validatorIndex;
|
|
9
|
-
}
|
|
10
|
-
add(dependentRoot, proposalSlot, validatorIndex) {
|
|
11
|
-
this.validatorByDependentRootBySlot.getOrDefault(proposalSlot).set(dependentRoot, validatorIndex);
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
15
|
-
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
16
|
-
* on each slot tick.
|
|
17
|
-
*/
|
|
18
|
-
prune(currentSlot) {
|
|
19
|
-
for (const slot of this.validatorByDependentRootBySlot.keys()) {
|
|
20
|
-
if (slot < currentSlot) {
|
|
21
|
-
this.validatorByDependentRootBySlot.delete(slot);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=seenProposerPreferences.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"seenProposerPreferences.js","sourceRoot":"","sources":["../../../src/chain/seenCache/seenProposerPreferences.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAEvC;;GAEG;AACH,MAAM,OAAO,uBAAuB;IACjB,8BAA8B,GAAG,IAAI,MAAM,CAC1D,GAAG,EAAE,CAAC,IAAI,GAAG,EAA2B,CACzC,CAAC;IAEF,OAAO,CAAC,aAAsB,EAAE,YAAkB,EAAE,cAA8B,EAAW;QAC3F,OAAO,IAAI,CAAC,8BAA8B,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,aAAa,CAAC,KAAK,cAAc,CAAC;IAAA,CACrG;IAED,GAAG,CAAC,aAAsB,EAAE,YAAkB,EAAE,cAA8B,EAAQ;QACpF,IAAI,CAAC,8BAA8B,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAAA,CACnG;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAiB,EAAQ;QAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,8BAA8B,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9D,IAAI,IAAI,GAAG,WAAW,EAAE,CAAC;gBACvB,IAAI,CAAC,8BAA8B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IAAA,CACF;CACF"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {RootHex, Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
-
import {MapDef} from "@lodestar/utils";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
6
|
-
*/
|
|
7
|
-
export class SeenProposerPreferences {
|
|
8
|
-
private readonly validatorByDependentRootBySlot = new MapDef<Slot, Map<RootHex, ValidatorIndex>>(
|
|
9
|
-
() => new Map<RootHex, ValidatorIndex>()
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
isKnown(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean {
|
|
13
|
-
return this.validatorByDependentRootBySlot.get(proposalSlot)?.get(dependentRoot) === validatorIndex;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
add(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): void {
|
|
17
|
-
this.validatorByDependentRootBySlot.getOrDefault(proposalSlot).set(dependentRoot, validatorIndex);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
22
|
-
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
23
|
-
* on each slot tick.
|
|
24
|
-
*/
|
|
25
|
-
prune(currentSlot: Slot): void {
|
|
26
|
-
for (const slot of this.validatorByDependentRootBySlot.keys()) {
|
|
27
|
-
if (slot < currentSlot) {
|
|
28
|
-
this.validatorByDependentRootBySlot.delete(slot);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|