@lodestar/beacon-node 1.40.0-dev.4acd3ce568 → 1.40.0-dev.9defa5c09b

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 (251) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +8 -18
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/debug/index.d.ts +1 -1
  5. package/lib/api/impl/debug/index.d.ts.map +1 -1
  6. package/lib/api/impl/debug/index.js +3 -6
  7. package/lib/api/impl/debug/index.js.map +1 -1
  8. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  9. package/lib/api/impl/lodestar/index.js +5 -2
  10. package/lib/api/impl/lodestar/index.js.map +1 -1
  11. package/lib/api/impl/validator/index.d.ts.map +1 -1
  12. package/lib/api/impl/validator/index.js +9 -8
  13. package/lib/api/impl/validator/index.js.map +1 -1
  14. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/utils/archiveBlocks.js +4 -0
  16. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  17. package/lib/chain/blocks/blockInput/blockInput.d.ts +2 -0
  18. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  19. package/lib/chain/blocks/blockInput/blockInput.js +6 -0
  20. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  21. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  22. package/lib/chain/blocks/importBlock.js +2 -6
  23. package/lib/chain/blocks/importBlock.js.map +1 -1
  24. package/lib/chain/blocks/index.d.ts.map +1 -1
  25. package/lib/chain/blocks/index.js +0 -14
  26. package/lib/chain/blocks/index.js.map +1 -1
  27. package/lib/chain/blocks/types.d.ts +0 -2
  28. package/lib/chain/blocks/types.d.ts.map +1 -1
  29. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  30. package/lib/chain/blocks/verifyBlock.js +0 -7
  31. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  32. package/lib/chain/blocks/writeBlockInputToDb.d.ts +1 -4
  33. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  34. package/lib/chain/blocks/writeBlockInputToDb.js +20 -28
  35. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  36. package/lib/chain/chain.d.ts +21 -4
  37. package/lib/chain/chain.d.ts.map +1 -1
  38. package/lib/chain/chain.js +178 -13
  39. package/lib/chain/chain.js.map +1 -1
  40. package/lib/chain/errors/attestationError.d.ts +14 -1
  41. package/lib/chain/errors/attestationError.d.ts.map +1 -1
  42. package/lib/chain/errors/attestationError.js +8 -0
  43. package/lib/chain/errors/attestationError.js.map +1 -1
  44. package/lib/chain/errors/executionPayloadBid.d.ts +48 -0
  45. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -0
  46. package/lib/chain/errors/executionPayloadBid.js +15 -0
  47. package/lib/chain/errors/executionPayloadBid.js.map +1 -0
  48. package/lib/chain/errors/executionPayloadEnvelope.d.ts +48 -0
  49. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -0
  50. package/lib/chain/errors/executionPayloadEnvelope.js +16 -0
  51. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -0
  52. package/lib/chain/errors/index.d.ts +3 -0
  53. package/lib/chain/errors/index.d.ts.map +1 -1
  54. package/lib/chain/errors/index.js +3 -0
  55. package/lib/chain/errors/index.js.map +1 -1
  56. package/lib/chain/errors/payloadAttestation.d.ts +34 -0
  57. package/lib/chain/errors/payloadAttestation.d.ts.map +1 -0
  58. package/lib/chain/errors/payloadAttestation.js +13 -0
  59. package/lib/chain/errors/payloadAttestation.js.map +1 -0
  60. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  61. package/lib/chain/forkChoice/index.js +18 -0
  62. package/lib/chain/forkChoice/index.js.map +1 -1
  63. package/lib/chain/interface.d.ts +21 -3
  64. package/lib/chain/interface.d.ts.map +1 -1
  65. package/lib/chain/interface.js.map +1 -1
  66. package/lib/chain/opPools/executionPayloadBidPool.d.ts +21 -0
  67. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -0
  68. package/lib/chain/opPools/executionPayloadBidPool.js +57 -0
  69. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -0
  70. package/lib/chain/opPools/index.d.ts +2 -0
  71. package/lib/chain/opPools/index.d.ts.map +1 -1
  72. package/lib/chain/opPools/index.js +2 -0
  73. package/lib/chain/opPools/index.js.map +1 -1
  74. package/lib/chain/opPools/payloadAttestationPool.d.ts +24 -0
  75. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -0
  76. package/lib/chain/opPools/payloadAttestationPool.js +109 -0
  77. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -0
  78. package/lib/chain/prepareNextSlot.js +6 -4
  79. package/lib/chain/prepareNextSlot.js.map +1 -1
  80. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
  81. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  82. package/lib/chain/produceBlock/produceBlockBody.js +5 -3
  83. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  84. package/lib/chain/regen/interface.d.ts +3 -8
  85. package/lib/chain/regen/interface.d.ts.map +1 -1
  86. package/lib/chain/regen/interface.js +1 -1
  87. package/lib/chain/regen/interface.js.map +1 -1
  88. package/lib/chain/regen/queued.d.ts +1 -2
  89. package/lib/chain/regen/queued.d.ts.map +1 -1
  90. package/lib/chain/regen/queued.js +2 -16
  91. package/lib/chain/regen/queued.js.map +1 -1
  92. package/lib/chain/regen/regen.d.ts +3 -7
  93. package/lib/chain/regen/regen.d.ts.map +1 -1
  94. package/lib/chain/regen/regen.js +3 -16
  95. package/lib/chain/regen/regen.js.map +1 -1
  96. package/lib/chain/seenCache/index.d.ts +3 -1
  97. package/lib/chain/seenCache/index.d.ts.map +1 -1
  98. package/lib/chain/seenCache/index.js +3 -1
  99. package/lib/chain/seenCache/index.js.map +1 -1
  100. package/lib/chain/seenCache/seenAttesters.d.ts +5 -0
  101. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  102. package/lib/chain/seenCache/seenAttesters.js +5 -0
  103. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  104. package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts +12 -0
  105. package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts.map +1 -0
  106. package/lib/chain/seenCache/seenExecutionPayloadBids.js +30 -0
  107. package/lib/chain/seenCache/seenExecutionPayloadBids.js.map +1 -0
  108. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts +15 -0
  109. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts.map +1 -0
  110. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js +28 -0
  111. package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js.map +1 -0
  112. package/lib/chain/validation/aggregateAndProof.js +32 -10
  113. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  114. package/lib/chain/validation/attestation.d.ts.map +1 -1
  115. package/lib/chain/validation/attestation.js +23 -4
  116. package/lib/chain/validation/attestation.js.map +1 -1
  117. package/lib/chain/validation/blobSidecar.js +1 -1
  118. package/lib/chain/validation/blobSidecar.js.map +1 -1
  119. package/lib/chain/validation/block.d.ts.map +1 -1
  120. package/lib/chain/validation/block.js +4 -3
  121. package/lib/chain/validation/block.js.map +1 -1
  122. package/lib/chain/validation/dataColumnSidecar.js +1 -1
  123. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  124. package/lib/chain/validation/executionPayloadBid.d.ts +5 -0
  125. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -0
  126. package/lib/chain/validation/executionPayloadBid.js +103 -0
  127. package/lib/chain/validation/executionPayloadBid.js.map +1 -0
  128. package/lib/chain/validation/executionPayloadEnvelope.d.ts +5 -0
  129. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -0
  130. package/lib/chain/validation/executionPayloadEnvelope.js +89 -0
  131. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -0
  132. package/lib/chain/validation/payloadAttestationMessage.d.ts +9 -0
  133. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -0
  134. package/lib/chain/validation/payloadAttestationMessage.js +72 -0
  135. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -0
  136. package/lib/chain/validatorMonitor.d.ts +2 -0
  137. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  138. package/lib/chain/validatorMonitor.js +42 -3
  139. package/lib/chain/validatorMonitor.js.map +1 -1
  140. package/lib/db/repositories/checkpointState.js +0 -1
  141. package/lib/db/repositories/checkpointState.js.map +1 -1
  142. package/lib/metrics/metrics/lodestar.d.ts +27 -0
  143. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  144. package/lib/metrics/metrics/lodestar.js +64 -0
  145. package/lib/metrics/metrics/lodestar.js.map +1 -1
  146. package/lib/network/gossip/interface.d.ts +20 -2
  147. package/lib/network/gossip/interface.d.ts.map +1 -1
  148. package/lib/network/gossip/interface.js +3 -0
  149. package/lib/network/gossip/interface.js.map +1 -1
  150. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  151. package/lib/network/gossip/scoringParameters.js +38 -2
  152. package/lib/network/gossip/scoringParameters.js.map +1 -1
  153. package/lib/network/gossip/topic.d.ts +77 -1
  154. package/lib/network/gossip/topic.d.ts.map +1 -1
  155. package/lib/network/gossip/topic.js +20 -0
  156. package/lib/network/gossip/topic.js.map +1 -1
  157. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  158. package/lib/network/processor/gossipHandlers.js +34 -3
  159. package/lib/network/processor/gossipHandlers.js.map +1 -1
  160. package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
  161. package/lib/network/processor/gossipQueues/index.js +16 -0
  162. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  163. package/lib/network/processor/index.d.ts.map +1 -1
  164. package/lib/network/processor/index.js +3 -0
  165. package/lib/network/processor/index.js.map +1 -1
  166. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  167. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +2 -4
  168. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  169. package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts +1 -2
  170. package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts.map +1 -1
  171. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js +5 -26
  172. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js.map +1 -1
  173. package/lib/network/reqresp/handlers/blobSidecarsByRoot.d.ts +1 -2
  174. package/lib/network/reqresp/handlers/blobSidecarsByRoot.d.ts.map +1 -1
  175. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +5 -7
  176. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
  177. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  178. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +1 -2
  179. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  180. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  181. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -5
  182. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  183. package/lib/network/reqresp/handlers/index.js +2 -2
  184. package/lib/network/reqresp/handlers/index.js.map +1 -1
  185. package/lib/sync/range/chain.d.ts.map +1 -1
  186. package/lib/sync/range/chain.js +0 -1
  187. package/lib/sync/range/chain.js.map +1 -1
  188. package/lib/sync/range/range.d.ts.map +1 -1
  189. package/lib/sync/range/range.js +0 -3
  190. package/lib/sync/range/range.js.map +1 -1
  191. package/lib/sync/unknownBlock.d.ts.map +1 -1
  192. package/lib/sync/unknownBlock.js +0 -3
  193. package/lib/sync/unknownBlock.js.map +1 -1
  194. package/package.json +15 -15
  195. package/src/api/impl/beacon/blocks/index.ts +8 -18
  196. package/src/api/impl/debug/index.ts +2 -6
  197. package/src/api/impl/lodestar/index.ts +6 -3
  198. package/src/api/impl/validator/index.ts +12 -11
  199. package/src/chain/archiveStore/utils/archiveBlocks.ts +4 -0
  200. package/src/chain/blocks/blockInput/blockInput.ts +8 -0
  201. package/src/chain/blocks/importBlock.ts +2 -6
  202. package/src/chain/blocks/index.ts +0 -19
  203. package/src/chain/blocks/types.ts +0 -2
  204. package/src/chain/blocks/verifyBlock.ts +0 -8
  205. package/src/chain/blocks/writeBlockInputToDb.ts +24 -30
  206. package/src/chain/chain.ts +203 -21
  207. package/src/chain/errors/attestationError.ts +11 -1
  208. package/src/chain/errors/executionPayloadBid.ts +35 -0
  209. package/src/chain/errors/executionPayloadEnvelope.ts +34 -0
  210. package/src/chain/errors/index.ts +3 -0
  211. package/src/chain/errors/payloadAttestation.ts +25 -0
  212. package/src/chain/forkChoice/index.ts +19 -0
  213. package/src/chain/interface.ts +32 -1
  214. package/src/chain/opPools/executionPayloadBidPool.ts +77 -0
  215. package/src/chain/opPools/index.ts +2 -0
  216. package/src/chain/opPools/payloadAttestationPool.ts +157 -0
  217. package/src/chain/prepareNextSlot.ts +6 -6
  218. package/src/chain/produceBlock/produceBlockBody.ts +7 -5
  219. package/src/chain/regen/interface.ts +2 -12
  220. package/src/chain/regen/queued.ts +3 -23
  221. package/src/chain/regen/regen.ts +4 -24
  222. package/src/chain/seenCache/index.ts +3 -1
  223. package/src/chain/seenCache/seenAttesters.ts +5 -0
  224. package/src/chain/seenCache/seenExecutionPayloadBids.ts +35 -0
  225. package/src/chain/seenCache/seenExecutionPayloadEnvelope.ts +34 -0
  226. package/src/chain/validation/aggregateAndProof.ts +33 -10
  227. package/src/chain/validation/attestation.ts +24 -3
  228. package/src/chain/validation/blobSidecar.ts +1 -1
  229. package/src/chain/validation/block.ts +4 -3
  230. package/src/chain/validation/dataColumnSidecar.ts +1 -1
  231. package/src/chain/validation/executionPayloadBid.ts +140 -0
  232. package/src/chain/validation/executionPayloadEnvelope.ts +122 -0
  233. package/src/chain/validation/payloadAttestationMessage.ts +109 -0
  234. package/src/chain/validatorMonitor.ts +52 -3
  235. package/src/db/repositories/checkpointState.ts +1 -1
  236. package/src/metrics/metrics/lodestar.ts +65 -0
  237. package/src/network/gossip/interface.ts +17 -0
  238. package/src/network/gossip/scoringParameters.ts +44 -2
  239. package/src/network/gossip/topic.ts +21 -0
  240. package/src/network/processor/gossipHandlers.ts +48 -3
  241. package/src/network/processor/gossipQueues/index.ts +16 -0
  242. package/src/network/processor/index.ts +3 -0
  243. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +2 -4
  244. package/src/network/reqresp/handlers/beaconBlocksByRoot.ts +5 -32
  245. package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +5 -9
  246. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +5 -2
  247. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -5
  248. package/src/network/reqresp/handlers/index.ts +2 -2
  249. package/src/sync/range/chain.ts +0 -1
  250. package/src/sync/range/range.ts +0 -3
  251. package/src/sync/unknownBlock.ts +0 -3
