@lodestar/beacon-node 1.41.0-dev.afd446235e → 1.41.0-dev.bb273175f2

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 (157) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +3 -2
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/debug/index.d.ts.map +1 -1
  5. package/lib/api/impl/debug/index.js +1 -0
  6. package/lib/api/impl/debug/index.js.map +1 -1
  7. package/lib/api/impl/validator/index.d.ts.map +1 -1
  8. package/lib/api/impl/validator/index.js +26 -10
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  11. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  12. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +3 -8
  13. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  14. package/lib/chain/archiveStore/utils/archiveBlocks.js +1 -1
  15. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  16. package/lib/chain/archiveStore/utils/updateBackfillRange.js +1 -1
  17. package/lib/chain/archiveStore/utils/updateBackfillRange.js.map +1 -1
  18. package/lib/chain/blocks/blockInput/blockInput.d.ts +20 -2
  19. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  20. package/lib/chain/blocks/blockInput/blockInput.js +47 -0
  21. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  22. package/lib/chain/blocks/blockInput/types.d.ts +13 -1
  23. package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
  24. package/lib/chain/blocks/blockInput/types.js +1 -0
  25. package/lib/chain/blocks/blockInput/types.js.map +1 -1
  26. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  27. package/lib/chain/blocks/importBlock.js +27 -4
  28. package/lib/chain/blocks/importBlock.js.map +1 -1
  29. package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts.map +1 -1
  30. package/lib/chain/blocks/verifyBlocksDataAvailability.js +3 -0
  31. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  32. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +4 -0
  33. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  34. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -1
  35. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  36. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  37. package/lib/chain/blocks/verifyBlocksSanityChecks.js +4 -1
  38. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  39. package/lib/chain/blocks/writeBlockInputToDb.d.ts +12 -3
  40. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  41. package/lib/chain/blocks/writeBlockInputToDb.js +101 -96
  42. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  43. package/lib/chain/chain.d.ts +1 -1
  44. package/lib/chain/chain.d.ts.map +1 -1
  45. package/lib/chain/chain.js +11 -11
  46. package/lib/chain/chain.js.map +1 -1
  47. package/lib/chain/emitter.d.ts +3 -3
  48. package/lib/chain/emitter.d.ts.map +1 -1
  49. package/lib/chain/errors/executionPayloadEnvelope.d.ts +2 -2
  50. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  51. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  52. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  53. package/lib/chain/forkChoice/index.js +30 -24
  54. package/lib/chain/forkChoice/index.js.map +1 -1
  55. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  56. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  57. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  58. package/lib/chain/produceBlock/produceBlockBody.js +1 -2
  59. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  60. package/lib/chain/regen/queued.d.ts.map +1 -1
  61. package/lib/chain/regen/queued.js +4 -1
  62. package/lib/chain/regen/queued.js.map +1 -1
  63. package/lib/chain/regen/regen.d.ts.map +1 -1
  64. package/lib/chain/regen/regen.js +6 -2
  65. package/lib/chain/regen/regen.js.map +1 -1
  66. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  67. package/lib/chain/seenCache/seenGossipBlockInput.js +15 -7
  68. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  69. package/lib/chain/validation/aggregateAndProof.js +1 -1
  70. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  71. package/lib/chain/validation/attestation.js +3 -3
  72. package/lib/chain/validation/attestation.js.map +1 -1
  73. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  74. package/lib/chain/validation/attesterSlashing.js +8 -1
  75. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  76. package/lib/chain/validation/blobSidecar.js +2 -2
  77. package/lib/chain/validation/blobSidecar.js.map +1 -1
  78. package/lib/chain/validation/block.d.ts.map +1 -1
  79. package/lib/chain/validation/block.js +6 -3
  80. package/lib/chain/validation/block.js.map +1 -1
  81. package/lib/chain/validation/dataColumnSidecar.js +1 -1
  82. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  83. package/lib/chain/validation/executionPayloadBid.js +1 -2
  84. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  85. package/lib/chain/validation/executionPayloadEnvelope.js +4 -4
  86. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  87. package/lib/chain/validation/payloadAttestationMessage.js +1 -2
  88. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  89. package/lib/constants/network.d.ts +1 -2
  90. package/lib/constants/network.d.ts.map +1 -1
  91. package/lib/constants/network.js +0 -1
  92. package/lib/constants/network.js.map +1 -1
  93. package/lib/network/peers/peerManager.d.ts +0 -8
  94. package/lib/network/peers/peerManager.d.ts.map +1 -1
  95. package/lib/network/peers/peerManager.js +3 -48
  96. package/lib/network/peers/peerManager.js.map +1 -1
  97. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  98. package/lib/network/processor/gossipHandlers.js +4 -1
  99. package/lib/network/processor/gossipHandlers.js.map +1 -1
  100. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  101. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +3 -2
  102. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  103. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  104. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +3 -2
  105. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  106. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +1 -1
  107. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
  108. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  109. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +3 -2
  110. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  111. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
  112. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  113. package/lib/util/clock.d.ts +6 -0
  114. package/lib/util/clock.d.ts.map +1 -1
  115. package/lib/util/clock.js +9 -3
  116. package/lib/util/clock.js.map +1 -1
  117. package/package.json +26 -26
  118. package/src/api/impl/beacon/blocks/index.ts +3 -2
  119. package/src/api/impl/debug/index.ts +1 -0
  120. package/src/api/impl/validator/index.ts +26 -12
  121. package/src/chain/archiveStore/archiveStore.ts +5 -5
  122. package/src/chain/archiveStore/utils/archiveBlocks.ts +4 -5
  123. package/src/chain/archiveStore/utils/updateBackfillRange.ts +1 -1
  124. package/src/chain/blocks/blockInput/blockInput.ts +68 -3
  125. package/src/chain/blocks/blockInput/types.ts +13 -0
  126. package/src/chain/blocks/importBlock.ts +35 -4
  127. package/src/chain/blocks/verifyBlocksDataAvailability.ts +3 -0
  128. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +9 -2
  129. package/src/chain/blocks/verifyBlocksSanityChecks.ts +7 -2
  130. package/src/chain/blocks/writeBlockInputToDb.ts +119 -101
  131. package/src/chain/chain.ts +26 -14
  132. package/src/chain/emitter.ts +3 -3
  133. package/src/chain/errors/executionPayloadEnvelope.ts +6 -2
  134. package/src/chain/forkChoice/index.ts +39 -21
  135. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  136. package/src/chain/produceBlock/produceBlockBody.ts +1 -2
  137. package/src/chain/regen/queued.ts +7 -2
  138. package/src/chain/regen/regen.ts +9 -3
  139. package/src/chain/seenCache/seenGossipBlockInput.ts +16 -7
  140. package/src/chain/validation/aggregateAndProof.ts +1 -1
  141. package/src/chain/validation/attestation.ts +3 -3
  142. package/src/chain/validation/attesterSlashing.ts +9 -0
  143. package/src/chain/validation/blobSidecar.ts +2 -2
  144. package/src/chain/validation/block.ts +9 -4
  145. package/src/chain/validation/dataColumnSidecar.ts +1 -1
  146. package/src/chain/validation/executionPayloadBid.ts +1 -2
  147. package/src/chain/validation/executionPayloadEnvelope.ts +4 -4
  148. package/src/chain/validation/payloadAttestationMessage.ts +1 -2
  149. package/src/constants/network.ts +0 -1
  150. package/src/network/peers/peerManager.ts +4 -61
  151. package/src/network/processor/gossipHandlers.ts +12 -3
  152. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +3 -2
  153. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +3 -2
  154. package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +1 -1
  155. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +3 -2
  156. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
  157. package/src/util/clock.ts +9 -4
