@lodestar/beacon-node 1.45.0-dev.e10fb5e991 → 1.45.0-dev.e6d38ecb03

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.
Files changed (34) hide show
  1. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/pool/index.js +2 -42
  3. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/state/index.js +46 -3
  6. package/lib/api/impl/beacon/state/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/utils.d.ts +11 -2
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/utils.js +39 -3
  10. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  11. package/lib/api/impl/validator/index.d.ts.map +1 -1
  12. package/lib/api/impl/validator/index.js +37 -3
  13. package/lib/api/impl/validator/index.js.map +1 -1
  14. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  15. package/lib/chain/produceBlock/produceBlockBody.js +4 -12
  16. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  17. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  18. package/lib/network/gossip/gossipsub.js +2 -1
  19. package/lib/network/gossip/gossipsub.js.map +1 -1
  20. package/lib/network/gossip/topic.d.ts +765 -3
  21. package/lib/network/gossip/topic.d.ts.map +1 -1
  22. package/lib/network/gossip/topic.js +23 -3
  23. package/lib/network/gossip/topic.js.map +1 -1
  24. package/lib/network/processor/gossipHandlers.js +2 -2
  25. package/lib/network/processor/gossipHandlers.js.map +1 -1
  26. package/package.json +14 -14
  27. package/src/api/impl/beacon/pool/index.ts +0 -52
  28. package/src/api/impl/beacon/state/index.ts +52 -1
  29. package/src/api/impl/beacon/state/utils.ts +57 -4
  30. package/src/api/impl/validator/index.ts +45 -2
  31. package/src/chain/produceBlock/produceBlockBody.ts +9 -17
  32. package/src/network/gossip/gossipsub.ts +2 -1
  33. package/src/network/gossip/topic.ts +28 -4
  34. package/src/network/processor/gossipHandlers.ts +1 -1
@@ -65,6 +65,8 @@ import {
65
65
  AttestationError,
66
66
  AttestationErrorCode,
67
67
  GossipAction,
68
+ ProposerPreferencesError,
69
+ ProposerPreferencesErrorCode,
68
70
  SyncCommitteeError,
69
71
  SyncCommitteeErrorCode,
70
72
  } from "../../../chain/errors/index.js";
@@ -74,6 +76,7 @@ import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/prod
74
76
  import {RegenCaller} from "../../../chain/regen/index.js";
75
77
  import {CheckpointHex} from "../../../chain/stateCache/types.js";
76
78
  import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
79
+ import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
77
80
  import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
78
81
  import {ZERO_HASH} from "../../../constants/index.js";
79
82
  import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
@@ -1118,8 +1121,8 @@ export function getValidatorApi(
1118
1121
 
1119
1122
  const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
1120
1123
  if (!block) {
1121
- // No block is seen at slot. Return 404 so vc can skip casting payload attestation.
1122
- throw new ApiError(404, `No canonical block found at slot=${slot}`);
1124
+ // No canonical block is seen at slot. Return 204 so vc can skip casting payload attestation.
1125
+ return {data: undefined, meta: {version: fork}, status: 204};
1123
1126
  }
1124
1127
 
1125
1128
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
@@ -1771,6 +1774,46 @@ export function getValidatorApi(
1771
1774
  });
1772
1775
  },
1773
1776
 
1777
+ async submitProposerPreferences({signedProposerPreferences}) {
1778
+ const failures: FailureList = [];
1779
+
1780
+ await Promise.all(
1781
+ signedProposerPreferences.map(async (signed, i) => {
1782
+ try {
1783
+ await validateGossipProposerPreferences(chain, signed);
1784
+
1785
+ chain.proposerPreferencesPool.add(signed);
1786
+ await network.publishProposerPreferences(signed);
1787
+ chain.emitter.emit(routes.events.EventType.proposerPreferences, {
1788
+ version: config.getForkName(signed.message.proposalSlot),
1789
+ data: signed,
1790
+ });
1791
+ } catch (e) {
1792
+ const logCtx = {
1793
+ slot: signed.message.proposalSlot,
1794
+ validatorIndex: signed.message.validatorIndex,
1795
+ dependentRoot: toRootHex(signed.message.dependentRoot),
1796
+ };
1797
+
1798
+ if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
1799
+ logger.debug("Ignoring known signed proposer preferences", logCtx);
1800
+ return;
1801
+ }
1802
+
1803
+ failures.push({index: i, message: (e as Error).message});
1804
+ logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e as Error);
1805
+ if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
1806
+ chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
1807
+ }
1808
+ }
1809
+ })
1810
+ );
1811
+
1812
+ if (failures.length > 0) {
1813
+ throw new IndexedError("Error processing signed proposer preferences", failures);
1814
+ }
1815
+ },
1816
+
1774
1817
  async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
