@lodestar/beacon-node 1.51.0-dev.aa1ecb7c18 → 1.51.0-dev.ab8f3f6381
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/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +17 -0
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts +2 -2
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +35 -47
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +4 -0
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +24 -3
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +2 -2
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +4 -0
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/chain.d.ts +24 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +119 -6
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +6 -0
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts +11 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +11 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/forkChoiceExecutionEngine.d.ts +53 -0
- package/lib/chain/forkChoiceExecutionEngine.d.ts.map +1 -0
- package/lib/chain/forkChoiceExecutionEngine.js +121 -0
- package/lib/chain/forkChoiceExecutionEngine.js.map +1 -0
- package/lib/chain/interface.d.ts +7 -3
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +17 -4
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +177 -37
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/index.d.ts +1 -0
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +1 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/opPools/singleAttestationPool.d.ts +77 -0
- package/lib/chain/opPools/singleAttestationPool.d.ts.map +1 -0
- package/lib/chain/opPools/singleAttestationPool.js +209 -0
- package/lib/chain/opPools/singleAttestationPool.js.map +1 -0
- package/lib/chain/options.d.ts +5 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +3 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/proposerTracker.d.ts +31 -0
- package/lib/chain/proposerTracker.d.ts.map +1 -0
- package/lib/chain/proposerTracker.js +75 -0
- package/lib/chain/proposerTracker.js.map +1 -0
- package/lib/chain/regen/interface.d.ts +1 -1
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/queued.d.ts +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +5 -3
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/execution/engine/http.d.ts +2 -2
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +5 -4
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +16 -2
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcTransport.d.ts +2 -2
- package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -1
- package/lib/execution/engine/jsonRpcTransport.js +1 -1
- package/lib/execution/engine/jsonRpcTransport.js.map +1 -1
- package/lib/execution/engine/restTransport.d.ts +2 -2
- package/lib/execution/engine/restTransport.d.ts.map +1 -1
- package/lib/execution/engine/restTransport.js.map +1 -1
- package/lib/execution/engine/transport.d.ts +1 -12
- package/lib/execution/engine/transport.d.ts.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +26 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +69 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/core/networkCore.d.ts +2 -1
- package/lib/network/core/networkCore.d.ts.map +1 -1
- package/lib/network/core/networkCore.js +4 -0
- package/lib/network/core/networkCore.js.map +1 -1
- package/lib/network/core/networkCoreWorker.js +1 -0
- package/lib/network/core/networkCoreWorker.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.js +3 -0
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/core/types.d.ts +4 -1
- package/lib/network/core/types.d.ts.map +1 -1
- package/lib/network/network.d.ts +2 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +8 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/options.d.ts.map +1 -1
- package/lib/network/options.js +2 -0
- package/lib/network/options.js.map +1 -1
- package/lib/network/peers/utils/subnetMap.d.ts +5 -2
- package/lib/network/peers/utils/subnetMap.d.ts.map +1 -1
- package/lib/network/peers/utils/subnetMap.js +6 -3
- package/lib/network/peers/utils/subnetMap.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +20 -0
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/subnets/attnetsService.d.ts +35 -5
- package/lib/network/subnets/attnetsService.d.ts.map +1 -1
- package/lib/network/subnets/attnetsService.js +84 -6
- package/lib/network/subnets/attnetsService.js.map +1 -1
- package/lib/network/subnets/interface.d.ts +7 -0
- package/lib/network/subnets/interface.d.ts.map +1 -1
- package/lib/network/subnets/interface.js.map +1 -1
- package/lib/util/execution.d.ts +4 -4
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/pool/index.ts +26 -0
- package/src/chain/GetBlobsTracker.ts +2 -2
- package/src/chain/blocks/importBlock.ts +55 -67
- package/src/chain/blocks/importExecutionPayload.ts +42 -13
- package/src/chain/blocks/index.ts +5 -2
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +4 -1
- package/src/chain/chain.ts +134 -3
- package/src/chain/emitter.ts +7 -0
- package/src/chain/forkChoice/index.ts +11 -0
- package/src/chain/forkChoiceExecutionEngine.ts +197 -0
- package/src/chain/interface.ts +7 -2
- package/src/chain/opPools/aggregatedAttestationPool.ts +222 -35
- package/src/chain/opPools/index.ts +1 -0
- package/src/chain/opPools/singleAttestationPool.ts +287 -0
- package/src/chain/options.ts +5 -0
- package/src/chain/produceBlock/produceBlockBody.ts +7 -3
- package/src/chain/proposerTracker.ts +94 -0
- package/src/chain/regen/interface.ts +1 -1
- package/src/chain/regen/queued.ts +6 -4
- package/src/execution/engine/http.ts +12 -13
- package/src/execution/engine/interface.ts +18 -2
- package/src/execution/engine/jsonRpcTransport.ts +10 -2
- package/src/execution/engine/restTransport.ts +3 -1
- package/src/execution/engine/transport.ts +8 -14
- package/src/index.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +70 -0
- package/src/network/core/networkCore.ts +6 -1
- package/src/network/core/networkCoreWorker.ts +1 -0
- package/src/network/core/networkCoreWorkerHandler.ts +4 -1
- package/src/network/core/types.ts +4 -1
- package/src/network/network.ts +10 -0
- package/src/network/options.ts +2 -0
- package/src/network/peers/utils/subnetMap.ts +6 -3
- package/src/network/processor/gossipHandlers.ts +29 -0
- package/src/network/subnets/attnetsService.ts +94 -6
- package/src/network/subnets/interface.ts +7 -0
- package/src/util/execution.ts +6 -6
|
@@ -1347,8 +1347,67 @@ export function createLodestarMetrics(
|
|
|
1347
1347
|
name: "lodestar_oppool_aggregated_attestation_pool_packed_attestations_missing_shuffling_committees_total",
|
|
1348
1348
|
help: "Total number of committees skipped due to missing shuffling when producing packed attestations",
|
|
1349
1349
|
}),
|
|
1350
|
+
attestationValue: register.counter<{source: "aggregate" | "single"}>({
|
|
1351
|
+
name: "lodestar_oppool_packed_attestation_value_total",
|
|
1352
|
+
help: "Sum of new seen effective balance increments times participation flag weight of packed attestations, by source",
|
|
1353
|
+
labelNames: ["source"],
|
|
1354
|
+
}),
|
|
1355
|
+
blockNewSeenAttesters: register.gauge({
|
|
1356
|
+
name: "lodestar_oppool_packed_attestations_block_new_seen_attesters",
|
|
1357
|
+
help: "Total number of new seen attesters across all packed attestations of the last produced block",
|
|
1358
|
+
}),
|
|
1359
|
+
singlesValueRatio: register.histogram({
|
|
1360
|
+
name: "lodestar_oppool_packed_attestations_singles_value_ratio",
|
|
1361
|
+
help: "Ratio of attestation value contributed by single attestations per produced block",
|
|
1362
|
+
buckets: [0, 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1],
|
|
1363
|
+
}),
|
|
1364
|
+
singlesNewSeenAttesters: register.histogram({
|
|
1365
|
+
name: "lodestar_oppool_packed_attestations_singles_new_seen_attesters",
|
|
1366
|
+
help: "Number of attesters newly included only thanks to single attestations per produced block",
|
|
1367
|
+
buckets: [0, 1, 5, 10, 25, 50, 100, 250, 500, 1000, 1024],
|
|
1368
|
+
}),
|
|
1369
|
+
singlesNewSeenAttestersByInclusionDistance: register.gauge<{inclusionDistance: number}>({
|
|
1370
|
+
name: "lodestar_oppool_packed_attestations_singles_new_seen_attesters_by_inclusion_distance",
|
|
1371
|
+
help: "Number of attesters newly included only thanks to single attestations in the last produced block, by inclusion distance",
|
|
1372
|
+
labelNames: ["inclusionDistance"],
|
|
1373
|
+
}),
|
|
1374
|
+
singlesNewSeenEffectiveBalance: register.histogram({
|
|
1375
|
+
name: "lodestar_oppool_packed_attestations_singles_new_seen_effective_balance",
|
|
1376
|
+
help: "Effective balance increments of attesters newly included only thanks to single attestations per produced block",
|
|
1377
|
+
buckets: [0, 32, 160, 320, 800, 1600, 3200, 8000, 32000],
|
|
1378
|
+
}),
|
|
1379
|
+
singlesOnlyConsolidations: register.histogram({
|
|
1380
|
+
name: "lodestar_oppool_packed_attestations_singles_only_consolidations",
|
|
1381
|
+
help: "Number of packed attestations built only from single attestations per produced block",
|
|
1382
|
+
buckets: [0, 1, 2, 4, 8],
|
|
1383
|
+
}),
|
|
1384
|
+
singlesFoldedCommittees: register.histogram({
|
|
1385
|
+
name: "lodestar_oppool_packed_attestations_singles_folded_committees",
|
|
1386
|
+
help: "Number of committees in packed attestations that received single attestations per produced block",
|
|
1387
|
+
buckets: [0, 1, 2, 4, 8, 16, 32, 64],
|
|
1388
|
+
}),
|
|
1350
1389
|
},
|
|
1351
1390
|
},
|
|
1391
|
+
singleAttestationPool: {
|
|
1392
|
+
size: register.gauge({
|
|
1393
|
+
name: "lodestar_oppool_single_attestation_pool_size",
|
|
1394
|
+
help: "Current number of single attestations in the SingleAttestationPool",
|
|
1395
|
+
}),
|
|
1396
|
+
gossipInsertOutcome: register.counter<{insertOutcome: InsertOutcome}>({
|
|
1397
|
+
name: "lodestar_oppool_single_attestation_pool_gossip_insert_outcome_total",
|
|
1398
|
+
help: "Total number of InsertOutcome as a result of adding a single attestation from gossip to the SingleAttestationPool",
|
|
1399
|
+
labelNames: ["insertOutcome"],
|
|
1400
|
+
}),
|
|
1401
|
+
apiInsertOutcome: register.counter<{insertOutcome: InsertOutcome}>({
|
|
1402
|
+
name: "lodestar_oppool_single_attestation_pool_api_insert_outcome_total",
|
|
1403
|
+
help: "Total number of InsertOutcome as a result of adding a single attestation from api to the SingleAttestationPool",
|
|
1404
|
+
labelNames: ["insertOutcome"],
|
|
1405
|
+
}),
|
|
1406
|
+
deletedByBlock: register.counter({
|
|
1407
|
+
name: "lodestar_oppool_single_attestation_pool_deleted_by_block_total",
|
|
1408
|
+
help: "Total number of single attestations deleted because a canonical block includes them",
|
|
1409
|
+
}),
|
|
1410
|
+
},
|
|
1352
1411
|
attestationPool: {
|
|
1353
1412
|
size: register.gauge({
|
|
1354
1413
|
name: "lodestar_oppool_attestation_pool_size",
|
|
@@ -1497,6 +1556,17 @@ export function createLodestarMetrics(
|
|
|
1497
1556
|
},
|
|
1498
1557
|
},
|
|
1499
1558
|
|
|
1559
|
+
proposerTracker: {
|
|
1560
|
+
proposalSlots: register.gauge({
|
|
1561
|
+
name: "lodestar_proposer_tracker_proposal_slots",
|
|
1562
|
+
help: "Current number of upcoming block proposals of connected validators tracked by the ProposerTracker",
|
|
1563
|
+
}),
|
|
1564
|
+
collectableSlot: register.gauge({
|
|
1565
|
+
name: "lodestar_proposer_tracker_collectable_slot",
|
|
1566
|
+
help: "1 if single attestations of the clock slot are collected for an upcoming local proposal, 0 otherwise",
|
|
1567
|
+
}),
|
|
1568
|
+
},
|
|
1569
|
+
|
|
1500
1570
|
chain: {
|
|
1501
1571
|
blacklistedBlocks: register.gauge({
|
|
1502
1572
|
name: "lodestar_blacklisted_blocks_total",
|
|
@@ -8,7 +8,7 @@ import {BeaconConfig, ForkBoundary} from "@lodestar/config";
|
|
|
8
8
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
9
9
|
import {isForkPostFulu} from "@lodestar/params";
|
|
10
10
|
import {ResponseIncoming} from "@lodestar/reqresp";
|
|
11
|
-
import {Epoch, Status, fulu, sszTypesFor} from "@lodestar/types";
|
|
11
|
+
import {Epoch, Slot, Status, fulu, sszTypesFor} from "@lodestar/types";
|
|
12
12
|
import {formatNodePeer} from "../../api/impl/node/utils.js";
|
|
13
13
|
import {GoodByeReasonCode} from "../../constants/index.js";
|
|
14
14
|
import {RegistryMetricCreator} from "../../metrics/index.js";
|
|
@@ -345,6 +345,11 @@ export class NetworkCore implements INetworkCore {
|
|
|
345
345
|
if (subscriptions.length > 0) this.peerManager.onCommitteeSubscriptions();
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
async addLocalProposalSlots(slots: Slot[]): Promise<void> {
|
|
349
|
+
// no peerManager.onCommitteeSubscriptions(): the extra subscription relies on connected peers, no discovery
|
|
350
|
+
this.attnetsService.addLocalProposalSlots(slots);
|
|
351
|
+
}
|
|
352
|
+
|
|
348
353
|
async prepareSyncCommitteeSubnets(subscriptions: CommitteeSubscription[]): Promise<void> {
|
|
349
354
|
this.syncnetsService.addCommitteeSubscriptions(subscriptions);
|
|
350
355
|
if (subscriptions.length > 0) this.peerManager.onCommitteeSubscriptions();
|
|
@@ -136,6 +136,7 @@ const libp2pWorkerApi: NetworkWorkerApi = {
|
|
|
136
136
|
|
|
137
137
|
prepareBeaconCommitteeSubnets: (subscriptions) => core.prepareBeaconCommitteeSubnets(subscriptions),
|
|
138
138
|
prepareSyncCommitteeSubnets: (subscriptions) => core.prepareSyncCommitteeSubnets(subscriptions),
|
|
139
|
+
addLocalProposalSlots: (slots) => core.addLocalProposalSlots(slots),
|
|
139
140
|
reportPeer: (peer, action, actionName) => core.reportPeer(peer, action, actionName),
|
|
140
141
|
reStatusPeers: (peers) => core.reStatusPeers(peers),
|
|
141
142
|
goodbyeAndDisconnect: (peer, reason) => core.goodbyeAndDisconnect(peer, reason),
|
|
@@ -9,7 +9,7 @@ import {routes} from "@lodestar/api";
|
|
|
9
9
|
import {BeaconConfig, chainConfigToJson} from "@lodestar/config";
|
|
10
10
|
import type {LoggerNode} from "@lodestar/logger/node";
|
|
11
11
|
import {ResponseIncoming, ResponseOutgoing} from "@lodestar/reqresp";
|
|
12
|
-
import {Status} from "@lodestar/types";
|
|
12
|
+
import {Slot, Status} from "@lodestar/types";
|
|
13
13
|
import {withTimeout} from "@lodestar/utils";
|
|
14
14
|
import {GoodByeReasonCode} from "../../constants/index.js";
|
|
15
15
|
import {Metrics} from "../../metrics/index.js";
|
|
@@ -220,6 +220,9 @@ export class WorkerNetworkCore implements INetworkCore {
|
|
|
220
220
|
prepareSyncCommitteeSubnets(subscriptions: CommitteeSubscription[]): Promise<void> {
|
|
221
221
|
return this.getApi().prepareSyncCommitteeSubnets(subscriptions);
|
|
222
222
|
}
|
|
223
|
+
addLocalProposalSlots(slots: Slot[]): Promise<void> {
|
|
224
|
+
return this.getApi().addLocalProposalSlots(slots);
|
|
225
|
+
}
|
|
223
226
|
subscribeGossipCoreTopics(): Promise<void> {
|
|
224
227
|
return this.getApi().subscribeGossipCoreTopics();
|
|
225
228
|
}
|
|
@@ -4,7 +4,7 @@ import {routes} from "@lodestar/api";
|
|
|
4
4
|
import {SpecJson} from "@lodestar/config";
|
|
5
5
|
import {LoggerNodeOpts} from "@lodestar/logger/node";
|
|
6
6
|
import {ResponseIncoming} from "@lodestar/reqresp";
|
|
7
|
-
import {Status} from "@lodestar/types";
|
|
7
|
+
import {Slot, Status} from "@lodestar/types";
|
|
8
8
|
import {GoodByeReasonCode} from "../../constants/index.js";
|
|
9
9
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
10
10
|
import {NetworkOptions} from "../options.js";
|
|
@@ -62,6 +62,8 @@ export interface INetworkCore extends INetworkCorePublic {
|
|
|
62
62
|
|
|
63
63
|
/** Chain must push status updates to the network core */
|
|
64
64
|
updateStatus(status: Status): Promise<void>;
|
|
65
|
+
/** Block proposal slots of connected validators, used to subscribe to all attestation subnets before them */
|
|
66
|
+
addLocalProposalSlots(slots: Slot[]): Promise<void>;
|
|
65
67
|
|
|
66
68
|
setTargetGroupCount(count: number): Promise<void>;
|
|
67
69
|
/** Opens stream to handle ReqResp outgoing request */
|
|
@@ -111,6 +113,7 @@ export type NetworkWorkerApi = INetworkCorePublic & {
|
|
|
111
113
|
getConnectedPeers(): Promise<PeerIdStr[]>;
|
|
112
114
|
getConnectedPeerCount(): Promise<number>;
|
|
113
115
|
updateStatus(status: Status): Promise<void>;
|
|
116
|
+
addLocalProposalSlots(slots: Slot[]): Promise<void>;
|
|
114
117
|
|
|
115
118
|
setTargetGroupCount(count: number): Promise<void>;
|
|
116
119
|
|
package/src/network/network.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
SignedAggregateAndProof,
|
|
20
20
|
SignedBeaconBlock,
|
|
21
21
|
SingleAttestation,
|
|
22
|
+
Slot,
|
|
22
23
|
SlotRootHex,
|
|
23
24
|
SubnetID,
|
|
24
25
|
altair,
|
|
@@ -153,6 +154,7 @@ export class Network implements INetwork {
|
|
|
153
154
|
this.chain.emitter.on(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
|
|
154
155
|
this.chain.emitter.on(ChainEvent.publishProposerSlashing, this.onPublishProposerSlashing);
|
|
155
156
|
this.chain.emitter.on(ChainEvent.updateStatus, this.onUpdateStatus);
|
|
157
|
+
this.chain.emitter.on(ChainEvent.localProposalSlots, this.onLocalProposalSlots);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
static async init({
|
|
@@ -258,6 +260,7 @@ export class Network implements INetwork {
|
|
|
258
260
|
this.chain.emitter.off(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
|
|
259
261
|
this.chain.emitter.off(ChainEvent.publishProposerSlashing, this.onPublishProposerSlashing);
|
|
260
262
|
this.chain.emitter.off(ChainEvent.updateStatus, this.onUpdateStatus);
|
|
263
|
+
this.chain.emitter.off(ChainEvent.localProposalSlots, this.onLocalProposalSlots);
|
|
261
264
|
this.connectedPeersSyncMeta.clear();
|
|
262
265
|
this.subscribedToCoreTopics = false;
|
|
263
266
|
const results = await Promise.allSettled([this.networkProcessor.stop(), this.core.close()]);
|
|
@@ -285,6 +288,13 @@ export class Network implements INetwork {
|
|
|
285
288
|
return this.core.prepareBeaconCommitteeSubnets(subscriptions);
|
|
286
289
|
}
|
|
287
290
|
|
|
291
|
+
/** The core subscribes to all attestation subnets before these proposals, see `slotsToSubscribeBeforeProposalDuty` */
|
|
292
|
+
private onLocalProposalSlots = (proposalSlots: Slot[]): void => {
|
|
293
|
+
this.core.addLocalProposalSlots(proposalSlots).catch((e) => {
|
|
294
|
+
this.logger.debug("Error adding local proposal slots", {slots: proposalSlots.join(",")}, e as Error);
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
|
|
288
298
|
async prepareSyncCommitteeSubnets(subscriptions: CommitteeSubscription[]): Promise<void> {
|
|
289
299
|
return this.core.prepareSyncCommitteeSubnets(subscriptions);
|
|
290
300
|
}
|
package/src/network/options.ts
CHANGED
|
@@ -74,6 +74,8 @@ export const defaultNetworkOptions: NetworkOptions = {
|
|
|
74
74
|
maxYoungGenerationSizeMb: 152,
|
|
75
75
|
// subscribe 2 slots before aggregator dutied slot to get stable mesh peers as monitored on goerli
|
|
76
76
|
slotsToSubscribeBeforeAggregatorDuty: 2,
|
|
77
|
+
// up to the max number of attestations per block
|
|
78
|
+
slotsToSubscribeBeforeProposalDuty: 8,
|
|
77
79
|
// This will enable the light client server by default
|
|
78
80
|
disableLightClientServer: false,
|
|
79
81
|
quic: true,
|
|
@@ -66,11 +66,14 @@ export class SubnetMap {
|
|
|
66
66
|
return subnets;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Return subnetIds with a `toSlot` less than `currentSlot`. Also deletes expired entries
|
|
71
|
+
* @param keep expired subnets for which it returns true are neither returned nor deleted
|
|
72
|
+
*/
|
|
73
|
+
getExpired(currentSlot: Slot, keep?: (subnet: SubnetID) => boolean): SubnetID[] {
|
|
71
74
|
const subnetIds: SubnetID[] = [];
|
|
72
75
|
for (const [subnet, toSlot] of this.subnets.entries()) {
|
|
73
|
-
if (toSlot < currentSlot) {
|
|
76
|
+
if (toSlot < currentSlot && keep?.(subnet) !== true) {
|
|
74
77
|
subnetIds.push(subnet);
|
|
75
78
|
this.subnets.delete(subnet);
|
|
76
79
|
}
|
|
@@ -1377,6 +1377,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1377
1377
|
// Errors might indicate an issue with our node or the connected EL client
|
|
1378
1378
|
logLevel = LogLevel.error;
|
|
1379
1379
|
break;
|
|
1380
|
+
case PayloadErrorCode.BLOCK_EXECUTION_INVALID:
|
|
1381
|
+
logLevel = LogLevel.warn;
|
|
1382
|
+
break;
|
|
1380
1383
|
// INVALID_SIGNATURE only happens for an envelope added by another source
|
|
1381
1384
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1382
1385
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
@@ -1659,6 +1662,32 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp
|
|
|
1659
1662
|
logger.debug("Error adding gossip unaggregated attestation to pool", {subnet}, e as Error);
|
|
1660
1663
|
}
|
|
1661
1664
|
|
|
1665
|
+
// Collected only for vote slots that an upcoming block of a connected validator can include
|
|
1666
|
+
if (isForkPostElectra(fork) && chain.proposerTracker.shouldCollect(indexedAttestation.data.slot)) {
|
|
1667
|
+
try {
|
|
1668
|
+
const insertOutcome = chain.singleAttestationPool.add(
|
|
1669
|
+
attestation as SingleAttestation<ForkPostElectra>,
|
|
1670
|
+
attDataRootHex,
|
|
1671
|
+
validatorCommitteeIndex,
|
|
1672
|
+
committeeSize
|
|
1673
|
+
);
|
|
1674
|
+
metrics?.opPool.singleAttestationPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1675
|
+
} catch (e) {
|
|
1676
|
+
logger.debug(
|
|
1677
|
+
"Error adding gossip single attestation to pool",
|
|
1678
|
+
{
|
|
1679
|
+
slot: indexedAttestation.data.slot,
|
|
1680
|
+
committeeIndex,
|
|
1681
|
+
validatorCommitteeIndex,
|
|
1682
|
+
validatorIndex: (attestation as SingleAttestation<ForkPostElectra>).attesterIndex,
|
|
1683
|
+
subnet,
|
|
1684
|
+
attDataRoot: attDataRootHex,
|
|
1685
|
+
},
|
|
1686
|
+
e as Error
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1662
1691
|
// Separate boundary: a pool insertion error above must not skip the fork-choice vote
|
|
1663
1692
|
if (!options.dontSendGossipAttestationsToForkchoice) {
|
|
1664
1693
|
try {
|
|
@@ -20,6 +20,7 @@ const gossipType = GossipType.beacon_attestation;
|
|
|
20
20
|
export enum SubnetSource {
|
|
21
21
|
committee = "committee",
|
|
22
22
|
longLived = "long_lived",
|
|
23
|
+
beforeProposal = "before_proposal",
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
// map of subnet to time to form stable mesh as seconds, null if not yet formed
|
|
@@ -40,7 +41,7 @@ export class AttnetsService implements IAttnetsService {
|
|
|
40
41
|
/** Committee subnets - PeerManager must find peers for those */
|
|
41
42
|
private committeeSubnets = new SubnetMap();
|
|
42
43
|
/**
|
|
43
|
-
* All currently subscribed short-lived subnets, for attestation aggregation
|
|
44
|
+
* All currently subscribed short-lived subnets, for attestation aggregation and before local block proposals
|
|
44
45
|
* This class will tell gossip to subscribe and un-subscribe
|
|
45
46
|
* If a value exists for `SubscriptionId` it means that gossip subscription is active in network.gossip
|
|
46
47
|
*/
|
|
@@ -52,6 +53,11 @@ export class AttnetsService implements IAttnetsService {
|
|
|
52
53
|
* Used to determine if we should process an attestation.
|
|
53
54
|
*/
|
|
54
55
|
private aggregatorSlotSubnet = new MapDef<Slot, AggregatorDutyInfo>(() => new Map());
|
|
56
|
+
/**
|
|
57
|
+
* Block proposal slots of validators connected to this node.
|
|
58
|
+
* Used to subscribe to all subnets `slotsToSubscribeBeforeProposalDuty` slots before, see onSlot
|
|
59
|
+
*/
|
|
60
|
+
private localProposalSlots = new Set<Slot>();
|
|
55
61
|
|
|
56
62
|
constructor(
|
|
57
63
|
private readonly config: BeaconConfig,
|
|
@@ -119,6 +125,22 @@ export class AttnetsService implements IAttnetsService {
|
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Called from the main thread with upcoming block proposal slots of connected validators.
|
|
130
|
+
* Tracked here, the subscription happens in onSlot like aggregator duties, or right away for a proposal learned
|
|
131
|
+
* after its subscription slot (node restart, newly connected validator)
|
|
132
|
+
*/
|
|
133
|
+
addLocalProposalSlots(slots: Slot[]): void {
|
|
134
|
+
const clockSlot = this.clock.currentSlot;
|
|
135
|
+
for (const proposalSlot of slots) {
|
|
136
|
+
this.localProposalSlots.add(proposalSlot);
|
|
137
|
+
// not from the slot before the proposal, its votes are published before a mesh could form
|
|
138
|
+
if (proposalSlot - this.opts.slotsToSubscribeBeforeProposalDuty <= clockSlot && clockSlot < proposalSlot - 1) {
|
|
139
|
+
this.subscribeAllSubnetsUntilProposal(proposalSlot);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
122
144
|
/**
|
|
123
145
|
* Check if a subscription is still active before handling a gossip object
|
|
124
146
|
*/
|
|
@@ -158,7 +180,8 @@ export class AttnetsService implements IAttnetsService {
|
|
|
158
180
|
|
|
159
181
|
/**
|
|
160
182
|
* Run per slot.
|
|
161
|
-
* - Subscribe to gossip subnets 2 slots in advance
|
|
183
|
+
* - Subscribe to gossip subnets 2 slots in advance of aggregator duties
|
|
184
|
+
* - Subscribe to all subnets `slotsToSubscribeBeforeProposalDuty` slots in advance of local block proposals
|
|
162
185
|
* - Unsubscribe from expired subnets
|
|
163
186
|
* - Track time to stable mesh if not yet formed
|
|
164
187
|
*/
|
|
@@ -186,19 +209,76 @@ export class AttnetsService implements IAttnetsService {
|
|
|
186
209
|
this.trackTimeToStableMesh(clockSlot, dutiedSlot, dutiedInfo);
|
|
187
210
|
}
|
|
188
211
|
|
|
212
|
+
// Must run before unsubscribeExpiredCommitteeSubnets so that a subnet whose subscription expires in this slot
|
|
213
|
+
// gets its ttl extended instead of being unsubscribed and subscribed again
|
|
214
|
+
this.subscribeAllSubnetsBeforeProposal(clockSlot);
|
|
215
|
+
|
|
189
216
|
this.unsubscribeExpiredCommitteeSubnets(clockSlot);
|
|
190
217
|
this.pruneExpiredAggregator(clockSlot);
|
|
218
|
+
this.pruneExpiredLocalProposals(clockSlot);
|
|
191
219
|
} catch (e) {
|
|
192
220
|
this.logger.error("Error on AttnetsService.onSlot", {slot: clockSlot}, e as Error);
|
|
193
221
|
}
|
|
194
222
|
};
|
|
195
223
|
|
|
196
224
|
private onHalfSlot = (clockSlot: Slot): void => {
|
|
197
|
-
|
|
198
|
-
|
|
225
|
+
try {
|
|
226
|
+
for (const [dutiedSlot, dutiedInfo] of this.aggregatorSlotSubnet.entries()) {
|
|
227
|
+
this.trackTimeToStableMesh(clockSlot, dutiedSlot, dutiedInfo);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.unsubscribeAllSubnetsBeforeProposal(clockSlot);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
this.logger.error("Error on AttnetsService.onHalfSlot", {slot: clockSlot}, e as Error);
|
|
199
233
|
}
|
|
200
234
|
};
|
|
201
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Subscribe to all subnets `slotsToSubscribeBeforeProposalDuty` slots before a block proposal of a connected
|
|
238
|
+
* validator, so that single attestations of those slots can be packed into the block. The subscription expires in
|
|
239
|
+
* the slot before the proposal, see unsubscribeAllSubnetsBeforeProposal
|
|
240
|
+
*/
|
|
241
|
+
private subscribeAllSubnetsBeforeProposal(clockSlot: Slot): void {
|
|
242
|
+
for (const proposalSlot of this.localProposalSlots) {
|
|
243
|
+
if (proposalSlot === clockSlot + this.opts.slotsToSubscribeBeforeProposalDuty) {
|
|
244
|
+
this.subscribeAllSubnetsUntilProposal(proposalSlot);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private subscribeAllSubnetsUntilProposal(proposalSlot: Slot): void {
|
|
250
|
+
if (this.opts.slotsToSubscribeBeforeProposalDuty === 0 || this.opts.subscribeAllSubnets) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const subnets = Array.from({length: ATTESTATION_SUBNET_COUNT}, (_, subnet) => subnet);
|
|
255
|
+
// Same as aggregator duties: trigger gossip subscription first, then register the subscriptions.
|
|
256
|
+
// Do not add to committeeSubnets: it drives peer discovery, we only use the peers we already have
|
|
257
|
+
this.subscribeToSubnets(subnets, SubnetSource.beforeProposal);
|
|
258
|
+
for (const subnet of subnets) {
|
|
259
|
+
this.shortLivedSubscriptions.request({subnet, toSlot: proposalSlot - 1});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Unsubscribe from subnets of subscribeAllSubnetsBeforeProposal at half of the slot before the proposal instead of
|
|
265
|
+
* the proposal slot start, so the proposal slot does not carry their traffic. Single attestations are published at
|
|
266
|
+
* 1/3 of the slot so they have arrived by then.
|
|
267
|
+
* Short-lived subscriptions are either aggregator duties or these, an aggregator duty ends in its dutied slot and is
|
|
268
|
+
* kept here (aggregates are due later), it expires at the next slot start in onSlot as before. So only subnets of
|
|
269
|
+
* subscribeAllSubnetsBeforeProposal are unsubscribed here.
|
|
270
|
+
*/
|
|
271
|
+
private unsubscribeAllSubnetsBeforeProposal(clockSlot: Slot): void {
|
|
272
|
+
const aggregatorSubnets = this.aggregatorSlotSubnet.get(clockSlot);
|
|
273
|
+
const expiring = this.shortLivedSubscriptions.getExpired(
|
|
274
|
+
clockSlot + 1,
|
|
275
|
+
(subnet) => aggregatorSubnets?.has(subnet) === true
|
|
276
|
+
);
|
|
277
|
+
if (expiring.length > 0) {
|
|
278
|
+
this.unsubscribeSubnets(expiring, clockSlot + 1, SubnetSource.beforeProposal);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
202
282
|
/**
|
|
203
283
|
* Track time to form stable mesh if not yet formed
|
|
204
284
|
*/
|
|
@@ -291,8 +371,8 @@ export class AttnetsService implements IAttnetsService {
|
|
|
291
371
|
}
|
|
292
372
|
|
|
293
373
|
/**
|
|
294
|
-
* Unsubscribe
|
|
295
|
-
* If a
|
|
374
|
+
* Unsubscribe from short-lived subnets whose subscription expired.
|
|
375
|
+
* If a long-lived subnet is present, we do not unsubscribe from it.
|
|
296
376
|
*/
|
|
297
377
|
private unsubscribeExpiredCommitteeSubnets(slot: Slot): void {
|
|
298
378
|
const expired = this.shortLivedSubscriptions.getExpired(slot);
|
|
@@ -313,6 +393,14 @@ export class AttnetsService implements IAttnetsService {
|
|
|
313
393
|
}
|
|
314
394
|
}
|
|
315
395
|
|
|
396
|
+
private pruneExpiredLocalProposals(currentSlot: Slot): void {
|
|
397
|
+
for (const proposalSlot of this.localProposalSlots) {
|
|
398
|
+
if (currentSlot > proposalSlot) {
|
|
399
|
+
this.localProposalSlots.delete(proposalSlot);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
316
404
|
/** Update ENR */
|
|
317
405
|
private updateMetadata(): void {
|
|
318
406
|
const subnets = ssz.phase0.AttestationSubnets.defaultValue();
|
|
@@ -25,6 +25,8 @@ export type SubnetsService = {
|
|
|
25
25
|
|
|
26
26
|
export interface IAttnetsService extends SubnetsService {
|
|
27
27
|
shouldProcess(subnet: SubnetID, slot: Slot): boolean;
|
|
28
|
+
/** Block proposal slots of validators connected to this node, see `slotsToSubscribeBeforeProposalDuty` */
|
|
29
|
+
addLocalProposalSlots(slots: Slot[]): void;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export type RandBetweenFn = (min: number, max: number) => number;
|
|
@@ -33,6 +35,11 @@ export type ShuffleFn = <T>(arr: T[]) => T[];
|
|
|
33
35
|
export type SubnetsServiceOpts = {
|
|
34
36
|
subscribeAllSubnets?: boolean;
|
|
35
37
|
slotsToSubscribeBeforeAggregatorDuty: number;
|
|
38
|
+
/**
|
|
39
|
+
* Subscribe to all attestation subnets this many slots before a block proposal of a connected validator, so that
|
|
40
|
+
* its single attestations can be packed into the block. In `[0, SLOTS_PER_EPOCH]`, 0 disables
|
|
41
|
+
*/
|
|
42
|
+
slotsToSubscribeBeforeProposalDuty: number;
|
|
36
43
|
};
|
|
37
44
|
|
|
38
45
|
export type SubnetsServiceTestOpts = {
|
package/src/util/execution.ts
CHANGED
|
@@ -42,7 +42,7 @@ export enum DataColumnEngineResult {
|
|
|
42
42
|
|
|
43
43
|
export async function getBlobSidecarsFromExecution(
|
|
44
44
|
config: ChainForkConfig,
|
|
45
|
-
executionEngine: IExecutionEngine,
|
|
45
|
+
executionEngine: Pick<IExecutionEngine, "getBlobs">,
|
|
46
46
|
metrics: Metrics | null,
|
|
47
47
|
emitter: ChainEventEmitter,
|
|
48
48
|
blockInput: IBlockInput
|
|
@@ -140,7 +140,7 @@ export async function getBlobSidecarsFromExecution(
|
|
|
140
140
|
*/
|
|
141
141
|
export async function getDataColumnSidecarsFromExecution(
|
|
142
142
|
config: ChainForkConfig,
|
|
143
|
-
executionEngine: IExecutionEngine,
|
|
143
|
+
executionEngine: Pick<IExecutionEngine, "getBlobs">,
|
|
144
144
|
emitter: ChainEventEmitter,
|
|
145
145
|
input: IBlockInput | PayloadEnvelopeInput,
|
|
146
146
|
metrics: Metrics | null,
|
|
@@ -289,7 +289,7 @@ export type RebuildMiss = {slot: Slot; reason: "unavailable" | "mismatch"; error
|
|
|
289
289
|
*/
|
|
290
290
|
export async function* reconstructExecutionPayloadEnvelopesByRange(
|
|
291
291
|
db: IBeaconDb,
|
|
292
|
-
executionEngine: IExecutionEngine,
|
|
292
|
+
executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
|
|
293
293
|
logger: Logger,
|
|
294
294
|
metrics: Metrics | null,
|
|
295
295
|
startSlot: Slot,
|
|
@@ -315,7 +315,7 @@ export async function* reconstructExecutionPayloadEnvelopesByRange(
|
|
|
315
315
|
* RANGE_UNSERVABLE for it
|
|
316
316
|
*/
|
|
317
317
|
async function* reconstructBatch(
|
|
318
|
-
executionEngine: IExecutionEngine,
|
|
318
|
+
executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
|
|
319
319
|
logger: Logger,
|
|
320
320
|
metrics: Metrics | null,
|
|
321
321
|
batch: RangeEntry[]
|
|
@@ -355,7 +355,7 @@ async function* reconstructBatch(
|
|
|
355
355
|
* on its path. Throws ENGINE_UNAVAILABLE only.
|
|
356
356
|
*/
|
|
357
357
|
export async function reconstructExecutionPayloadEnvelopes(
|
|
358
|
-
executionEngine: IExecutionEngine,
|
|
358
|
+
executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
|
|
359
359
|
metrics: Metrics | null,
|
|
360
360
|
headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[]
|
|
361
361
|
): Promise<(gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[]> {
|
|
@@ -382,7 +382,7 @@ export function isRebuildMiss(result: gloas.SignedExecutionPayloadEnvelope | Reb
|
|
|
382
382
|
* incremented once, in this function.
|
|
383
383
|
*/
|
|
384
384
|
async function reconstructEnvelopesBatch(
|
|
385
|
-
executionEngine: IExecutionEngine,
|
|
385
|
+
executionEngine: Pick<IExecutionEngine, "getPayloadBodiesByHashV2">,
|
|
386
386
|
metrics: Metrics | null,
|
|
387
387
|
headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[]
|
|
388
388
|
): Promise<(gloas.SignedExecutionPayloadEnvelope | RebuildMiss)[]> {
|