@@ -3,6 +3,7 @@ import {CheckpointWithHex} from "@lodestar/fork-choice";
3
3
  import {
4
4
  ForkName,
5
5
  ForkPostFulu,
6
+ ForkPostGloas,
6
7
  ForkPreGloas,
7
8
  SLOTS_PER_EPOCH,
8
9
  isForkPostDeneb,
@@ -20,6 +21,7 @@ import {
20
21
  BlockInput,
21
22
  BlockInputBlobs,
22
23
  BlockInputColumns,
24
+ BlockInputNoData,
23
25
  BlockInputPreData,
24
26
  BlockWithSource,
25
27
  DAType,
@@ -179,12 +181,19 @@ export class SeenBlockInput {
179
181
  if (!blockInput) {
180
182
  const {forkName, daOutOfRange} = this.buildCommonProps(block.message.slot);
181
183
 
182
- // TODO GLOAS: Implement
183
184
  if (isForkPostGloas(forkName)) {
184
- throw Error("Not implemented");
185
- }
186
- // Pre-deneb
187
- if (!isForkPostDeneb(forkName)) {
185
+ // Post-gloas
186
+ blockInput = BlockInputNoData.createFromBlock({
187
+ block: block as SignedBeaconBlock<ForkPostGloas>,
188
+ blockRootHex,
189
+ daOutOfRange,
190
+ forkName,
191
+ source,
192
+ seenTimestampSec,
193
+ peerIdStr,
194
+ });
195
+ } else if (!isForkPostDeneb(forkName)) {
196
+ // Pre-deneb
188
197
  blockInput = BlockInputPreData.createFromBlock({
189
198
  block,
190
199
  blockRootHex,
@@ -194,8 +203,8 @@ export class SeenBlockInput {
194
203
  seenTimestampSec,
195
204
  peerIdStr,
196
205
  });
197
- // Fulu Only
198
206
  } else if (isForkPostFulu(forkName)) {
207
+ // Fulu Only
199
208
  blockInput = BlockInputColumns.createFromBlock({
200
209
  block: block as SignedBeaconBlock<ForkPostFulu & ForkPreGloas>,
201
210
  blockRootHex,
@@ -207,8 +216,8 @@ export class SeenBlockInput {
207
216
  seenTimestampSec,
208
217
  peerIdStr,
209
218
  });
210
- // Deneb and Electra
211
219
  } else {
220
+ // Deneb and Electra
212
221
  blockInput = BlockInputBlobs.createFromBlock({
213
222
  block: block as SignedBeaconBlock<ForkBlobsDA>,
214
223
  blockRootHex,
@@ -81,7 +81,7 @@ async function validateAggregateAndProof(
81
81
  });
82
82
  }
83
83
  // [REJECT] `aggregate.data.index == 0` if `block.slot == aggregate.data.slot`.
84
- const block = chain.forkChoice.getBlock(attData.beaconBlockRoot);
84
+ const block = chain.forkChoice.getBlockDefaultStatus(attData.beaconBlockRoot);
85
85
 
86
86
  // If block is unknown, we don't handle it here. It will throw error later on at `verifyHeadBlockAndTargetRoot()`
87
87
  if (block !== null && block.slot === attData.slot && attData.index !== 0) {
@@ -186,7 +186,7 @@ export async function validateGossipAttestationsSameAttData(
186
186
  chain.seenAttesters.add(targetEpoch, validatorIndex);
187
187
  } else {
188
188
  step0ResultOrErrors[oldIndex] = {
189
- err: new AttestationError(GossipAction.IGNORE, {
189
+ err: new AttestationError(GossipAction.REJECT, {
190
190
  code: AttestationErrorCode.INVALID_SIGNATURE,
191
191
  }),
192
192
  };
@@ -307,7 +307,7 @@ async function validateAttestationNoSignatureCheck(
307
307
  }
308
308
 
309
309
  // [REJECT] `attestation.data.index == 0` if `block.slot == attestation.data.slot`.
310
- const block = chain.forkChoice.getBlock(attData.beaconBlockRoot);
310
+ const block = chain.forkChoice.getBlockDefaultStatus(attData.beaconBlockRoot);
311
311
 
312
312
  // block being null will be handled by `verifyHeadBlockAndTargetRoot`
313
313
  if (block !== null && block.slot === attSlot && attData.index !== 0) {
@@ -756,7 +756,7 @@ export function getAttestationDataSigningRoot(config: BeaconConfig, data: phase0
756
756
  function verifyHeadBlockIsKnown(chain: IBeaconChain, beaconBlockRoot: Root): ProtoBlock {
757
757
  // TODO (LH): Enforce a maximum skip distance for unaggregated attestations.
758
758
 
759
- const headBlock = chain.forkChoice.getBlock(beaconBlockRoot);
759
+ const headBlock = chain.forkChoice.getBlockDefaultStatus(beaconBlockRoot);
760
760
  if (headBlock === null) {
761
761
  throw new AttestationError(GossipAction.IGNORE, {
762
762
  code: AttestationErrorCode.UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT,
@@ -2,6 +2,7 @@ import {
2
2
  assertValidAttesterSlashing,
3
3
  getAttesterSlashableIndices,
4
4
  getAttesterSlashingSignatureSets,
5
+ isSlashableValidator,
5
6
  } from "@lodestar/state-transition";
6
7
  import {AttesterSlashing} from "@lodestar/types";
7
8
  import {AttesterSlashingError, AttesterSlashingErrorCode, GossipAction} from "../errors/index.js";
@@ -58,6 +59,14 @@ export async function validateAttesterSlashing(
58
59
  });
59
60
  }
60
61
 
62
+ const currentEpoch = state.epochCtx.epoch;
63
+ if (!intersectingIndices.some((index) => isSlashableValidator(state.validators.getReadonly(index), currentEpoch))) {
64
+ throw new AttesterSlashingError(GossipAction.REJECT, {
65
+ code: AttesterSlashingErrorCode.INVALID,
66
+ error: Error("AttesterSlashing has no slashable validators"),
67
+ });
68
+ }
69
+
61
70
  const signatureSets = getAttesterSlashingSignatureSets(chain.config, state.slot, attesterSlashing);
62
71
  if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
63
72
  throw new AttesterSlashingError(GossipAction.REJECT, {
@@ -78,7 +78,7 @@ export async function validateGossipBlobSidecar(
78
78
  // already know this block.
79
79
  const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobSidecar.signedBlockHeader.message);
80
80
  const blockHex = toRootHex(blockRoot);
81
- if (chain.forkChoice.getBlockHex(blockHex) !== null) {
81
+ if (chain.forkChoice.getBlockHexDefaultStatus(blockHex) !== null) {
82
82
  throw new BlobSidecarGossipError(GossipAction.IGNORE, {code: BlobSidecarErrorCode.ALREADY_KNOWN, root: blockHex});
83
83
  }
84
84
 
@@ -89,7 +89,7 @@ export async function validateGossipBlobSidecar(
89
89
  // gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is
90
90
  // retrieved).
91
91
  const parentRoot = toRootHex(blobSidecar.signedBlockHeader.message.parentRoot);
92
- const parentBlock = chain.forkChoice.getBlockHex(parentRoot);
92
+ const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
93
93
  if (parentBlock === null) {
94
94
  // If fork choice does *not* consider the parent to be a descendant of the finalized block,
95
95
  // then there are two more cases:
@@ -9,7 +9,7 @@ import {
9
9
  isExecutionEnabled,
10
10
  isExecutionStateType,
11
11
  } from "@lodestar/state-transition";
12
- import {SignedBeaconBlock, deneb, gloas} from "@lodestar/types";
12
+ import {SignedBeaconBlock, deneb, gloas, isGloasBeaconBlock} from "@lodestar/types";
13
13
  import {byteArrayEquals, sleep, toRootHex} from "@lodestar/utils";
14
14
  import {BlockErrorCode, BlockGossipError, GossipAction} from "../errors/index.js";
15
15
  import {IBeaconChain} from "../interface.js";
@@ -56,7 +56,7 @@ export async function validateGossipBlock(
56
56
  // check, we will load the parent and state from disk only to find out later that we
57
57
  // already know this block.
58
58
  const blockRoot = toRootHex(config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block));
59
- if (chain.forkChoice.getBlockHex(blockRoot) !== null) {
59
+ if (chain.forkChoice.getBlockHexDefaultStatus(blockRoot) !== null) {
60
60
  throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
61
61
  }
62
62
 
@@ -72,7 +72,12 @@ export async function validateGossipBlock(
72
72
  // [REJECT] The current finalized_checkpoint is an ancestor of block -- i.e.
73
73
  // get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == store.finalized_checkpoint.root
74
74
  const parentRoot = toRootHex(block.parentRoot);
75
- const parentBlock = chain.forkChoice.getBlockHex(parentRoot);
75
+ const parentBlock = isGloasBeaconBlock(block)
76
+ ? chain.forkChoice.getBlockHexAndBlockHash(
77
+ parentRoot,
78
+ toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
79
+ )
80
+ : chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
76
81
  if (parentBlock === null) {
77
82
  // If fork choice does *not* consider the parent to be a descendant of the finalized block,
78
83
  // then there are two more cases:
@@ -104,7 +109,7 @@ export async function validateGossipBlock(
104
109
 
105
110
  // [REJECT] The block is from a higher slot than its parent.
106
111
  if (parentBlock.slot >= blockSlot) {
107
- throw new BlockGossipError(GossipAction.IGNORE, {
112
+ throw new BlockGossipError(GossipAction.REJECT, {
108
113
  code: BlockErrorCode.NOT_LATER_THAN_PARENT,
109
114
  parentSlot: parentBlock.slot,
110
115
  slot: blockSlot,
@@ -73,7 +73,7 @@ export async function validateGossipDataColumnSidecar(
73
73
  // 6) [IGNORE] The sidecar's block's parent (defined by block_header.parent_root) has been seen (via gossip
74
74
  // or non-gossip sources)
75
75
  const parentRoot = toRootHex(blockHeader.parentRoot);
76
- const parentBlock = chain.forkChoice.getBlockHex(parentRoot);
76
+ const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
77
77
  if (parentBlock === null) {
78
78
  // If fork choice does *not* consider the parent to be a descendant of the finalized block,
79
79
  // then there are two more cases:
@@ -113,8 +113,7 @@ async function validateExecutionPayloadBid(
113
113
 
114
114
  // [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon
115
115
  // block in fork choice.
116
- const block = chain.forkChoice.getBlock(bid.parentBlockRoot);
117
- if (block === null) {
116
+ if (!chain.forkChoice.hasBlock(bid.parentBlockRoot)) {
118
117
  throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
119
118
  code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
120
119
  parentBlockRoot: parentBlockRootHex,
@@ -37,7 +37,7 @@ async function validateExecutionPayloadEnvelope(
37
37
  // the block is retrieved).
38
38
  // TODO GLOAS: Need to review this, we should queue the envelope for later
39
39
  // processing if the block is not yet known, otherwise we would ignore it here
40
- const block = chain.forkChoice.getBlock(envelope.beaconBlockRoot);
40
+ const block = chain.forkChoice.getBlockDefaultStatus(envelope.beaconBlockRoot);
41
41
  if (block === null) {
42
42
  throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
43
43
  code: ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN,
@@ -79,7 +79,7 @@ async function validateExecutionPayloadEnvelope(
79
79
  });
80
80
  }
81
81
 
82
- if (block.builderIndex === undefined || block.blockHashHex === undefined) {
82
+ if (block.builderIndex == null || block.blockHashFromBid == null) {
83
83
  // This indicates this block is a pre-gloas block which is wrong
84
84
  throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
85
85
  code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
@@ -97,11 +97,11 @@ async function validateExecutionPayloadEnvelope(
97
97
  }
98
98
 
99
99
  // [REJECT] `payload.block_hash == bid.block_hash`
100
- if (toRootHex(payload.blockHash) !== block.blockHashHex) {
100
+ if (toRootHex(payload.blockHash) !== block.blockHashFromBid) {
101
101
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
102
102
  code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
103
103
  envelopeBlockHash: toRootHex(payload.blockHash),
104
- bidBlockHash: block.blockHashHex,
104
+ bidBlockHash: block.blockHashFromBid,
105
105
  });
106
106
  }
107
107
 
@@ -59,8 +59,7 @@ async function validatePayloadAttestationMessage(
59
59
  // [IGNORE] The message's block `data.beacon_block_root` has been seen (via
60
60
  // gossip or non-gossip sources) (a client MAY queue attestation for processing
61
61
  // once the block is retrieved. Note a client might want to request payload after).
62
- const block = chain.forkChoice.getBlock(data.beaconBlockRoot);
63
- if (block === null) {
62
+ if (!chain.forkChoice.hasBlock(data.beaconBlockRoot)) {
64
63
  throw new PayloadAttestationError(GossipAction.IGNORE, {
65
64
  code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
66
65
  blockRoot: toRootHex(data.beaconBlockRoot),
@@ -30,5 +30,4 @@ export const GOODBYE_KNOWN_CODES: Record<string, string> = {
30
30
  export enum Libp2pEvent {
31
31
  connectionOpen = "connection:open",
32
32
  connectionClose = "connection:close",
33
- peerIdentify = "peer:identify",
34
33
  }
@@ -1,4 +1,4 @@
1
- import {Connection, type IdentifyResult, PeerId, PrivateKey} from "@libp2p/interface";
1
+ import {Connection, PeerId, PrivateKey} from "@libp2p/interface";
2
2
  import {BitArray} from "@chainsafe/ssz";
3
3
  import {BeaconConfig} from "@lodestar/config";
4
4
  import {LoggerNode} from "@lodestar/logger/node";
@@ -162,9 +162,6 @@ export class PeerManager {
162
162
 
163
163
  // A single map of connected peers with all necessary data to handle PINGs, STATUS, and metrics
164
164
  private connectedPeers: Map<PeerIdStr, PeerData>;
165
- /** Track one in-flight identify call per peer/connection id */
166
- private readonly identifyInProgress = new Map<PeerIdStr, string>();
167
-
168
165
  private opts: PeerManagerOpts;
169
166
  private intervals: NodeJS.Timeout[] = [];
170
167
 
@@ -195,7 +192,6 @@ export class PeerManager {
195
192
 
196
193
  this.libp2p.services.components.events.addEventListener(Libp2pEvent.connectionOpen, this.onLibp2pPeerConnect);
197
194
  this.libp2p.services.components.events.addEventListener(Libp2pEvent.connectionClose, this.onLibp2pPeerDisconnect);
198
- this.libp2p.services.components.events.addEventListener(Libp2pEvent.peerIdentify, this.onPeerIdentify);
199
195
  this.networkEventBus.on(NetworkEvent.reqRespRequest, this.onRequest);
200
196
 
201
197
  this.lastStatus = this.statusCache.get();
@@ -239,7 +235,6 @@ export class PeerManager {
239
235
  Libp2pEvent.connectionClose,
240
236
  this.onLibp2pPeerDisconnect
241
237
  );
242
- this.libp2p.services.components.events.removeEventListener(Libp2pEvent.peerIdentify, this.onPeerIdentify);
243
238
  this.networkEventBus.off(NetworkEvent.reqRespRequest, this.onRequest);
244
239
  for (const interval of this.intervals) clearInterval(interval);
245
240
  }
@@ -490,25 +485,7 @@ export class PeerManager {
490
485
  // peers that close identify right after connection open or turn out to be
491
486
  // irrelevant.
492
487
  if (peerData?.agentVersion === null) {
493
- const peerIdStr = peer.toString();
494
- const connection = getConnection(this.libp2p, peerIdStr);
495
- if (!connection || connection.status !== "open") {
496
- this.logger.debug("Peer has no open connection for identify", {peerId: prettyPrintPeerId(peer)});
497
- return;
498
- }
499
-
500
- const identifyKey = connection.id;
501
- if (this.identifyInProgress.get(peerIdStr) === identifyKey) {
502
- return;
503
- }
504
-
505
- this.identifyInProgress.set(peerIdStr, identifyKey);
506
- void this.identifyPeer(peerIdStr, prettyPrintPeerId(peer), connection, identifyKey).finally(() => {
507
- // Clear only if this identify attempt is still the active one for this peer
508
- if (this.identifyInProgress.get(peerIdStr) === identifyKey) {
509
- this.identifyInProgress.delete(peerIdStr);
510
- }
511
- });
488
+ void this.identifyPeer(peer.toString(), prettyPrintPeerId(peer), getConnection(this.libp2p, peer.toString()));
512
489
  }
513
490
  }
514
491
  }
@@ -867,7 +844,6 @@ export class PeerManager {
867
844
 
868
845
  // remove the ping and status timer for the peer
869
846
  this.connectedPeers.delete(peerIdStr);
870
- this.identifyInProgress.delete(peerIdStr);
871
847
 
872
848
  this.logger.verbose(logMessage, logContext);
873
849
  this.networkEventBus.emit(NetworkEvent.peerDisconnected, {peer: peerIdStr});
@@ -885,47 +861,14 @@ export class PeerManager {
885
861
  }
886
862
  }
887
863
 
888
- /**
889
- * Consume successful identify results from libp2p events.
890
- * This captures agentVersion from identify-push or successful inbound/outbound identify,
891
- * even if our explicit identify request failed earlier.
892
- */
893
- private onPeerIdentify = (evt: CustomEvent<IdentifyResult>): void => {
894
- const {peerId, agentVersion} = evt.detail;
895
- if (!agentVersion) return;
896
-
897
- const peerIdStr = peerId.toString();
898
- const peerData = this.connectedPeers.get(peerIdStr);
899
- if (!peerData) return;
900
-
901
- peerData.agentVersion = agentVersion;
902
- peerData.agentClient = getKnownClientFromAgentVersion(agentVersion);
903
- this.identifyInProgress.delete(peerIdStr);
904
- };
905
-
906
- private async identifyPeer(
907
- peerIdStr: string,
908
- peerIdPretty: string,
909
- connection: Connection,
910
- identifyKey: string
911
- ): Promise<void> {
912
- if (this.identifyInProgress.get(peerIdStr) !== identifyKey) {
913
- return;
914
- }
915
-
916
- if (connection.status !== "open") {
864
+ private async identifyPeer(peerIdStr: string, peerIdPretty: string, connection?: Connection): Promise<void> {
865
+ if (!connection || connection.status !== "open") {
917
866
  this.logger.debug("Peer has no open connection for identify", {peerId: peerIdPretty});
918
867
  return;
919
868
  }
920
869
 
921
870
  try {
922
871
  const result = await this.libp2p.services.identify.identify(connection);
923
-
924
- // A newer identify attempt may have superseded this one (e.g. reconnect).
925
- if (this.identifyInProgress.get(peerIdStr) !== identifyKey) {
926
- return;
927
- }
928
-
929
872
  const agentVersion = result.agentVersion;
930
873
  if (agentVersion) {
931
874
  const connectedPeerData = this.connectedPeers.get(peerIdStr);
@@ -2,6 +2,7 @@ import {routes} from "@lodestar/api";
2
2
  import {BeaconConfig, ChainForkConfig} from "@lodestar/config";
3
3
  import {
4
4
  ForkName,
5
+ ForkPostDeneb,
5
6
  ForkPostElectra,
6
7
  ForkPreElectra,
7
8
  ForkSeq,
@@ -70,6 +71,7 @@ import {validateGossipPayloadAttestationMessage} from "../../chain/validation/pa
70
71
  import {OpSource} from "../../chain/validatorMonitor.js";
71
72
  import {Metrics} from "../../metrics/index.js";
72
73
  import {kzgCommitmentToVersionedHash} from "../../util/blobs.js";
74
+ import {getBlobKzgCommitments} from "../../util/dataColumns.ts";
73
75
  import {INetworkCore} from "../core/index.js";
74
76
  import {NetworkEventBus} from "../events.js";
75
77
  import {
@@ -417,9 +419,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
417
419
  chain.getBlobsTracker.triggerGetBlobs(blockInput);
418
420
  } else {
419
421
  metrics?.blockInputFetchStats.totalDataAvailableBlockInputs.inc();
420
- metrics?.blockInputFetchStats.totalDataAvailableBlockInputBlobs.inc(
421
- (signedBlock.message as deneb.BeaconBlock).body.blobKzgCommitments.length
422
- );
422
+ const blobCount = getBlobKzgCommitments(
423
+ blockInput.forkName,
424
+ signedBlock as SignedBeaconBlock<ForkPostDeneb>
425
+ ).length;
426
+ metrics?.blockInputFetchStats.totalDataAvailableBlockInputBlobs.inc(blobCount);
423
427
  }
424
428
 
425
429
  chain
@@ -852,6 +856,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
852
856
  } catch (e) {
853
857
  logger.error("Error adding to payloadAttestation pool", {}, e as Error);
854
858
  }
859
+ chain.forkChoice.notifyPtcMessages(
860
+ toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
861
+ [validationResult.validatorCommitteeIndex],
862
+ payloadAttestationMessage.data.payloadPresent
863
+ );
855
864
  },
856
865
  [GossipType.execution_payload_bid]: async ({
857
866
  gossipData,
@@ -47,9 +47,10 @@ export async function* onBeaconBlocksByRange(
47
47
 
48
48
  // Non-finalized range of blocks
49
49
  if (endSlot > finalizedSlot) {
50
- const headRoot = chain.forkChoice.getHeadRoot();
50
+ const headBlock = chain.forkChoice.getHead();
51
+ const headRoot = headBlock.blockRoot;
51
52
  // TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
52
- const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot);
53
+ const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
53
54
  // getAllAncestorBlocks response includes the head node, so it's the full chain.
54
55
 
55
56
  // Iterate head chain with ascending block numbers
@@ -34,9 +34,10 @@ export async function* onBlobSidecarsByRange(
34
34
 
35
35
  // Non-finalized range of blobs
36
36
  if (endSlot > finalizedSlot) {
37
- const headRoot = chain.forkChoice.getHeadRoot();
37
+ const headBlock = chain.forkChoice.getHead();
38
+ const headRoot = headBlock.blockRoot;
38
39
  // TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
39
- const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot);
40
+ const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
40
41
 
41
42
  // Iterate head chain with ascending block numbers
42
43
  for (let i = headChain.length - 1; i >= 0; i--) {
@@ -20,7 +20,7 @@ export async function* onBlobSidecarsByRoot(
20
20
  for (const blobIdentifier of requestBody) {
21
21
  const {blockRoot, index} = blobIdentifier;
22
22
  const blockRootHex = toRootHex(blockRoot);
23
- const block = chain.forkChoice.getBlockHex(blockRootHex);
23
+ const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex);
24
24
 
25
25
  // NOTE: Only support non-finalized blocks.
26
26
  // SPEC: Clients MUST support requesting blocks and sidecars since the latest finalized epoch.
@@ -78,8 +78,9 @@ export async function* onDataColumnSidecarsByRange(
78
78
 
79
79
  // Non-finalized range of columns
80
80
  if (endSlot > finalizedSlot) {
81
- const headRoot = chain.forkChoice.getHeadRoot();
82
- const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot);
81
+ const headBlock = chain.forkChoice.getHead();
82
+ const headRoot = headBlock.blockRoot;
83
+ const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
83
84
 
84
85
  // Iterate head chain with ascending block numbers
85
86
  for (let i = headChain.length - 1; i >= 0; i--) {
@@ -34,7 +34,7 @@ export async function* onDataColumnSidecarsByRoot(
34
34
  }
35
35
 
36
36
  const blockRootHex = toRootHex(blockRoot);
37
- const block = chain.forkChoice.getBlockHex(blockRootHex);
37
+ const block = chain.forkChoice.getBlockHexDefaultStatus(blockRootHex);
38
38
  // If the block is not in fork choice, it may be finalized. Attempt to find its slot in block archive
39
39
  const slot = block ? block.slot : await db.blockArchive.getSlotByRoot(blockRoot);
40
40
 
package/src/util/clock.ts CHANGED
@@ -92,15 +92,17 @@ export class Clock extends EventEmitter implements IClock {
92
92
  }
93
93
  return slot;
94
94
  }
95
-
96
95
  /**
97
96
  * If it's too close to next slot given MAXIMUM_GOSSIP_CLOCK_DISPARITY, return currentSlot + 1.
98
97
  * Otherwise return currentSlot
98
+ *
99
+ * Spec: phase0/p2p-interface.md - gossip validation uses `current_time + MAXIMUM_GOSSIP_CLOCK_DISPARITY < message_time`
100
+ * to reject future messages (strict `<`), so the boundary (exactly equal) is accepted, hence `<=` here.
99
101
  */
100
102
  get currentSlotWithGossipDisparity(): Slot {
101
103
  const currentSlot = this.currentSlot;
102
104
  const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
103
- return nextSlotTime - Date.now() < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
105
+ return nextSlotTime - Date.now() <= this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
104
106
  }
105
107
 
106
108
  get currentEpoch(): Epoch {
@@ -121,6 +123,9 @@ export class Clock extends EventEmitter implements IClock {
121
123
 
122
124
  /**
123
125
  * Check if a slot is current slot given MAXIMUM_GOSSIP_CLOCK_DISPARITY.
126
+ *
127
+ * Uses `<=` for disparity checks because the spec rejects with strict `<`
128
+ * (phase0/p2p-interface.md), meaning the boundary (exactly equal) is accepted.
124
129
  */
125
130
  isCurrentSlotGivenGossipDisparity(slot: Slot): boolean {
126
131
  const currentSlot = this.currentSlot;
@@ -129,12 +134,12 @@ export class Clock extends EventEmitter implements IClock {
129
134
  }
130
135
  const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
131
136
  // we're too close to next slot, accept next slot
132
- if (nextSlotTime - Date.now() < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
137
+ if (nextSlotTime - Date.now() <= this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
133
138
  return slot === currentSlot + 1;
134
139
  }
135
140
  const currentSlotTime = computeTimeAtSlot(this.config, currentSlot, this.genesisTime) * 1000;
136
141
  // we've just passed the current slot, accept previous slot
137
- if (Date.now() - currentSlotTime < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
142
+ if (Date.now() - currentSlotTime <= this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
138
143
  return slot === currentSlot - 1;
139
144
  }
140
145
  return false;