1775
1818
  const fork = config.getForkName(slot);
1776
1819
 
@@ -43,6 +43,7 @@ import {
43
43
  ValidatorIndex,
44
44
  Wei,
45
45
  altair,
46
+ bellatrix,
46
47
  capella,
47
48
  deneb,
48
49
  electra,
@@ -830,28 +831,19 @@ export function getPayloadAttributesForSSE(
830
831
  feeRecipient,
831
832
  });
832
833
 
833
- let parentBlockNumber: number;
834
- if (isForkPostGloas(fork)) {
835
- const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
836
- toRootHex(parentBlockRoot),
837
- toRootHex(parentBlockHash)
838
- );
839
- if (parentBlock?.executionPayloadBlockHash == null) {
840
- throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
841
- }
842
- parentBlockNumber = parentBlock.executionPayloadNumber;
843
- } else {
844
- parentBlockNumber = prepareState.payloadBlockNumber;
845
- }
846
-
847
- const ssePayloadAttributes: SSEPayloadAttributes = {
834
+ const ssePayloadAttributes = {
848
835
  proposerIndex: prepareState.getBeaconProposer(prepareSlot),
849
836
  proposalSlot: prepareSlot,
850
- parentBlockNumber,
851
837
  parentBlockRoot,
852
838
  parentBlockHash,
853
839
  payloadAttributes,
854
- };
840
+ } as SSEPayloadAttributes;
841
+
842
+ if (!isForkPostGloas(fork)) {
843
+ // Removed in Gloas, builders can get the block number from the EL via the block hash if required
844
+ (ssePayloadAttributes as bellatrix.SSEPayloadAttributes).parentBlockNumber = prepareState.payloadBlockNumber;
845
+ }
846
+
855
847
  return ssePayloadAttributes;
856
848
  }
857
849
 
@@ -35,7 +35,7 @@ import {
35
35
  computeGossipPeerScoreParams,
36
36
  gossipScoreThresholds,
37
37
  } from "./scoringParameters.js";
38
- import {GossipTopicCache, getCoreTopicsAtFork, stringifyGossipTopic} from "./topic.js";
38
+ import {GossipTopicCache, getAllowedTopics, getCoreTopicsAtFork, stringifyGossipTopic} from "./topic.js";
39
39
 
40
40
  /** As specified in https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md */
41
41
  const GOSSIPSUB_HEARTBEAT_INTERVAL = 0.7 * 1000;
@@ -140,6 +140,7 @@ export class Eth2Gossipsub {
140
140
  const gossipsubInstance = gossipsub({
141
141
  globalSignaturePolicy: StrictNoSign,
142
142
  allowPublishToZeroTopicPeers: allowPublishToZeroPeers,
143
+ allowedTopics: getAllowedTopics(networkConfig),
143
144
  D: gossipsubD ?? GOSSIP_D,
144
145
  Dlo: gossipsubDLow ?? GOSSIP_D_LOW,
145
146
  Dhi: gossipsubDHigh ?? GOSSIP_D_HIGH,
@@ -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 = networkConfig.custodyConfig.sampledSubnets;
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
  }
@@ -1274,7 +1274,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1274
1274
  await validateGossipProposerPreferences(chain, signedProposerPreferences);
1275
1275
 
1276
1276
  chain.emitter.emit(routes.events.EventType.proposerPreferences, {
1277
- version: ForkName.gloas,
1277
+ version: config.getForkName(signedProposerPreferences.message.proposalSlot),
1278
1278
  data: signedProposerPreferences,
1279
1279
  });
1280
1280
  },