@lodestar/beacon-node 1.50.0-dev.814fe8c461 → 1.50.0-dev.82f05c43ff

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 (246) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +6 -1
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/index.js +13 -0
  6. package/lib/api/impl/beacon/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/state/index.js +23 -3
  9. package/lib/api/impl/beacon/state/index.js.map +1 -1
  10. package/lib/api/impl/debug/index.d.ts.map +1 -1
  11. package/lib/api/impl/debug/index.js +26 -18
  12. package/lib/api/impl/debug/index.js.map +1 -1
  13. package/lib/api/impl/validator/index.d.ts +1 -0
  14. package/lib/api/impl/validator/index.d.ts.map +1 -1
  15. package/lib/api/impl/validator/index.js +23 -1
  16. package/lib/api/impl/validator/index.js.map +1 -1
  17. package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
  18. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.js +14 -24
  20. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  21. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
  22. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
  24. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
  26. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
  28. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  29. package/lib/chain/archiveStore/historicalState/worker.js +1 -1
  30. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  31. package/lib/chain/archiveStore/interface.d.ts +1 -1
  32. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  33. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
  34. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  35. package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
  36. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  37. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  38. package/lib/chain/blocks/importBlock.js +11 -3
  39. package/lib/chain/blocks/importBlock.js.map +1 -1
  40. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  41. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  42. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  43. package/lib/chain/blocks/verifyBlock.js +1 -1
  44. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  45. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
  46. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  47. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
  48. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  49. package/lib/chain/chain.d.ts +9 -5
  50. package/lib/chain/chain.d.ts.map +1 -1
  51. package/lib/chain/chain.js +53 -18
  52. package/lib/chain/chain.js.map +1 -1
  53. package/lib/chain/emitter.d.ts +7 -1
  54. package/lib/chain/emitter.d.ts.map +1 -1
  55. package/lib/chain/emitter.js +5 -0
  56. package/lib/chain/emitter.js.map +1 -1
  57. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  58. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  59. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  60. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  61. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  62. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  63. package/lib/chain/errors/gossipValidation.js +2 -0
  64. package/lib/chain/errors/gossipValidation.js.map +1 -1
  65. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  66. package/lib/chain/forkChoice/index.js +2 -0
  67. package/lib/chain/forkChoice/index.js.map +1 -1
  68. package/lib/chain/interface.d.ts +3 -2
  69. package/lib/chain/interface.d.ts.map +1 -1
  70. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
  71. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  72. package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
  73. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  74. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
  75. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  76. package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
  77. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  78. package/lib/chain/options.d.ts +2 -1
  79. package/lib/chain/options.d.ts.map +1 -1
  80. package/lib/chain/options.js +1 -1
  81. package/lib/chain/options.js.map +1 -1
  82. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  83. package/lib/chain/prepareNextSlot.js +5 -2
  84. package/lib/chain/prepareNextSlot.js.map +1 -1
  85. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
  86. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  87. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  88. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  89. package/lib/chain/produceBlock/produceBlockBody.js +6 -2
  90. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  91. package/lib/chain/regen/regen.d.ts +2 -0
  92. package/lib/chain/regen/regen.d.ts.map +1 -1
  93. package/lib/chain/regen/regen.js +10 -4
  94. package/lib/chain/regen/regen.js.map +1 -1
  95. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
  96. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  97. package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
  98. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  99. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
  100. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  101. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
  102. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  103. package/lib/chain/shufflingCache.d.ts +1 -0
  104. package/lib/chain/shufflingCache.d.ts.map +1 -1
  105. package/lib/chain/shufflingCache.js +4 -0
  106. package/lib/chain/shufflingCache.js.map +1 -1
  107. package/lib/chain/validatorMonitor.d.ts +5 -1
  108. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  109. package/lib/chain/validatorMonitor.js +64 -54
  110. package/lib/chain/validatorMonitor.js.map +1 -1
  111. package/lib/db/beacon.d.ts +2 -0
  112. package/lib/db/beacon.d.ts.map +1 -1
  113. package/lib/db/beacon.js +3 -0
  114. package/lib/db/beacon.js.map +1 -1
  115. package/lib/db/buckets.d.ts +2 -1
  116. package/lib/db/buckets.d.ts.map +1 -1
  117. package/lib/db/buckets.js +1 -0
  118. package/lib/db/buckets.js.map +1 -1
  119. package/lib/db/interface.d.ts +2 -0
  120. package/lib/db/interface.d.ts.map +1 -1
  121. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  122. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  123. package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
  124. package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
  125. package/lib/db/repositories/earliestAvailableSlot.js +19 -0
  126. package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
  127. package/lib/execution/engine/disabled.d.ts +0 -2
  128. package/lib/execution/engine/disabled.d.ts.map +1 -1
  129. package/lib/execution/engine/disabled.js +0 -6
  130. package/lib/execution/engine/disabled.js.map +1 -1
  131. package/lib/execution/engine/http.d.ts +1 -3
  132. package/lib/execution/engine/http.d.ts.map +1 -1
  133. package/lib/execution/engine/http.js +2 -17
  134. package/lib/execution/engine/http.js.map +1 -1
  135. package/lib/execution/engine/interface.d.ts +2 -4
  136. package/lib/execution/engine/interface.d.ts.map +1 -1
  137. package/lib/execution/engine/mock.d.ts +0 -2
  138. package/lib/execution/engine/mock.d.ts.map +1 -1
  139. package/lib/execution/engine/mock.js +0 -8
  140. package/lib/execution/engine/mock.js.map +1 -1
  141. package/lib/execution/engine/types.d.ts +0 -8
  142. package/lib/execution/engine/types.d.ts.map +1 -1
  143. package/lib/execution/engine/types.js.map +1 -1
  144. package/lib/metrics/metrics/lodestar.d.ts +1 -0
  145. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  146. package/lib/metrics/metrics/lodestar.js +4 -0
  147. package/lib/metrics/metrics/lodestar.js.map +1 -1
  148. package/lib/metrics/metrics.d.ts +7 -3
  149. package/lib/metrics/metrics.d.ts.map +1 -1
  150. package/lib/metrics/metrics.js +4 -4
  151. package/lib/metrics/metrics.js.map +1 -1
  152. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  153. package/lib/network/processor/gossipHandlers.js +62 -63
  154. package/lib/network/processor/gossipHandlers.js.map +1 -1
  155. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  156. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  157. package/lib/network/processor/gossipValidatorFn.js +7 -3
  158. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  159. package/lib/network/processor/index.d.ts +9 -0
  160. package/lib/network/processor/index.d.ts.map +1 -1
  161. package/lib/network/processor/index.js +77 -10
  162. package/lib/network/processor/index.js.map +1 -1
  163. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +1 -1
  164. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  165. package/lib/node/nodejs.d.ts +3 -1
  166. package/lib/node/nodejs.d.ts.map +1 -1
  167. package/lib/node/nodejs.js +27 -4
  168. package/lib/node/nodejs.js.map +1 -1
  169. package/lib/sync/range/batch.d.ts +6 -0
  170. package/lib/sync/range/batch.d.ts.map +1 -1
  171. package/lib/sync/range/batch.js +11 -0
  172. package/lib/sync/range/batch.js.map +1 -1
  173. package/lib/sync/range/chain.d.ts.map +1 -1
  174. package/lib/sync/range/chain.js +6 -1
  175. package/lib/sync/range/chain.js.map +1 -1
  176. package/lib/sync/range/range.d.ts.map +1 -1
  177. package/lib/sync/range/range.js +50 -18
  178. package/lib/sync/range/range.js.map +1 -1
  179. package/lib/sync/unknownBlock.d.ts +5 -2
  180. package/lib/sync/unknownBlock.d.ts.map +1 -1
  181. package/lib/sync/unknownBlock.js +49 -24
  182. package/lib/sync/unknownBlock.js.map +1 -1
  183. package/lib/sync/utils/downloadByRange.d.ts +8 -0
  184. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  185. package/lib/sync/utils/downloadByRange.js +32 -9
  186. package/lib/sync/utils/downloadByRange.js.map +1 -1
  187. package/lib/util/execution.d.ts +9 -11
  188. package/lib/util/execution.d.ts.map +1 -1
  189. package/lib/util/execution.js +5 -1
  190. package/lib/util/execution.js.map +1 -1
  191. package/package.json +17 -17
  192. package/src/api/impl/beacon/blocks/index.ts +7 -4
  193. package/src/api/impl/beacon/index.ts +15 -0
  194. package/src/api/impl/beacon/state/index.ts +31 -3
  195. package/src/api/impl/debug/index.ts +28 -19
  196. package/src/api/impl/validator/index.ts +24 -2
  197. package/src/chain/archiveStore/archiveStore.ts +19 -25
  198. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
  199. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
  200. package/src/chain/archiveStore/historicalState/types.ts +2 -2
  201. package/src/chain/archiveStore/historicalState/worker.ts +1 -2
  202. package/src/chain/archiveStore/interface.ts +1 -1
  203. package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
  204. package/src/chain/blocks/importBlock.ts +13 -3
  205. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  206. package/src/chain/blocks/verifyBlock.ts +1 -0
  207. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
  208. package/src/chain/chain.ts +73 -20
  209. package/src/chain/emitter.ts +7 -1
  210. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  211. package/src/chain/errors/gossipValidation.ts +2 -0
  212. package/src/chain/forkChoice/index.ts +2 -0
  213. package/src/chain/interface.ts +3 -2
  214. package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
  215. package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
  216. package/src/chain/options.ts +3 -3
  217. package/src/chain/prepareNextSlot.ts +6 -3
  218. package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
  219. package/src/chain/produceBlock/produceBlockBody.ts +8 -1
  220. package/src/chain/regen/regen.ts +12 -4
  221. package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
  222. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
  223. package/src/chain/shufflingCache.ts +5 -0
  224. package/src/chain/validatorMonitor.ts +75 -57
  225. package/src/db/beacon.ts +3 -0
  226. package/src/db/buckets.ts +1 -0
  227. package/src/db/interface.ts +2 -0
  228. package/src/db/repositories/earliestAvailableSlot.ts +19 -0
  229. package/src/execution/engine/disabled.ts +0 -8
  230. package/src/execution/engine/http.ts +1 -30
  231. package/src/execution/engine/interface.ts +2 -6
  232. package/src/execution/engine/mock.ts +1 -19
  233. package/src/execution/engine/types.ts +0 -10
  234. package/src/metrics/metrics/lodestar.ts +4 -0
  235. package/src/metrics/metrics.ts +21 -6
  236. package/src/network/processor/gossipHandlers.ts +93 -76
  237. package/src/network/processor/gossipValidatorFn.ts +13 -3
  238. package/src/network/processor/index.ts +89 -10
  239. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +1 -1
  240. package/src/node/nodejs.ts +35 -3
  241. package/src/sync/range/batch.ts +12 -0
  242. package/src/sync/range/chain.ts +7 -1
  243. package/src/sync/range/range.ts +50 -18
  244. package/src/sync/unknownBlock.ts +62 -25
  245. package/src/sync/utils/downloadByRange.ts +47 -8
  246. package/src/util/execution.ts +16 -8