@@ -71,11 +71,34 @@ async function validateAggregateAndProof(
71
71
  const attData = aggregate.data;
72
72
  const attSlot = attData.slot;
73
73
 
74
- let attIndex: number | null;
75
- if (ForkSeq[fork] >= ForkSeq.electra) {
76
- attIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
74
+ let committeeIndex: number | null;
75
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
76
+ // [REJECT] `aggregate.data.index < 2`.
77
+ if (attData.index >= 2) {
78
+ throw new AttestationError(GossipAction.REJECT, {
79
+ code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE,
80
+ attDataIndex: attData.index,
81
+ });
82
+ }
83
+ // [REJECT] `aggregate.data.index == 0` if `block.slot == aggregate.data.slot`.
84
+ const block = chain.forkChoice.getBlock(attData.beaconBlockRoot);
85
+
86
+ // If block is unknown, we don't handle it here. It will throw error later on at `verifyHeadBlockAndTargetRoot()`
87
+ if (block !== null && block.slot === attData.slot && attData.index !== 0) {
88
+ throw new AttestationError(GossipAction.REJECT, {
89
+ code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT,
90
+ });
91
+ }
92
+
93
+ // [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate)
94
+ committeeIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
95
+ if (committeeIndex === null) {
96
+ throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NOT_EXACTLY_ONE_COMMITTEE_BIT_SET});
97
+ }
98
+ } else if (ForkSeq[fork] >= ForkSeq.electra) {
99
+ committeeIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
77
100
  // [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate)
78
- if (attIndex === null) {
101
+ if (committeeIndex === null) {
79
102
  throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NOT_EXACTLY_ONE_COMMITTEE_BIT_SET});
80
103
  }
81
104
  // [REJECT] aggregate.data.index == 0
@@ -83,11 +106,11 @@ async function validateAggregateAndProof(
83
106
  throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX});
84
107
  }
85
108
  } else {
86
- attIndex = attData.index;
109
+ committeeIndex = attData.index;
87
110
  }
88
111
 
89
112
  const seenAttDataKey = serializedData ? getSeenAttDataKeyFromSignedAggregateAndProof(fork, serializedData) : null;
90
- const cachedAttData = seenAttDataKey ? chain.seenAttestationDatas.get(attSlot, attIndex, seenAttDataKey) : null;
113
+ const cachedAttData = seenAttDataKey ? chain.seenAttestationDatas.get(attSlot, committeeIndex, seenAttDataKey) : null;
91
114
 
92
115
  const attEpoch = computeEpochAtSlot(attSlot);
93
116
  const attTarget = attData.target;
@@ -136,7 +159,7 @@ async function validateAggregateAndProof(
136
159
  : toRootHex(ssz.phase0.AttestationData.hashTreeRoot(attData));
137
160
  if (
138
161
  !skipValidationKnownAttesters &&
139
- chain.seenAggregatedAttestations.isKnown(targetEpoch, attIndex, attDataRootHex, aggregationBits)
162
+ chain.seenAggregatedAttestations.isKnown(targetEpoch, committeeIndex, attDataRootHex, aggregationBits)
140
163
  ) {
141
164
  throw new AttestationError(GossipAction.IGNORE, {
142
165
  code: AttestationErrorCode.ATTESTERS_ALREADY_KNOWN,
@@ -177,7 +200,7 @@ async function validateAggregateAndProof(
177
200
  // -- i.e. data.index < get_committee_count_per_slot(state, data.target.epoch)
178
201
  const committeeValidatorIndices = cachedAttData
179
202
  ? cachedAttData.committeeValidatorIndices
180
- : getCommitteeValidatorIndices(shuffling, attSlot, attIndex);
203
+ : getCommitteeValidatorIndices(shuffling, attSlot, committeeIndex);
181
204
 
182
205
  // [REJECT] The number of aggregation bits matches the committee size
183
206
  // -- i.e. `len(aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, index))`.
@@ -248,7 +271,7 @@ async function validateAggregateAndProof(
248
271
  // Same race-condition check as above for seen aggregators
249
272
  if (
250
273
  !skipValidationKnownAttesters &&
251
- chain.seenAggregatedAttestations.isKnown(targetEpoch, attIndex, attDataRootHex, aggregationBits)
274
+ chain.seenAggregatedAttestations.isKnown(targetEpoch, committeeIndex, attDataRootHex, aggregationBits)
252
275
  ) {
253
276
  throw new AttestationError(GossipAction.IGNORE, {
254
277
  code: AttestationErrorCode.ATTESTERS_ALREADY_KNOWN,
@@ -260,7 +283,7 @@ async function validateAggregateAndProof(
260
283
  chain.seenAggregators.add(targetEpoch, aggregatorIndex);
261
284
  chain.seenAggregatedAttestations.add(
262
285
  targetEpoch,
263
- attIndex,
286
+ committeeIndex,
264
287
  attDataRootHex,
265
288
  {aggregationBits, trueBitCount: attestingIndices.length},
266
289
  false
@@ -10,6 +10,7 @@ import {
10
10
  ForkSeq,
11
11
  SLOTS_PER_EPOCH,
12
12
  isForkPostElectra,
13
+ isForkPostGloas,
13
14
  } from "@lodestar/params";
14
15
  import {
15
16
  EpochShuffling,
@@ -293,9 +294,29 @@ async function validateAttestationNoSignatureCheck(
293
294
  // api or first time validation of a gossip attestation
294
295
  committeeIndex = attestationOrCache.attestation.committeeIndex;
295
296
 
296
- // [REJECT] attestation.data.index == 0
297
- if (attData.index !== 0) {
298
- throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX});
297
+ if (isForkPostGloas(fork)) {
298
+ // [REJECT] `attestation.data.index < 2`.
299
+ if (attData.index >= 2) {
300
+ throw new AttestationError(GossipAction.REJECT, {
301
+ code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE,
302
+ attDataIndex: attData.index,
303
+ });
304
+ }
305
+
306
+ // [REJECT] `attestation.data.index == 0` if `block.slot == attestation.data.slot`.
307
+ const block = chain.forkChoice.getBlock(attData.beaconBlockRoot);
308
+
309
+ // block being null will be handled by `verifyHeadBlockAndTargetRoot`
310
+ if (block !== null && block.slot === attSlot && attData.index !== 0) {
311
+ throw new AttestationError(GossipAction.REJECT, {
312
+ code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT,
313
+ });
314
+ }
315
+ } else {
316
+ // [REJECT] attestation.data.index == 0
317
+ if (attData.index !== 0) {
318
+ throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX});
319
+ }
299
320
  }
300
321
  } else {
301
322
  // phase0 attestation
@@ -124,7 +124,7 @@ export async function validateGossipBlobSidecar(
124
124
  // [IGNORE] The block's parent (defined by block.parent_root) has been seen (via both gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is retrieved).
125
125
  // [REJECT] The block's parent (defined by block.parent_root) passes validation.
126
126
  const blockState = await chain.regen
127
- .getBlockSlotState(parentRoot, blobSlot, {dontTransferCache: true}, RegenCaller.validateGossipBlock)
127
+ .getBlockSlotState(parentBlock, blobSlot, {dontTransferCache: true}, RegenCaller.validateGossipBlock)
128
128
  .catch(() => {
129
129
  throw new BlobSidecarGossipError(GossipAction.IGNORE, {
130
130
  code: BlobSidecarErrorCode.PARENT_UNKNOWN,
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ForkName, isForkPostDeneb} from "@lodestar/params";
2
+ import {ForkName, isForkPostBellatrix, isForkPostDeneb, isForkPostGloas} from "@lodestar/params";
3
3
  import {
4
4
  computeEpochAtSlot,
5
5
  computeStartSlotAtEpoch,
@@ -111,7 +111,7 @@ export async function validateGossipBlock(
111
111
  }
112
112
 
113
113
  // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in Consensus Layer -- i.e. validate that len(body.signed_beacon_block.message.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK
114
- if (isForkPostDeneb(fork)) {
114
+ if (isForkPostDeneb(fork) && !isForkPostGloas(fork)) {
115
115
  const blobKzgCommitmentsLen = (block as deneb.BeaconBlock).body.blobKzgCommitments.length;
116
116
  const maxBlobsPerBlock = config.getMaxBlobsPerBlock(computeEpochAtSlot(blockSlot));
117
117
  if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
@@ -128,6 +128,7 @@ export async function validateGossipBlock(
128
128
  // this is something we should change this in the future to make the code airtight to the spec.
129
129
  // [IGNORE] The block's parent (defined by block.parent_root) has been seen (via both gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is retrieved).
130
130
  // [REJECT] The block's parent (defined by block.parent_root) passes validation.
131
+ // TODO GLOAS: post-gloas, we check the validity of bid's parent payload, not the entire beacon block
131
132
  const blockState = await chain.regen
132
133
  .getPreState(block, {dontTransferCache: true}, RegenCaller.validateGossipBlock)
133
134
  .catch(() => {
@@ -140,7 +141,7 @@ export async function validateGossipBlock(
140
141
  // Extra conditions for merge fork blocks
141
142
  // [REJECT] The block's execution payload timestamp is correct with respect to the slot
142
143
  // -- i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
143
- if (fork === ForkName.bellatrix) {
144
+ if (isForkPostBellatrix(fork) && !isForkPostGloas(fork)) {
144
145
  if (!isExecutionBlockBodyType(block.body)) throw Error("Not merge block type");
145
146
  const executionPayload = block.body.executionPayload;
146
147
  if (isExecutionStateType(blockState) && isExecutionEnabled(blockState, block)) {
@@ -106,7 +106,7 @@ export async function validateGossipDataColumnSidecar(
106
106
  // this is something we should change this in the future to make the code airtight to the spec.
107
107
  // 7) [REJECT] The sidecar's block's parent passes validation.
108
108
  const blockState = await chain.regen
109
- .getBlockSlotState(parentRoot, blockHeader.slot, {dontTransferCache: true}, RegenCaller.validateGossipDataColumn)
109
+ .getBlockSlotState(parentBlock, blockHeader.slot, {dontTransferCache: true}, RegenCaller.validateGossipDataColumn)
110
110
  .catch(() => {
111
111
  throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
112
112
  code: DataColumnSidecarErrorCode.PARENT_UNKNOWN,
@@ -0,0 +1,140 @@
1
+ import {PublicKey} from "@chainsafe/blst";
2
+ import {
3
+ CachedBeaconStateGloas,
4
+ canBuilderCoverBid,
5
+ createSingleSignatureSetFromComponents,
6
+ getExecutionPayloadBidSigningRoot,
7
+ isActiveBuilder,
8
+ } from "@lodestar/state-transition";
9
+ import {gloas} from "@lodestar/types";
10
+ import {toRootHex} from "@lodestar/utils";
11
+ import {ExecutionPayloadBidError, ExecutionPayloadBidErrorCode, GossipAction} from "../errors/index.js";
12
+ import {IBeaconChain} from "../index.js";
13
+ import {RegenCaller} from "../regen/index.js";
14
+
15
+ export async function validateApiExecutionPayloadBid(
16
+ chain: IBeaconChain,
17
+ signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
18
+ ): Promise<void> {
19
+ return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
20
+ }
21
+
22
+ export async function validateGossipExecutionPayloadBid(
23
+ chain: IBeaconChain,
24
+ signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
25
+ ): Promise<void> {
26
+ return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
27
+ }
28
+
29
+ async function validateExecutionPayloadBid(
30
+ chain: IBeaconChain,
31
+ signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
32
+ ): Promise<void> {
33
+ const bid = signedExecutionPayloadBid.message;
34
+ const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
35
+ const parentBlockHashHex = toRootHex(bid.parentBlockHash);
36
+ const state = (await chain.getHeadStateAtCurrentEpoch(
37
+ RegenCaller.validateGossipExecutionPayloadBid
38
+ )) as CachedBeaconStateGloas;
39
+
40
+ // [IGNORE] `bid.slot` is the current slot or the next slot.
41
+ const currentSlot = chain.clock.currentSlot;
42
+ if (bid.slot !== currentSlot && bid.slot !== currentSlot + 1) {
43
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
44
+ code: ExecutionPayloadBidErrorCode.INVALID_SLOT,
45
+ builderIndex: bid.builderIndex,
46
+ slot: bid.slot,
47
+ });
48
+ }
49
+
50
+ // [IGNORE] the `SignedProposerPreferences` where `preferences.proposal_slot`
51
+ // is equal to `bid.slot` has been seen.
52
+ // TODO GLOAS: Implement this along with proposer preference
53
+
54
+ // [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
55
+ // `is_active_builder(state, bid.builder_index)` returns `True`.
56
+ if (!isActiveBuilder(state, bid.builderIndex)) {
57
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
58
+ code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
59
+ builderIndex: bid.builderIndex,
60
+ });
61
+ }
62
+
63
+ // [REJECT] `bid.execution_payment` is zero.
64
+ if (bid.executionPayment !== 0) {
65
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
66
+ code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
67
+ builderIndex: bid.builderIndex,
68
+ executionPayment: bid.executionPayment,
69
+ });
70
+ }
71
+
72
+ // [REJECT] `bid.fee_recipient` matches the `fee_recipient` from the proposer's
73
+ // `SignedProposerPreferences` associated with `bid.slot`.
74
+ // [REJECT] `bid.gas_limit` matches the `gas_limit` from the proposer's
75
+ // `SignedProposerPreferences` associated with `bid.slot`.
76
+ // TODO GLOAS: Implement this along with proposer preference
77
+
78
+ // [IGNORE] this is the first signed bid seen with a valid signature from the given builder for this slot.
79
+ if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex)) {
80
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
81
+ code: ExecutionPayloadBidErrorCode.BID_ALREADY_KNOWN,
82
+ builderIndex: bid.builderIndex,
83
+ slot: bid.slot,
84
+ parentBlockRoot: parentBlockRootHex,
85
+ parentBlockHash: parentBlockHashHex,
86
+ });
87
+ }
88
+
89
+ // [IGNORE] this bid is the highest value bid seen for the corresponding slot
90
+ // and the given parent block hash.
91
+ const bestBid = chain.executionPayloadBidPool.getBestBid(parentBlockRootHex, parentBlockHashHex, bid.slot);
92
+ if (bestBid !== null && bestBid.value >= bid.value) {
93
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
94
+ code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
95
+ bidValue: bid.value,
96
+ currentHighestBid: bestBid.value,
97
+ });
98
+ }
99
+ // [IGNORE] `bid.value` is less or equal than the builder's excess balance --
100
+ // i.e. `can_builder_cover_bid(state, builder_index, amount)` returns `True`.
101
+ if (!canBuilderCoverBid(state, bid.builderIndex, bid.value)) {
102
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
103
+ code: ExecutionPayloadBidErrorCode.BID_TOO_HIGH,
104
+ bidValue: bid.value,
105
+ builderBalance: state.builders.getReadonly(bid.builderIndex).balance,
106
+ });
107
+ }
108
+
109
+ // [IGNORE] `bid.parent_block_hash` is the block hash of a known execution
110
+ // payload in fork choice.
111
+ // TODO GLOAS: implement this
112
+
113
+ // [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon
114
+ // block in fork choice.
115
+ const block = chain.forkChoice.getBlock(bid.parentBlockRoot);
116
+ if (block === null) {
117
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
118
+ code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
119
+ parentBlockRoot: parentBlockRootHex,
120
+ });
121
+ }
122
+
123
+ // [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
124
+ const signatureSet = createSingleSignatureSetFromComponents(
125
+ PublicKey.fromBytes(state.builders.getReadonly(bid.builderIndex).pubkey),
126
+ getExecutionPayloadBidSigningRoot(chain.config, state as CachedBeaconStateGloas, bid),
127
+ signedExecutionPayloadBid.signature
128
+ );
129
+
130
+ if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
131
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
132
+ code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE,
133
+ builderIndex: bid.builderIndex,
134
+ slot: bid.slot,
135
+ });
136
+ }
137
+
138
+ // Valid
139
+ chain.seenExecutionPayloadBids.add(bid.slot, bid.builderIndex);
140
+ }
@@ -0,0 +1,122 @@
1
+ import {PublicKey} from "@chainsafe/blst";
2
+ import {
3
+ CachedBeaconStateGloas,
4
+ computeStartSlotAtEpoch,
5
+ createSingleSignatureSetFromComponents,
6
+ getExecutionPayloadEnvelopeSigningRoot,
7
+ } from "@lodestar/state-transition";
8
+ import {gloas} from "@lodestar/types";
9
+ import {toRootHex} from "@lodestar/utils";
10
+ import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
11
+ import {IBeaconChain} from "../index.js";
12
+
13
+ export async function validateApiExecutionPayloadEnvelope(
14
+ chain: IBeaconChain,
15
+ executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope
16
+ ): Promise<void> {
17
+ return validateExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
18
+ }
19
+
20
+ export async function validateGossipExecutionPayloadEnvelope(
21
+ chain: IBeaconChain,
22
+ executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope
23
+ ): Promise<void> {
24
+ return validateExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
25
+ }
26
+
27
+ async function validateExecutionPayloadEnvelope(
28
+ chain: IBeaconChain,
29
+ executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope
30
+ ): Promise<void> {
31
+ const envelope = executionPayloadEnvelope.message;
32
+ const {payload} = envelope;
33
+ const blockRootHex = toRootHex(envelope.beaconBlockRoot);
34
+
35
+ // [IGNORE] The envelope's block root `envelope.block_root` has been seen (via
36
+ // gossip or non-gossip sources) (a client MAY queue payload for processing once
37
+ // the block is retrieved).
38
+ // TODO GLOAS: Need to review this
39
+ const block = chain.forkChoice.getBlock(envelope.beaconBlockRoot);
40
+ if (block === null) {
41
+ throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
42
+ code: ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN,
43
+ blockRoot: blockRootHex,
44
+ });
45
+ }
46
+
47
+ // [IGNORE] The node has not seen another valid
48
+ // `SignedExecutionPayloadEnvelope` for this block root from this builder.
49
+ if (chain.seenExecutionPayloadEnvelopes.isKnown(blockRootHex)) {
50
+ throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
51
+ code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
52
+ blockRoot: blockRootHex,
53
+ slot: envelope.slot,
54
+ });
55
+ }
56
+
57
+ // [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
58
+ const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
59
+ const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
60
+ if (envelope.slot < finalizedSlot) {
61
+ throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
62
+ code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK,
63
+ envelopeSlot: envelope.slot,
64
+ finalizedSlot,
65
+ });
66
+ }
67
+
68
+ // [REJECT] `block` passes validation.
69
+ // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
70
+ // it is possible that the block didn't pass the validation
71
+
72
+ // [REJECT] `block.slot` equals `envelope.slot`.
73
+ if (block.slot !== envelope.slot) {
74
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
75
+ code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
76
+ envelopeSlot: envelope.slot,
77
+ blockSlot: block.slot,
78
+ });
79
+ }
80
+
81
+ if (block.builderIndex === undefined || block.blockHashHex === undefined) {
82
+ // This indicates this block is a pre-gloas block which is wrong
83
+ throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
84
+ code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
85
+ blockRoot: blockRootHex,
86
+ });
87
+ }
88
+
89
+ // [REJECT] `envelope.builder_index == bid.builder_index`
90
+ if (envelope.builderIndex !== block.builderIndex) {
91
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
92
+ code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
93
+ envelopeBuilderIndex: envelope.builderIndex,
94
+ bidBuilderIndex: block.builderIndex,
95
+ });
96
+ }
97
+
98
+ // [REJECT] `payload.block_hash == bid.block_hash`
99
+ if (toRootHex(payload.blockHash) !== block.blockHashHex) {
100
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
101
+ code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
102
+ envelopeBlockHash: toRootHex(payload.blockHash),
103
+ bidBlockHash: block.blockHashHex,
104
+ });
105
+ }
106
+
107
+ // [REJECT] `signed_execution_payload_envelope.signature` is valid with respect to the builder's public key.
108
+ const state = chain.getHeadState() as CachedBeaconStateGloas;
109
+ const signatureSet = createSingleSignatureSetFromComponents(
110
+ PublicKey.fromBytes(state.builders.getReadonly(envelope.builderIndex).pubkey),
111
+ getExecutionPayloadEnvelopeSigningRoot(chain.config, envelope),
112
+ executionPayloadEnvelope.signature
113
+ );
114
+
115
+ if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
116
+ throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
117
+ code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
118
+ });
119
+ }
120
+
121
+ chain.seenExecutionPayloadEnvelopes.add(blockRootHex, envelope.slot);
122
+ }
@@ -0,0 +1,109 @@
1
+ import {
2
+ CachedBeaconStateGloas,
3
+ computeEpochAtSlot,
4
+ createSingleSignatureSetFromComponents,
5
+ getPayloadAttestationDataSigningRoot,
6
+ } from "@lodestar/state-transition";
7
+ import {RootHex, gloas, ssz} from "@lodestar/types";
8
+ import {toRootHex} from "@lodestar/utils";
9
+ import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
10
+ import {IBeaconChain} from "../index.js";
11
+
12
+ export type PayloadAttestationValidationResult = {
13
+ attDataRootHex: RootHex;
14
+ validatorCommitteeIndex: number;
15
+ };
16
+
17
+ export async function validateApiPayloadAttestationMessage(
18
+ chain: IBeaconChain,
19
+ payloadAttestationMessage: gloas.PayloadAttestationMessage
20
+ ): Promise<PayloadAttestationValidationResult> {
21
+ return validatePayloadAttestationMessage(chain, payloadAttestationMessage);
22
+ }
23
+
24
+ export async function validateGossipPayloadAttestationMessage(
25
+ chain: IBeaconChain,
26
+ payloadAttestationMessage: gloas.PayloadAttestationMessage
27
+ ): Promise<PayloadAttestationValidationResult> {
28
+ return validatePayloadAttestationMessage(chain, payloadAttestationMessage);
29
+ }
30
+
31
+ async function validatePayloadAttestationMessage(
32
+ chain: IBeaconChain,
33
+ payloadAttestationMessage: gloas.PayloadAttestationMessage
34
+ ): Promise<PayloadAttestationValidationResult> {
35
+ const {data, validatorIndex} = payloadAttestationMessage;
36
+ const epoch = computeEpochAtSlot(data.slot);
37
+
38
+ // [IGNORE] The message's slot is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance), i.e. `data.slot == current_slot`.
39
+ if (!chain.clock.isCurrentSlotGivenGossipDisparity(data.slot)) {
40
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
41
+ code: PayloadAttestationErrorCode.NOT_CURRENT_SLOT,
42
+ currentSlot: chain.clock.currentSlot,
43
+ slot: data.slot,
44
+ });
45
+ }
46
+
47
+ // [IGNORE] The `payload_attestation_message` is the first valid message received
48
+ // from the validator with index `payload_attestation_message.validator_index`.
49
+ // A single validator can participate PTC at most once per epoch
50
+ if (chain.seenPayloadAttesters.isKnown(epoch, validatorIndex)) {
51
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
52
+ code: PayloadAttestationErrorCode.PAYLOAD_ATTESTATION_ALREADY_KNOWN,
53
+ validatorIndex,
54
+ slot: data.slot,
55
+ blockRoot: toRootHex(data.beaconBlockRoot),
56
+ });
57
+ }
58
+
59
+ // [IGNORE] The message's block `data.beacon_block_root` has been seen (via
60
+ // gossip or non-gossip sources) (a client MAY queue attestation for processing
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) {
64
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
65
+ code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
66
+ blockRoot: toRootHex(data.beaconBlockRoot),
67
+ });
68
+ }
69
+
70
+ const state = chain.getHeadState() as CachedBeaconStateGloas;
71
+
72
+ // [REJECT] The message's block `data.beacon_block_root` passes validation.
73
+ // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
74
+ // it is possible that the block didn't pass the validation
75
+
76
+ // [REJECT] The message's validator index is within the payload committee in
77
+ // `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
78
+ // processing the block up to the current slot as determined by the fork choice.
79
+ const ptc = state.epochCtx.getPayloadTimelinessCommittee(data.slot);
80
+ const validatorCommitteeIndex = ptc.indexOf(validatorIndex);
81
+
82
+ if (validatorCommitteeIndex === -1) {
83
+ throw new PayloadAttestationError(GossipAction.REJECT, {
84
+ code: PayloadAttestationErrorCode.INVALID_ATTESTER,
85
+ attesterIndex: validatorIndex,
86
+ });
87
+ }
88
+
89
+ // [REJECT] `payload_attestation_message.signature` is valid with respect to the validator's public key.
90
+ const signatureSet = createSingleSignatureSetFromComponents(
91
+ chain.index2pubkey[validatorIndex],
92
+ getPayloadAttestationDataSigningRoot(state, data),
93
+ payloadAttestationMessage.signature
94
+ );
95
+
96
+ if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
97
+ throw new PayloadAttestationError(GossipAction.REJECT, {
98
+ code: PayloadAttestationErrorCode.INVALID_SIGNATURE,
99
+ });
100
+ }
101
+
102
+ // Valid
103
+ chain.seenPayloadAttesters.add(epoch, validatorIndex);
104
+
105
+ return {
106
+ attDataRootHex: toRootHex(ssz.gloas.PayloadAttestationData.hashTreeRoot(data)),
107
+ validatorCommitteeIndex,
108
+ };
109
+ }