package/src/db/beacon.ts CHANGED
@@ -8,6 +8,7 @@ import {FlatFileStore} from "./flatFileStore/flatFileStore.js";
8
8
  import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
9
9
  import {IBeaconDb} from "./interface.js";
10
10
  import {CheckpointStateRepository} from "./repositories/checkpointState.js";
11
+ import {EarliestAvailableSlotRepository} from "./repositories/earliestAvailableSlot.js";
11
12
  import {
12
13
  AttesterSlashingRepository,
13
14
  BLSToExecutionChangeRepository,
@@ -48,6 +49,7 @@ export class BeaconDb implements IBeaconDb {
48
49
 
49
50
  stateArchive: StateArchiveRepository;
50
51
  checkpointState: CheckpointStateRepository;
52
+ earliestAvailableSlot: EarliestAvailableSlotRepository;
51
53
 
52
54
  voluntaryExit: VoluntaryExitRepository;
53
55
  proposerSlashing: ProposerSlashingRepository;
@@ -83,6 +85,7 @@ export class BeaconDb implements IBeaconDb {
83
85
 
84
86
  this.stateArchive = new StateArchiveRepository(config, db);
85
87
  this.checkpointState = new CheckpointStateRepository(config, db);
88
+ this.earliestAvailableSlot = new EarliestAvailableSlotRepository(db);
86
89
  this.voluntaryExit = new VoluntaryExitRepository(config, db);
87
90
  this.blsToExecutionChange = new BLSToExecutionChangeRepository(config, db);
88
91
  this.proposerSlashing = new ProposerSlashingRepository(config, db);
package/src/db/buckets.ts CHANGED
@@ -72,6 +72,7 @@ export enum Bucket {
72
72
 
73
73
  gloas_executionPayloadEnvelope = 59, // GLOAS BeaconBlockRoot -> SignedExecutionPayloadEnvelope
74
74
  gloas_executionPayloadEnvelopeArchive = 60, // GLOAS Slot -> SignedExecutionPayloadEnvelope, or 0x00 + SignedExecutionPayloadHeaderEnvelope
75
+ earliestAvailableSlot = 61, // singleton -> Slot
75
76
  }
76
77
 
77
78
  export function getBucketNameByValue<T extends Bucket>(enumValue: T): keyof typeof Bucket {
@@ -3,6 +3,7 @@ import {Slot} from "@lodestar/types";
3
3
  import type {IDataColumnStore} from "./dataColumnStore.js";
4
4
  import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
5
5
  import {CheckpointStateRepository} from "./repositories/checkpointState.js";
6
+ import {EarliestAvailableSlotRepository} from "./repositories/earliestAvailableSlot.js";
6
7
  import {
7
8
  AttesterSlashingRepository,
8
9
  BLSToExecutionChangeRepository,
@@ -46,6 +47,7 @@ export interface IBeaconDb {
46
47
  stateArchive: StateArchiveRepository;
47
48
  // checkpoint states
48
49
  checkpointState: CheckpointStateRepository;
50
+ earliestAvailableSlot: EarliestAvailableSlotRepository;
49
51
 
50
52
  // op pool
51
53
  voluntaryExit: VoluntaryExitRepository;
@@ -0,0 +1,19 @@
1
+ import {Db, encodeKey} from "@lodestar/db";
2
+ import {Slot, ssz} from "@lodestar/types";
3
+ import {Bucket, getBucketNameByValue} from "../buckets.js";
4
+
5
+ export class EarliestAvailableSlotRepository {
6
+ private readonly key = encodeKey(Bucket.earliestAvailableSlot, 0);
7
+ private readonly opts = {bucketId: getBucketNameByValue(Bucket.earliestAvailableSlot)};
8
+
9
+ constructor(private readonly db: Db) {}
10
+
11
+ async get(): Promise<Slot | null> {
12
+ const bytes = await this.db.get(this.key, this.opts);
13
+ return bytes === null ? null : ssz.Slot.deserialize(bytes);
14
+ }
15
+
16
+ async set(slot: Slot): Promise<void> {
17
+ await this.db.put(this.key, ssz.Slot.serialize(slot), this.opts);
18
+ }
19
+ }
@@ -21,18 +21,10 @@ export class ExecutionEngineDisabled implements IExecutionEngine {
21
21
  throw Error("Execution engine disabled");
22
22
  }
23
23
 
24
- getPayloadBodiesByHash(): Promise<never> {
25
- throw Error("Execution engine disabled");
26
- }
27
-
28
24
  getPayloadBodiesByHashV2(): Promise<never> {
29
25
  throw Error("Execution engine disabled");
30
26
  }
31
27
 
32
- getPayloadBodiesByRange(): Promise<never> {
33
- throw Error("Execution engine disabled");
34
- }
35
-
36
28
  getBlobs(): Promise<never> {
37
29
  throw Error("Execution engine disabled");
38
30
  }
@@ -31,13 +31,11 @@ import {
31
31
  BLOB_AND_PROOF_V2_RPC_BYTES,
32
32
  EngineApiRpcParamTypes,
33
33
  EngineApiRpcReturnTypes,
34
- ExecutionPayloadBody,
35
34
  ExecutionPayloadBodyV2,
36
35
  assertReqSizeLimit,
37
36
  deserializeBlobAndProofs,
38
37
  deserializeBlobAndProofsV2,
39
38
  deserializeBlobAndProofsV2IntoBytes,
40
- deserializeExecutionPayloadBody,
41
39
  deserializeExecutionPayloadBodyV2,
42
40
  parseExecutionPayload,
43
41
  serializeBeaconBlockRoot,
@@ -46,7 +44,7 @@ import {
46
44
  serializePayloadAttributes,
47
45
  serializeVersionedHashes,
48
46
  } from "./types.js";
49
- import {bytesToData, getExecutionEngineState, numToQuantity} from "./utils.js";
47
+ import {bytesToData, getExecutionEngineState} from "./utils.js";
50
48
 
51
49
  export type ExecutionEngineModules = {
52
50
  signal: AbortSignal;
@@ -114,7 +112,6 @@ const notifyNewPayloadOpts: ReqOpts = {routeId: "notifyNewPayload"};
114
112
  const forkchoiceUpdatedV1Opts: ReqOpts = {routeId: "forkchoiceUpdated"};
115
113
  const getPayloadOpts: ReqOpts = {routeId: "getPayload"};
116
114
  const getPayloadBodiesByHashOpts: ReqOpts = {routeId: "getPayloadBodiesByHash"};
117
- const getPayloadBodiesByRangeOpts: ReqOpts = {routeId: "getPayloadBodiesByRange"};
118
115
  const getBlobsV1Opts: ReqOpts = {routeId: "getBlobsV1"};
119
116
  const getBlobsV2Opts: ReqOpts = {routeId: "getBlobsV2"};
120
117
  const getClientVersionOpts: ReqOpts = {routeId: "getClientVersion"};
@@ -468,16 +465,6 @@ export class ExecutionEngineHttp implements IExecutionEngine {
468
465
  this.payloadIdCache.prune();
469
466
  }
470
467
 
471
- async getPayloadBodiesByHash(_fork: ForkName, blockHashes: RootHex[]): Promise<(ExecutionPayloadBody | null)[]> {
472
- const method = "engine_getPayloadBodiesByHashV1";
473
- assertReqSizeLimit(blockHashes.length, 32);
474
- const response = await this.rpc.fetchWithRetries<
475
- EngineApiRpcReturnTypes[typeof method],
476
- EngineApiRpcParamTypes[typeof method]
477
- >({method, params: [blockHashes]}, getPayloadBodiesByHashOpts);
478
- return response.map(deserializeExecutionPayloadBody);
479
- }
480
-
481
468
  async getPayloadBodiesByHashV2(blockHashes: RootHex[]): Promise<(ExecutionPayloadBodyV2 | null)[]> {
482
469
  const method = "engine_getPayloadBodiesByHashV2";
483
470
  assertReqSizeLimit(blockHashes.length, 32);
@@ -488,22 +475,6 @@ export class ExecutionEngineHttp implements IExecutionEngine {
488
475
  return response.map(deserializeExecutionPayloadBodyV2);
489
476
  }
490
477
 
491
- async getPayloadBodiesByRange(
492
- _fork: ForkName,
493
- startBlockNumber: number,
494
- blockCount: number
495
- ): Promise<(ExecutionPayloadBody | null)[]> {
496
- const method = "engine_getPayloadBodiesByRangeV1";
497
- assertReqSizeLimit(blockCount, 32);
498
- const start = numToQuantity(startBlockNumber);
499
- const count = numToQuantity(blockCount);
500
- const response = await this.rpc.fetchWithRetries<
501
- EngineApiRpcReturnTypes[typeof method],
502
- EngineApiRpcParamTypes[typeof method]
503
- >({method, params: [start, count]}, getPayloadBodiesByRangeOpts);
504
- return response.map(deserializeExecutionPayloadBody);
505
- }
506
-
507
478
  async getBlobs(
508
479
  fork: ForkPostFulu,
509
480
  versionedHashes: VersionedHashes,
@@ -13,7 +13,7 @@ import {BlobsBundle, ExecutionPayload, ExecutionRequests, Root, RootHex, Wei, ca
13
13
  import {BlobAndProof} from "@lodestar/types/deneb";
14
14
  import {BlobAndProofV2} from "@lodestar/types/fulu";
15
15
  import {PayloadId, PayloadIdCache, WithdrawalV1} from "./payloadIdCache.js";
16
- import {ExecutionPayloadBody, ExecutionPayloadBodyV2} from "./types.js";
16
+ import {ExecutionPayloadBodyV2} from "./types.js";
17
17
  import {DATA} from "./utils.js";
18
18
 
19
19
  export {PayloadIdCache, type PayloadId, type WithdrawalV1};
@@ -170,13 +170,9 @@ export interface IExecutionEngine {
170
170
  shouldOverrideBuilder?: boolean;
171
171
  }>;
172
172
 
173
- getPayloadBodiesByHash(fork: ForkName, blockHash: DATA[]): Promise<(ExecutionPayloadBody | null)[]>;
174
-
175
- /** Amsterdam: like V1 but also returns the block access list (null if the EL no longer has it) */
173
+ /** Amsterdam: payload bodies including the block access list (null if the EL no longer has it) */
176
174
  getPayloadBodiesByHashV2(blockHash: DATA[]): Promise<(ExecutionPayloadBodyV2 | null)[]>;
177
175
 
178
- getPayloadBodiesByRange(fork: ForkName, start: number, count: number): Promise<(ExecutionPayloadBody | null)[]>;
179
-
180
176
  getBlobs(
181
177
  fork: ForkPostFulu,
182
178
  versionedHashes: VersionedHashes,
@@ -22,7 +22,6 @@ import {
22
22
  BlobsBundleRpc,
23
23
  EngineApiRpcParamTypes,
24
24
  EngineApiRpcReturnTypes,
25
- ExecutionPayloadBodyRpc,
26
25
  ExecutionPayloadBodyV2Rpc,
27
26
  ExecutionPayloadRpc,
28
27
  ExecutionRequestsRpc,
@@ -50,7 +49,7 @@ type ExecutionBlock = {
50
49
  blockHash: RootHex;
51
50
  timestamp: number;
52
51
  blockNumber: number;
53
- /** Bodies as received via newPayload, served back by engine_getPayloadBodiesByHash{V1,V2} */
52
+ /** Bodies as received via newPayload, served back by engine_getPayloadBodiesByHashV2 */
54
53
  body: ExecutionPayloadBodyV2Rpc;
55
54
  };
56
55
 
@@ -148,23 +147,13 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
148
147
  engine_getPayloadV4: this.getPayloadV5.bind(this),
149
148
  engine_getPayloadV5: this.getPayloadV5.bind(this),
150
149
  engine_getPayloadV6: this.getPayloadV5.bind(this),
151
- engine_getPayloadBodiesByHashV1: this.getPayloadBodiesByHash.bind(this),
152
150
  engine_getPayloadBodiesByHashV2: this.getPayloadBodiesByHashV2.bind(this),
153
- engine_getPayloadBodiesByRangeV1: this.getPayloadBodiesByRange.bind(this),
154
151
  engine_getClientVersionV1: this.getClientVersionV1.bind(this),
155
152
  engine_getBlobsV1: this.getBlobs.bind(this),
156
153
  engine_getBlobsV2: this.getBlobsV2.bind(this),
157
154
  };
158
155
  }
159
156
 
160
- private getPayloadBodiesByHash(
161
- blockHashes: EngineApiRpcParamTypes["engine_getPayloadBodiesByHashV1"][0]
162
- ): EngineApiRpcReturnTypes["engine_getPayloadBodiesByHashV1"] {
163
- return this.getPayloadBodiesByHashV2(blockHashes).map((body) =>
164
- body ? {transactions: body.transactions, withdrawals: body.withdrawals} : null
165
- );
166
- }
167
-
168
157
  private getPayloadBodiesByHashV2(
169
158
  blockHashes: EngineApiRpcParamTypes["engine_getPayloadBodiesByHashV2"][0]
170
159
  ): EngineApiRpcReturnTypes["engine_getPayloadBodiesByHashV2"] {
@@ -172,13 +161,6 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
172
161
  return blockHashes.map((hash) => this.validBlocks.get(hash)?.body ?? null);
173
162
  }
174
163
 
175
- private getPayloadBodiesByRange(
176
- _start: EngineApiRpcParamTypes["engine_getPayloadBodiesByRangeV1"][0],
177
- _count: EngineApiRpcParamTypes["engine_getPayloadBodiesByRangeV1"][1]
178
- ): EngineApiRpcReturnTypes["engine_getPayloadBodiesByRangeV1"] {
179
- return [] as ExecutionPayloadBodyRpc[];
180
- }
181
-
182
164
  /**
183
165
  * Mock manipulator to add predefined responses before execution engine client calls
184
166
  */
@@ -88,15 +88,8 @@ export type EngineApiRpcParamTypes = {
88
88
  /**
89
89
  * 1. Array of DATA - Array of block_hash field values of the ExecutionPayload structure
90
90
  * */
91
- engine_getPayloadBodiesByHashV1: DATA[][];
92
91
  engine_getPayloadBodiesByHashV2: DATA[][];
93
92
 
94
- /**
95
- * 1. start: QUANTITY, 64 bits - Starting block number
96
- * 2. count: QUANTITY, 64 bits - Number of blocks to return
97
- */
98
- engine_getPayloadBodiesByRangeV1: [start: QUANTITY, count: QUANTITY];
99
-
100
93
  /**
101
94
  * Object - Instance of ClientVersion
102
95
  */
@@ -148,11 +141,8 @@ export type EngineApiRpcReturnTypes = {
148
141
  engine_getPayloadV5: ExecutionPayloadResponse;
149
142
  engine_getPayloadV6: ExecutionPayloadResponse;
150
143
 
151
- engine_getPayloadBodiesByHashV1: (ExecutionPayloadBodyRpc | null)[];
152
144
  engine_getPayloadBodiesByHashV2: (ExecutionPayloadBodyV2Rpc | null)[];
153
145
 
154
- engine_getPayloadBodiesByRangeV1: (ExecutionPayloadBodyRpc | null)[];
155
-
156
146
  engine_getClientVersionV1: ClientVersionRpc[];
157
147
 
158
148
  engine_getBlobsV1: (BlobAndProofRpc | null)[];
@@ -1281,6 +1281,10 @@ export function createLodestarMetrics(
1281
1281
  name: "lodestar_oppool_aggregated_attestation_pool_packed_attestations_seen_committees_total",
1282
1282
  help: "Total number of committees for which all members are seen when producing packed attestations",
1283
1283
  }),
1284
+ missingShufflingCommittees: register.gauge({
1285
+ name: "lodestar_oppool_aggregated_attestation_pool_packed_attestations_missing_shuffling_committees_total",
1286
+ help: "Total number of committees skipped due to missing shuffling when producing packed attestations",
1287
+ }),
1284
1288
  },
1285
1289
  },
1286
1290
  attestationPool: {
@@ -9,27 +9,42 @@ import {RegistryMetricCreator} from "./utils/registryMetricCreator.js";
9
9
 
10
10
  export type Metrics = BeaconMetrics &
11
11
  ForkChoiceMetrics &
12
- BeaconStateTransitionMetrics &
13
- LodestarMetrics & {register: RegistryMetricCreator; close: () => void};
12
+ LodestarMetrics & {
13
+ /** Null when we use native state transition metrics instead */
14
+ stateTransition: BeaconStateTransitionMetrics | null;
15
+ register: RegistryMetricCreator;
16
+ close: () => void;
17
+ };
18
+
19
+ export type CreateMetricsOptions = {
20
+ /*
21
+ * Toggle inclusion of state transition metrics.
22
+ *
23
+ * False when native state transition is used, so we
24
+ * can grab metrics from the native implementation instead.
25
+ * */
26
+ includeStateTransitionMetrics?: boolean;
27
+ collectNodeMetrics?: boolean;
28
+ };
14
29
 
15
30
  export function createMetrics(
16
31
  opts: MetricsOptions,
17
32
  genesisTime: number,
18
33
  externalRegistries: Registry[] = [],
19
- {collectNodeMetrics = true}: {collectNodeMetrics?: boolean} = {}
34
+ createOpts: CreateMetricsOptions = {}
20
35
  ): Metrics {
21
36
  const register = new RegistryMetricCreator();
22
37
  const beacon = createBeaconMetrics(register);
23
38
  const forkChoice = getForkChoiceMetrics(register);
24
39
  const lodestar = createLodestarMetrics(register, opts.metadata, genesisTime);
25
- const stateTransition = getMetrics(register);
40
+ const stateTransition = createOpts.includeStateTransitionMetrics === false ? null : getMetrics(register);
26
41
 
27
42
  const onUnhandledRejection = (_error: unknown): void => {
28
43
  lodestar.unhandledPromiseRejections.inc();
29
44
  };
30
45
  process.on("unhandledRejection", onUnhandledRejection);
31
46
 
32
- const nodeJsMetricsClose = collectNodeMetrics ? collectNodeJSMetrics(register) : (): void => {};
47
+ const nodeJsMetricsClose = createOpts.collectNodeMetrics === false ? (): void => {} : collectNodeJSMetrics(register);
33
48
  const close = (): void => {
34
49
  process.removeListener("unhandledRejection", onUnhandledRejection);
35
50
  nodeJsMetricsClose();
@@ -46,7 +61,7 @@ export function createMetrics(
46
61
  ...beacon,
47
62
  ...forkChoice,
48
63
  ...lodestar,
49
- ...stateTransition,
64
+ stateTransition,
50
65
  register,
51
66
  close,
52
67
  };
@@ -12,9 +12,10 @@ import {
12
12
  isForkPostElectra,
13
13
  isForkPostGloas,
14
14
  } from "@lodestar/params";
15
- import {computeTimeAtSlot} from "@lodestar/state-transition";
15
+ import {computeTimeAtSlot, signedBlockToSignedHeader} from "@lodestar/state-transition";
16
16
  import {
17
17
  Root,
18
+ RootHex,
18
19
  SignedBeaconBlock,
19
20
  SingleAttestation,
20
21
  Slot,
@@ -78,6 +79,7 @@ import {
78
79
  validateGossipSyncCommittee,
79
80
  validateGossipVoluntaryExit,
80
81
  validateSyncCommitteeGossipContributionAndProof,
82
+ verifyBlockProposerSignature,
81
83
  } from "../../chain/validation/index.js";
82
84
  import {validateLightClientFinalityUpdate} from "../../chain/validation/lightClientFinalityUpdate.js";
83
85
  import {validateLightClientOptimisticUpdate} from "../../chain/validation/lightClientOptimisticUpdate.js";
@@ -148,37 +150,16 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
148
150
  function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHandlerOpts): SequentialGossipHandlers {
149
151
  const {chain, config, metrics, logger, core} = modules;
150
152
 
151
- async function validateBeaconBlock(
153
+ /**
154
+ * Add a gossip block to the seen caches. The proposer signature of the block MUST be verified.
155
+ */
156
+ function cacheSignatureVerifiedBlock(
152
157
  signedBlock: SignedBeaconBlock,
158
+ blockRootHex: RootHex,
153
159
  fork: ForkName,
154
160
  peerIdStr: string,
155
161
  seenTimestampSec: number
156
- ): Promise<IBlockInput> {
157
- const slot = signedBlock.message.slot;
158
- const forkTypes = config.getForkTypes(slot);
159
- const blockRootHex = toRootHex(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message));
160
- const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
161
- const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
162
-
163
- // always set block to seen cache for all forks so that we don't need to download it
164
- // TODO: validate block before adding to cache
165
- // tracked in https://github.com/ChainSafe/lodestar/issues/7957
166
-
167
- const logCtx = {
168
- slot,
169
- root: blockRootHex,
170
- currentSlot: chain.clock.currentSlot,
171
- peerId: peerIdStr,
172
- delaySec,
173
- recvToValLatency,
174
- };
175
-
176
- logger.debug("Received gossip block", {...logCtx});
177
-
178
- // optimistically add gossip block to the seen cache
179
- // if validation fails, we will NOT forward this gossip block to peers
180
- // - if PARENT_BLOCK_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
181
- // - if other validator errors, blockInput will stay in the seen cache and will be pruned on finalization
162
+ ): BlockInput {
182
163
  const blockInput = chain.seenBlockInputCache.getByBlock({
183
164
  block: signedBlock,
184
165
  blockRootHex,
@@ -187,8 +168,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
187
168
  peerIdStr,
188
169
  });
189
170
 
190
- // Optimistically seed the payload-envelope cache too, mirroring seenBlockInputCache above.
191
- // This ensures we have PayloadEnvelopeInput, even through "PARENT_BLOCK_UNKNOWN" error
171
+ // Also seed the payload-envelope cache, so we have PayloadEnvelopeInput even through "PARENT_BLOCK_UNKNOWN" error
192
172
  // see https://github.com/ChainSafe/lodestar/issues/9475
193
173
  if (isForkPostGloas(fork)) {
194
174
  chain.seenPayloadEnvelopeInputCache.add({
@@ -202,8 +182,37 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
202
182
  });
203
183
  }
204
184
 
185
+ return blockInput;
186
+ }
187
+
188
+ async function validateBeaconBlock(
189
+ signedBlock: SignedBeaconBlock,
190
+ fork: ForkName,
191
+ peerIdStr: string,
192
+ seenTimestampSec: number
193
+ ): Promise<IBlockInput> {
194
+ const slot = signedBlock.message.slot;
195
+ const forkTypes = config.getForkTypes(slot);
196
+ const blockRootHex = toRootHex(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message));
197
+ const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
198
+ const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
199
+
200
+ const logCtx = {
201
+ slot,
202
+ root: blockRootHex,
203
+ currentSlot: chain.clock.currentSlot,
204
+ peerId: peerIdStr,
205
+ delaySec,
206
+ recvToValLatency,
207
+ };
208
+
209
+ logger.debug("Received gossip block", {...logCtx});
210
+
211
+ chain.seenBlockInputCache.markValidatingBlock(blockRootHex);
212
+
205
213
  try {
206
214
  const {skippedSlots} = await validateGossipBlock(config, chain, signedBlock, fork);
215
+ const blockInput = cacheSignatureVerifiedBlock(signedBlock, blockRootHex, fork, peerIdStr, seenTimestampSec);
207
216
 
208
217
  const blockInputMeta = blockInput.getLogMeta();
209
218
 
@@ -229,54 +238,54 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
229
238
  if (e instanceof BlockGossipError) {
230
239
  logger.debug("Gossip block has error", {slot, root: blockRootHex, code: e.type.code});
231
240
  if (
232
- (e.type.code === BlockErrorCode.PARENT_BLOCK_UNKNOWN ||
233
- e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN) &&
234
- blockInput
241
+ e.type.code === BlockErrorCode.PARENT_BLOCK_UNKNOWN ||
242
+ e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN
235
243
  ) {
244
+ // The block is retained for unknown parent sync without full validation. Authenticate it first, this
245
+ // does not need the parent state
246
+ const proposerIndex = signedBlock.message.proposerIndex;
247
+ await verifyBlockProposerSignature(chain, signedBlock, blockRootHex);
248
+ chain.seenBlockProposers.observeBlockRoot(
249
+ slot,
250
+ proposerIndex,
251
+ blockRootHex,
252
+ signedBlockToSignedHeader(config, signedBlock)
253
+ );
254
+
255
+ // Check again after the signature verification so concurrent proposals cannot both be retained
256
+ if (chain.seenBlockProposers.isKnown(slot, proposerIndex)) {
257
+ if (chain.seenBlockProposers.isRepeatProposal(slot, proposerIndex, blockRootHex)) {
258
+ throw new BlockGossipError(GossipAction.IGNORE, {
259
+ code: BlockErrorCode.REPEAT_PROPOSAL,
260
+ proposerIndex,
261
+ root: blockRootHex,
262
+ });
263
+ }
264
+ throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRootHex});
265
+ }
266
+ chain.seenBlockProposers.add(slot, proposerIndex, blockRootHex);
267
+
268
+ const blockInput = cacheSignatureVerifiedBlock(signedBlock, blockRootHex, fork, peerIdStr, seenTimestampSec);
236
269
  chain.emitter.emit(ChainEvent.blockUnknownParent, {
237
270
  blockInput,
238
271
  peer: peerIdStr,
239
272
  source: BlockInputSource.gossip,
240
273
  });
241
- // throw error (don't prune the blockInput)
242
274
  throw e;
243
275
  }
244
276
 
245
- // IGNORE means the block is acceptable (e.g. FUTURE_SLOT, ALREADY_KNOWN), just not propagated.
246
- // Keep the optimistically-added cache entries; they are pruned on finalization. Only REJECT
247
- // (provably invalid), unexpected errors and repeat proposals that are not imported prune.
248
- if (e.action === GossipAction.IGNORE) {
249
- // Only a signature-verified sibling is imported by the beacon_block handler, any other repeat proposal
250
- // is dropped from the caches and re-downloaded by sync if it ever becomes relevant. Its signature was not
251
- // verified, so only its own entry is removed, never its claimed ancestors
252
- if (
253
- e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
254
- !chain.seenBlockProposers.hasBlockRoot(slot, signedBlock.message.proposerIndex, blockRootHex)
255
- ) {
256
- chain.seenBlockInputCache.remove(blockRootHex);
257
- if (isForkPostGloas(fork)) {
258
- chain.seenPayloadEnvelopeInputCache.remove(blockRootHex);
259
- }
260
- }
261
- throw e;
277
+ if (e.action === GossipAction.REJECT) {
278
+ chain.persistInvalidSszValue(
279
+ forkTypes.SignedBeaconBlock,
280
+ signedBlock,
281
+ `gossip_reject_slot_${slot}`,
282
+ blockRootHex
283
+ );
262
284
  }
263
-
264
- chain.persistInvalidSszValue(
265
- forkTypes.SignedBeaconBlock,
266
- signedBlock,
267
- `gossip_reject_slot_${slot}`,
268
- blockRootHex
269
- );
270
- }
271
-
272
- // REJECT or unexpected (non-BlockGossipError) error: drop the optimistically-added entries from
273
- // both caches, keeping them consistent. The block may carry any parent root, so only its own entry is removed
274
- chain.seenBlockInputCache.remove(blockRootHex);
275
- if (isForkPostGloas(fork)) {
276
- chain.seenPayloadEnvelopeInputCache.remove(blockRootHex);
277
285
  }
278
286
  throw e;
279
287
  } finally {
288
+ chain.seenBlockInputCache.unmarkValidatingBlock(blockRootHex);
280
289
  // The block received from the network may have established an equivocation, either by conflicting
281
290
  // with a previously observed block root (REPEAT_PROPOSAL) or with a root observed during validation
282
291
  const proposerIndex = signedBlock.message.proposerIndex;
@@ -752,18 +761,23 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
752
761
  e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
753
762
  // Only a signature-verified sibling recorded in the seen cache is imported. The cache holds at most two
754
763
  // roots per proposer and slot, which bounds full imports over gossip to one alternate
755
- chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root)
764
+ chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root) &&
765
+ // A sibling with an unknown parent cannot be imported, sync fetches it if its branch becomes relevant
766
+ chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null
756
767
  ) {
757
- // blockInput was optimistically seeded in validateBeaconBlock and retained on IGNORE
758
- const blockInput = chain.seenBlockInputCache.get(e.type.root);
759
- if (blockInput) {
760
- // We're returning IGNORE (thrown below), so this block is not forwarded to peers. Unlike the
761
- // happy path there is no rush to forward, so we don't need to wrap in callInNextEventLoop.
762
- chain.serializedCache.set(signedBlock, serializedData);
763
- // this is technically not a valid gossip block but gossip validation is a cheap subset of checks
764
- // this runs the full state transition, so importing an equivocating-but-valid block here is safe.
765
- handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
766
- }
768
+ const blockInput = cacheSignatureVerifiedBlock(
769
+ signedBlock,
770
+ e.type.root,
771
+ topic.boundary.fork,
772
+ peerIdStr,
773
+ seenTimestampSec
774
+ );
775
+ // We're returning IGNORE (thrown below), so this block is not forwarded to peers. Unlike the
776
+ // happy path there is no rush to forward, so we don't need to wrap in callInNextEventLoop.
777
+ chain.serializedCache.set(signedBlock, serializedData);
778
+ // this is technically not a valid gossip block but gossip validation is a cheap subset of checks
779
+ // this runs the full state transition, so importing an equivocating-but-valid block here is safe.
780
+ handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
767
781
  }
768
782
  // rethrow so gossipValidatorFn maps IGNORE -> TopicValidatorResult.Ignore (message not forwarded)
769
783
  throw e;
@@ -1341,6 +1355,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1341
1355
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1342
1356
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1343
1357
  core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1358
+ // The builder may have signed another envelope that is valid, keeping this one would make
1359
+ // by-root and range sync reuse it and never import the payload
1360
+ chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
1344
1361
  // Misbehaving peer, but could highlight an issue in another client
1345
1362
  logLevel = LogLevel.warn;
1346
1363
  break;
@@ -20,6 +20,7 @@ import {
20
20
  VoluntaryExitErrorCode,
21
21
  } from "../../chain/errors/index.js";
22
22
  import {Metrics} from "../../metrics/index.js";
23
+ import {PeerIdStr} from "../../util/peerId.js";
23
24
  import {INetworkCore} from "../core/index.js";
24
25
  import {
25
26
  BatchGossipHandlerFn,
@@ -40,6 +41,8 @@ export type ValidatorFnModules = {
40
41
  core: INetworkCore;
41
42
  };
42
43
 
44
+ export type OnFatalPeerFn = (peer: PeerIdStr) => void;
45
+
43
46
  type RejectPeerActionRule = {default: PeerAction; byCode?: Record<string, PeerAction>};
44
47
 
45
48
  /**
@@ -112,7 +115,7 @@ const gossipRejectPeerAction: Record<GossipType, RejectPeerActionRule> = {
112
115
  [GossipType.light_client_optimistic_update]: {default: PeerAction.HighToleranceError},
113
116
  };
114
117
 
115
- function rejectPeerAction(type: GossipType, code: string): PeerAction {
118
+ export function rejectPeerAction(type: GossipType, code: string): PeerAction {
116
119
  const rule = gossipRejectPeerAction[type];
117
120
  return rule.byCode?.[code] ?? rule.default;
118
121
  }
@@ -123,7 +126,8 @@ function rejectPeerAction(type: GossipType, code: string): PeerAction {
123
126
  */
124
127
  export function getGossipValidatorBatchFn(
125
128
  gossipHandlers: GossipHandlers,
126
- modules: ValidatorFnModules
129
+ modules: ValidatorFnModules,
130
+ onFatalPeer: OnFatalPeerFn
127
131
  ): GossipValidatorBatchFn {
128
132
  const {logger, metrics, core} = modules;
129
133
 
@@ -173,6 +177,7 @@ export function getGossipValidatorBatchFn(
173
177
  metrics?.networkProcessor.gossipAttestationRejectByReason.inc({reason: e.type.code});
174
178
  const peerAction = rejectPeerAction(type, e.type.code);
175
179
  core.reportPeer(propagationSource, peerAction, e.type.code);
180
+ if (peerAction === PeerAction.Fatal) onFatalPeer(propagationSource);
176
181
  logger.debug(
177
182
  `Gossip validation ${type} rejected`,
178
183
  {peer: propagationSource, clientAgent, clientVersion, peerAction},
@@ -208,7 +213,11 @@ export function getGossipValidatorBatchFn(
208
213
  *
209
214
  * @see getGossipHandlers for reasoning on why GossipHandlerFn are used for gossip validation.
210
215
  */
211
- export function getGossipValidatorFn(gossipHandlers: GossipHandlers, modules: ValidatorFnModules): GossipValidatorFn {
216
+ export function getGossipValidatorFn(
217
+ gossipHandlers: GossipHandlers,
218
+ modules: ValidatorFnModules,
219
+ onFatalPeer: OnFatalPeerFn
220
+ ): GossipValidatorFn {
212
221
  const {logger, metrics, core} = modules;
213
222
 
214
223
  return async function gossipValidatorFn({
@@ -260,6 +269,7 @@ export function getGossipValidatorFn(gossipHandlers: GossipHandlers, modules: Va
260
269
  metrics?.networkProcessor.gossipValidationReject.inc({topic: type});
261
270
  const peerAction = rejectPeerAction(type, e.type.code);
262
271
  core.reportPeer(propagationSource, peerAction, e.type.code);
272
+ if (peerAction === PeerAction.Fatal) onFatalPeer(propagationSource);
263
273
  logger.debug(
264
274
  `Gossip validation ${type} rejected`,
265
275
  {peer: propagationSource, clientAgent, clientVersion, peerAction},