@lodestar/beacon-node 1.50.0-dev.ebecebb9ba → 1.50.0-dev.f0a5c36612

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 (296) 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/pool/index.js +4 -4
  8. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  9. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  10. package/lib/api/impl/beacon/state/index.js +23 -3
  11. package/lib/api/impl/beacon/state/index.js.map +1 -1
  12. package/lib/api/impl/debug/index.d.ts.map +1 -1
  13. package/lib/api/impl/debug/index.js +26 -18
  14. package/lib/api/impl/debug/index.js.map +1 -1
  15. package/lib/api/impl/validator/index.d.ts +1 -0
  16. package/lib/api/impl/validator/index.d.ts.map +1 -1
  17. package/lib/api/impl/validator/index.js +23 -1
  18. package/lib/api/impl/validator/index.js.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.d.ts +6 -0
  20. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/archiveStore.js +18 -24
  22. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
  24. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
  26. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
  28. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  29. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
  30. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  31. package/lib/chain/archiveStore/historicalState/worker.js +1 -1
  32. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  33. package/lib/chain/archiveStore/interface.d.ts +1 -1
  34. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  35. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
  36. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  37. package/lib/chain/archiveStore/utils/pruneHistory.js +5 -3
  38. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  39. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/importBlock.js +11 -3
  41. package/lib/chain/blocks/importBlock.js.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  43. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  44. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  45. package/lib/chain/blocks/verifyBlock.js +1 -1
  46. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  47. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
  48. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  49. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
  50. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  51. package/lib/chain/chain.d.ts +9 -5
  52. package/lib/chain/chain.d.ts.map +1 -1
  53. package/lib/chain/chain.js +56 -21
  54. package/lib/chain/chain.js.map +1 -1
  55. package/lib/chain/emitter.d.ts +7 -1
  56. package/lib/chain/emitter.d.ts.map +1 -1
  57. package/lib/chain/emitter.js +5 -0
  58. package/lib/chain/emitter.js.map +1 -1
  59. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  60. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  61. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  62. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  63. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  64. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  65. package/lib/chain/errors/gossipValidation.js +2 -0
  66. package/lib/chain/errors/gossipValidation.js.map +1 -1
  67. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  68. package/lib/chain/forkChoice/index.js +2 -0
  69. package/lib/chain/forkChoice/index.js.map +1 -1
  70. package/lib/chain/initState.d.ts +2 -9
  71. package/lib/chain/initState.d.ts.map +1 -1
  72. package/lib/chain/initState.js +5 -38
  73. package/lib/chain/initState.js.map +1 -1
  74. package/lib/chain/interface.d.ts +3 -2
  75. package/lib/chain/interface.d.ts.map +1 -1
  76. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
  77. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  78. package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
  79. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  80. package/lib/chain/opPools/opPool.d.ts +5 -4
  81. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  82. package/lib/chain/opPools/opPool.js +62 -18
  83. package/lib/chain/opPools/opPool.js.map +1 -1
  84. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
  85. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  86. package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
  87. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  88. package/lib/chain/opPools/utils.d.ts +4 -1
  89. package/lib/chain/opPools/utils.d.ts.map +1 -1
  90. package/lib/chain/opPools/utils.js +11 -1
  91. package/lib/chain/opPools/utils.js.map +1 -1
  92. package/lib/chain/options.d.ts +2 -1
  93. package/lib/chain/options.d.ts.map +1 -1
  94. package/lib/chain/options.js +1 -1
  95. package/lib/chain/options.js.map +1 -1
  96. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  97. package/lib/chain/prepareNextSlot.js +5 -2
  98. package/lib/chain/prepareNextSlot.js.map +1 -1
  99. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
  100. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  101. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  102. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  103. package/lib/chain/produceBlock/produceBlockBody.js +6 -2
  104. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  105. package/lib/chain/regen/regen.d.ts +2 -0
  106. package/lib/chain/regen/regen.d.ts.map +1 -1
  107. package/lib/chain/regen/regen.js +10 -4
  108. package/lib/chain/regen/regen.js.map +1 -1
  109. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
  110. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  111. package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
  112. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  113. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
  114. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  115. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
  116. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  117. package/lib/chain/shufflingCache.d.ts +1 -0
  118. package/lib/chain/shufflingCache.d.ts.map +1 -1
  119. package/lib/chain/shufflingCache.js +4 -0
  120. package/lib/chain/shufflingCache.js.map +1 -1
  121. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  122. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  123. package/lib/chain/validation/attesterSlashing.js +3 -0
  124. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  125. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  126. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  127. package/lib/chain/validation/proposerSlashing.js +3 -0
  128. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  129. package/lib/chain/validatorMonitor.d.ts +5 -1
  130. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  131. package/lib/chain/validatorMonitor.js +64 -54
  132. package/lib/chain/validatorMonitor.js.map +1 -1
  133. package/lib/db/beacon.d.ts +2 -0
  134. package/lib/db/beacon.d.ts.map +1 -1
  135. package/lib/db/beacon.js +5 -0
  136. package/lib/db/beacon.js.map +1 -1
  137. package/lib/db/buckets.d.ts +2 -1
  138. package/lib/db/buckets.d.ts.map +1 -1
  139. package/lib/db/buckets.js +1 -0
  140. package/lib/db/buckets.js.map +1 -1
  141. package/lib/db/interface.d.ts +2 -0
  142. package/lib/db/interface.d.ts.map +1 -1
  143. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  144. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  145. package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
  146. package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
  147. package/lib/db/repositories/earliestAvailableSlot.js +19 -0
  148. package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
  149. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  150. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  151. package/lib/db/repositories/proposerSlashing.js +5 -1
  152. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  153. package/lib/execution/engine/disabled.d.ts +0 -2
  154. package/lib/execution/engine/disabled.d.ts.map +1 -1
  155. package/lib/execution/engine/disabled.js +0 -6
  156. package/lib/execution/engine/disabled.js.map +1 -1
  157. package/lib/execution/engine/http.d.ts +1 -3
  158. package/lib/execution/engine/http.d.ts.map +1 -1
  159. package/lib/execution/engine/http.js +2 -17
  160. package/lib/execution/engine/http.js.map +1 -1
  161. package/lib/execution/engine/interface.d.ts +2 -4
  162. package/lib/execution/engine/interface.d.ts.map +1 -1
  163. package/lib/execution/engine/mock.d.ts +0 -2
  164. package/lib/execution/engine/mock.d.ts.map +1 -1
  165. package/lib/execution/engine/mock.js +0 -8
  166. package/lib/execution/engine/mock.js.map +1 -1
  167. package/lib/execution/engine/types.d.ts +0 -8
  168. package/lib/execution/engine/types.d.ts.map +1 -1
  169. package/lib/execution/engine/types.js.map +1 -1
  170. package/lib/index.d.ts +1 -1
  171. package/lib/index.d.ts.map +1 -1
  172. package/lib/index.js +1 -1
  173. package/lib/index.js.map +1 -1
  174. package/lib/metrics/metrics/lodestar.d.ts +1 -0
  175. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  176. package/lib/metrics/metrics/lodestar.js +4 -0
  177. package/lib/metrics/metrics/lodestar.js.map +1 -1
  178. package/lib/metrics/metrics.d.ts +7 -3
  179. package/lib/metrics/metrics.d.ts.map +1 -1
  180. package/lib/metrics/metrics.js +4 -4
  181. package/lib/metrics/metrics.js.map +1 -1
  182. package/lib/network/metadata.d.ts +1 -1
  183. package/lib/network/metadata.d.ts.map +1 -1
  184. package/lib/network/metadata.js +17 -17
  185. package/lib/network/metadata.js.map +1 -1
  186. package/lib/network/peers/datastore.d.ts +9 -9
  187. package/lib/network/peers/datastore.d.ts.map +1 -1
  188. package/lib/network/peers/datastore.js +36 -36
  189. package/lib/network/peers/datastore.js.map +1 -1
  190. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  191. package/lib/network/processor/gossipHandlers.js +66 -67
  192. package/lib/network/processor/gossipHandlers.js.map +1 -1
  193. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  194. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  195. package/lib/network/processor/gossipValidatorFn.js +7 -3
  196. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  197. package/lib/network/processor/index.d.ts +9 -0
  198. package/lib/network/processor/index.d.ts.map +1 -1
  199. package/lib/network/processor/index.js +77 -10
  200. package/lib/network/processor/index.js.map +1 -1
  201. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  202. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
  203. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  204. package/lib/node/nodejs.d.ts +3 -1
  205. package/lib/node/nodejs.d.ts.map +1 -1
  206. package/lib/node/nodejs.js +27 -4
  207. package/lib/node/nodejs.js.map +1 -1
  208. package/lib/sync/range/batch.d.ts +11 -5
  209. package/lib/sync/range/batch.d.ts.map +1 -1
  210. package/lib/sync/range/batch.js +21 -17
  211. package/lib/sync/range/batch.js.map +1 -1
  212. package/lib/sync/range/chain.d.ts +4 -4
  213. package/lib/sync/range/chain.d.ts.map +1 -1
  214. package/lib/sync/range/chain.js +12 -6
  215. package/lib/sync/range/chain.js.map +1 -1
  216. package/lib/sync/range/range.d.ts.map +1 -1
  217. package/lib/sync/range/range.js +75 -29
  218. package/lib/sync/range/range.js.map +1 -1
  219. package/lib/sync/unknownBlock.d.ts +14 -7
  220. package/lib/sync/unknownBlock.d.ts.map +1 -1
  221. package/lib/sync/unknownBlock.js +132 -66
  222. package/lib/sync/unknownBlock.js.map +1 -1
  223. package/lib/sync/utils/downloadByRange.d.ts +38 -14
  224. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  225. package/lib/sync/utils/downloadByRange.js +84 -39
  226. package/lib/sync/utils/downloadByRange.js.map +1 -1
  227. package/lib/util/execution.d.ts +11 -11
  228. package/lib/util/execution.d.ts.map +1 -1
  229. package/lib/util/execution.js +6 -2
  230. package/lib/util/execution.js.map +1 -1
  231. package/package.json +17 -17
  232. package/src/api/impl/beacon/blocks/index.ts +7 -4
  233. package/src/api/impl/beacon/index.ts +15 -0
  234. package/src/api/impl/beacon/pool/index.ts +4 -4
  235. package/src/api/impl/beacon/state/index.ts +31 -3
  236. package/src/api/impl/debug/index.ts +28 -19
  237. package/src/api/impl/validator/index.ts +24 -2
  238. package/src/chain/archiveStore/archiveStore.ts +26 -26
  239. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
  240. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
  241. package/src/chain/archiveStore/historicalState/types.ts +2 -2
  242. package/src/chain/archiveStore/historicalState/worker.ts +1 -2
  243. package/src/chain/archiveStore/interface.ts +1 -1
  244. package/src/chain/archiveStore/utils/pruneHistory.ts +9 -4
  245. package/src/chain/blocks/importBlock.ts +13 -3
  246. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  247. package/src/chain/blocks/verifyBlock.ts +1 -0
  248. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
  249. package/src/chain/chain.ts +76 -23
  250. package/src/chain/emitter.ts +7 -1
  251. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  252. package/src/chain/errors/gossipValidation.ts +2 -0
  253. package/src/chain/forkChoice/index.ts +2 -0
  254. package/src/chain/initState.ts +8 -60
  255. package/src/chain/interface.ts +3 -2
  256. package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
  257. package/src/chain/opPools/opPool.ts +91 -18
  258. package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
  259. package/src/chain/opPools/utils.ts +31 -3
  260. package/src/chain/options.ts +3 -3
  261. package/src/chain/prepareNextSlot.ts +6 -3
  262. package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
  263. package/src/chain/produceBlock/produceBlockBody.ts +8 -1
  264. package/src/chain/regen/regen.ts +12 -4
  265. package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
  266. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
  267. package/src/chain/shufflingCache.ts +5 -0
  268. package/src/chain/validation/attesterSlashing.ts +7 -4
  269. package/src/chain/validation/proposerSlashing.ts +7 -4
  270. package/src/chain/validatorMonitor.ts +75 -57
  271. package/src/db/beacon.ts +5 -0
  272. package/src/db/buckets.ts +1 -0
  273. package/src/db/interface.ts +2 -0
  274. package/src/db/repositories/earliestAvailableSlot.ts +19 -0
  275. package/src/db/repositories/proposerSlashing.ts +6 -1
  276. package/src/execution/engine/disabled.ts +0 -8
  277. package/src/execution/engine/http.ts +1 -30
  278. package/src/execution/engine/interface.ts +2 -6
  279. package/src/execution/engine/mock.ts +1 -19
  280. package/src/execution/engine/types.ts +0 -10
  281. package/src/index.ts +1 -1
  282. package/src/metrics/metrics/lodestar.ts +4 -0
  283. package/src/metrics/metrics.ts +21 -6
  284. package/src/network/metadata.ts +17 -17
  285. package/src/network/peers/datastore.ts +36 -36
  286. package/src/network/processor/gossipHandlers.ts +97 -80
  287. package/src/network/processor/gossipValidatorFn.ts +13 -3
  288. package/src/network/processor/index.ts +89 -10
  289. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
  290. package/src/node/nodejs.ts +35 -3
  291. package/src/sync/range/batch.ts +25 -19
  292. package/src/sync/range/chain.ts +15 -8
  293. package/src/sync/range/range.ts +76 -30
  294. package/src/sync/unknownBlock.ts +156 -71
  295. package/src/sync/utils/downloadByRange.ts +123 -58
  296. package/src/util/execution.ts +17 -9
@@ -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
  };
@@ -42,13 +42,13 @@ export class MetadataController {
42
42
  private onSetValue: (key: string, value: Uint8Array) => void;
43
43
  private networkConfig: NetworkConfig;
44
44
  private logger: Logger;
45
- private _metadata: fulu.Metadata;
45
+ private metadata: fulu.Metadata;
46
46
 
47
47
  constructor(opts: MetadataOpts, modules: MetadataModules) {
48
48
  this.networkConfig = modules.networkConfig;
49
49
  this.logger = modules.logger;
50
50
  this.onSetValue = modules.onSetValue;
51
- this._metadata = opts.metadata ?? {
51
+ this.metadata = opts.metadata ?? {
52
52
  ...ssz.fulu.Metadata.defaultValue(),
53
53
  custodyGroupCount: modules.networkConfig.custodyConfig.targetCustodyGroupCount,
54
54
  };
@@ -58,61 +58,61 @@ export class MetadataController {
58
58
  // updateEth2Field() MUST be called with clock epoch
59
59
  this.updateEth2Field(currentEpoch);
60
60
 
61
- this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this._metadata.attnets));
61
+ this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.metadata.attnets));
62
62
 
63
63
  const config = this.networkConfig.config;
64
64
 
65
65
  if (config.getForkSeq(computeStartSlotAtEpoch(currentEpoch)) >= ForkSeq.altair) {
66
66
  // Only persist syncnets if altair fork is already activated. If currentFork is altair but head is phase0
67
67
  // adding syncnets to the ENR is not a problem, we will just have a useless field for a few hours.
68
- this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this._metadata.syncnets));
68
+ this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.metadata.syncnets));
69
69
  }
70
70
 
71
71
  // Set CGC regardless of fork. It may be useful to clients before Fulu, and will be ignored otherwise.
72
- this.onSetValue(ENRKey.cgc, serializeCgc(this._metadata.custodyGroupCount));
72
+ this.onSetValue(ENRKey.cgc, serializeCgc(this.metadata.custodyGroupCount));
73
73
  }
74
74
 
75
75
  get seqNumber(): bigint {
76
- return this._metadata.seqNumber;
76
+ return this.metadata.seqNumber;
77
77
  }
78
78
 
79
79
  get syncnets(): BitArray {
80
- return this._metadata.syncnets;
80
+ return this.metadata.syncnets;
81
81
  }
82
82
 
83
83
  set syncnets(syncnets: BitArray) {
84
84
  this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(syncnets));
85
- this._metadata.seqNumber++;
86
- this._metadata.syncnets = syncnets;
85
+ this.metadata.seqNumber++;
86
+ this.metadata.syncnets = syncnets;
87
87
  }
88
88
 
89
89
  get attnets(): BitArray {
90
- return this._metadata.attnets;
90
+ return this.metadata.attnets;
91
91
  }
92
92
 
93
93
  set attnets(attnets: BitArray) {
94
94
  this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(attnets));
95
- this._metadata.seqNumber++;
96
- this._metadata.attnets = attnets;
95
+ this.metadata.seqNumber++;
96
+ this.metadata.attnets = attnets;
97
97
  }
98
98
 
99
99
  get custodyGroupCount(): number {
100
- return this._metadata.custodyGroupCount;
100
+ return this.metadata.custodyGroupCount;
101
101
  }
102
102
 
103
103
  set custodyGroupCount(custodyGroupCount: number) {
104
- if (custodyGroupCount === this._metadata.custodyGroupCount) {
104
+ if (custodyGroupCount === this.metadata.custodyGroupCount) {
105
105
  return;
106
106
  }
107
107
  this.onSetValue(ENRKey.cgc, serializeCgc(custodyGroupCount));
108
108
  this.logger.debug("Updated cgc field in ENR", {custodyGroupCount});
109
- this._metadata.seqNumber++;
110
- this._metadata.custodyGroupCount = custodyGroupCount;
109
+ this.metadata.seqNumber++;
110
+ this.metadata.custodyGroupCount = custodyGroupCount;
111
111
  }
112
112
 
113
113
  /** Consumers that need the phase0.Metadata type can just ignore the .syncnets property */
114
114
  get json(): fulu.Metadata {
115
- return this._metadata;
115
+ return this.metadata;
116
116
  }
117
117
 
118
118
  /**
@@ -16,23 +16,23 @@ type AwaitGenerator<T, TReturn = any, TNext = any> = Generator<T, TReturn, TNext
16
16
  * This has a memory issue because all peer data stays in memory and loaded at startup time
17
17
  * This is written for libp2p >=0.35, we maintain the same mechanism but with bounded data structure
18
18
  * This datastore includes a memory datastore and fallback to db datastore
19
- * Use an in-memory datastore with last accessed time and _maxMemoryItems, on start it's empty (lazy load)
19
+ * Use an in-memory datastore with last accessed time and maxMemoryItems, on start it's empty (lazy load)
20
20
  * - get: Search in-memory datastore first, if not found search from db.
21
21
  * - If found from db, add back to the in-memory datastore
22
22
  * - Update lastAccessedMs
23
- * - put: move oldest items from memory to db if there are more than _maxMemoryItems items in memory
24
- * - update memory datastore, only update db datastore if there are at least _threshold dirty items
23
+ * - put: move oldest items from memory to db if there are more than maxMemoryItems items in memory
24
+ * - update memory datastore, only update db datastore if there are at least threshold dirty items
25
25
  * - Update lastAccessedMs
26
26
  */
27
27
  export class Eth2PeerDataStore extends BaseDatastore {
28
- private _dbDatastore: LevelDatastore;
29
- private _memoryDatastore: Map<string, MemoryItem>;
28
+ private dbDatastore: LevelDatastore;
29
+ private memoryDatastore: Map<string, MemoryItem>;
30
30
  /** Same to PersistentPeerStore of the old libp2p implementation */
31
- private _dirtyItems = new Set<string>();
31
+ private dirtyItems = new Set<string>();
32
32
  /** If there are more dirty items than threshold, commit data to db */
33
- private _threshold: number;
33
+ private threshold: number;
34
34
  /** If there are more memory items than this, prune oldest ones from memory and move to db */
35
- private _maxMemoryItems: number;
35
+ private maxMemoryItems: number;
36
36
 
37
37
  constructor(
38
38
  dbDatastore: LevelDatastore | string,
@@ -47,18 +47,18 @@ export class Eth2PeerDataStore extends BaseDatastore {
47
47
  throw Error(`Threshold ${threshold} should be at most maxMemoryItems ${maxMemoryItems}`);
48
48
  }
49
49
 
50
- this._dbDatastore = typeof dbDatastore === "string" ? new LevelDatastore(dbDatastore) : dbDatastore;
51
- this._memoryDatastore = new Map();
52
- this._threshold = threshold;
53
- this._maxMemoryItems = maxMemoryItems;
50
+ this.dbDatastore = typeof dbDatastore === "string" ? new LevelDatastore(dbDatastore) : dbDatastore;
51
+ this.memoryDatastore = new Map();
52
+ this.threshold = threshold;
53
+ this.maxMemoryItems = maxMemoryItems;
54
54
  }
55
55
 
56
56
  async open(): Promise<void> {
57
- return this._dbDatastore.open();
57
+ return this.dbDatastore.open();
58
58
  }
59
59
 
60
60
  async close(): Promise<void> {
61
- return this._dbDatastore.close();
61
+ return this.dbDatastore.close();
62
62
  }
63
63
 
64
64
  async put(key: Key, val: Uint8Array, _options?: AbortOptions): Promise<Key> {
@@ -67,23 +67,23 @@ export class Eth2PeerDataStore extends BaseDatastore {
67
67
 
68
68
  /**
69
69
  * Same interface to put with "fromDb" option, if this item is updated back from db
70
- * Move oldest items from memory data store to db if it's over this._maxMemoryItems
70
+ * Move oldest items from memory data store to db if it's over this.maxMemoryItems
71
71
  */
72
72
  async _put(key: Key, val: Uint8Array, fromDb = false): Promise<Key> {
73
- while (this._memoryDatastore.size >= this._maxMemoryItems) {
73
+ while (this.memoryDatastore.size >= this.maxMemoryItems) {
74
74
  // it's likely this is called only 1 time
75
75
  await this.pruneMemoryDatastore();
76
76
  }
77
77
 
78
78
  const keyStr = key.toString();
79
- const memoryItem = this._memoryDatastore.get(keyStr);
79
+ const memoryItem = this.memoryDatastore.get(keyStr);
80
80
  if (memoryItem) {
81
81
  // update existing
82
82
  memoryItem.lastAccessedMs = Date.now();
83
83
  memoryItem.data = val;
84
84
  } else {
85
85
  // new
86
- this._memoryDatastore.set(keyStr, {data: val, lastAccessedMs: Date.now()});
86
+ this.memoryDatastore.set(keyStr, {data: val, lastAccessedMs: Date.now()});
87
87
  }
88
88
 
89
89
  if (!fromDb) await this._addDirtyItem(keyStr);
@@ -98,15 +98,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
98
98
  */
99
99
  async get(key: Key, options?: AbortOptions): Promise<Uint8Array> {
100
100
  const keyStr = key.toString();
101
- const memoryItem = this._memoryDatastore.get(keyStr);
101
+ const memoryItem = this.memoryDatastore.get(keyStr);
102
102
  if (memoryItem) {
103
103
  memoryItem.lastAccessedMs = Date.now();
104
104
  return memoryItem.data;
105
105
  }
106
106
 
107
107
  // this throws error if not found
108
- const dbValue = await this._dbDatastore.get(key, options);
109
- // don't call this._memoryDatastore.set directly
108
+ const dbValue = await this.dbDatastore.get(key, options);
109
+ // don't call this.memoryDatastore.set directly
110
110
  // we want to get through prune() logic with fromDb as true
111
111
  await this._put(key, dbValue, true);
112
112
  return dbValue;
@@ -125,30 +125,30 @@ export class Eth2PeerDataStore extends BaseDatastore {
125
125
  }
126
126
 
127
127
  async delete(key: Key, options?: AbortOptions): Promise<void> {
128
- this._memoryDatastore.delete(key.toString());
129
- await this._dbDatastore.delete(key, options);
128
+ this.memoryDatastore.delete(key.toString());
129
+ await this.dbDatastore.delete(key, options);
130
130
  }
131
131
 
132
132
  async *_all(q: Query, options?: AbortOptions): AwaitGenerator<Pair> {
133
- for (const [key, value] of this._memoryDatastore.entries()) {
133
+ for (const [key, value] of this.memoryDatastore.entries()) {
134
134
  yield {
135
135
  key: new Key(key),
136
136
  value: value.data,
137
137
  };
138
138
  }
139
- yield* this._dbDatastore.query(q, options);
139
+ yield* this.dbDatastore.query(q, options);
140
140
  }
141
141
 
142
142
  async *_allKeys(q: KeyQuery, options?: AbortOptions): AwaitGenerator<Key> {
143
- for (const key of this._memoryDatastore.keys()) {
143
+ for (const key of this.memoryDatastore.keys()) {
144
144
  yield new Key(key);
145
145
  }
146
- yield* this._dbDatastore.queryKeys(q, options);
146
+ yield* this.dbDatastore.queryKeys(q, options);
147
147
  }
148
148
 
149
149
  private async _addDirtyItem(keyStr: string): Promise<void> {
150
- this._dirtyItems.add(keyStr);
151
- if (this._dirtyItems.size >= this._threshold) {
150
+ this.dirtyItems.add(keyStr);
151
+ if (this.dirtyItems.size >= this.threshold) {
152
152
  try {
153
153
  await this._commitData();
154
154
  } catch (_e) {}
@@ -156,15 +156,15 @@ export class Eth2PeerDataStore extends BaseDatastore {
156
156
  }
157
157
 
158
158
  private async _commitData(): Promise<void> {
159
- const batch = this._dbDatastore.batch();
160
- for (const keyStr of this._dirtyItems) {
161
- const memoryItem = this._memoryDatastore.get(keyStr);
159
+ const batch = this.dbDatastore.batch();
160
+ for (const keyStr of this.dirtyItems) {
161
+ const memoryItem = this.memoryDatastore.get(keyStr);
162
162
  if (memoryItem) {
163
163
  batch.put(new Key(keyStr), memoryItem.data);
164
164
  }
165
165
  }
166
166
  await batch.commit();
167
- this._dirtyItems.clear();
167
+ this.dirtyItems.clear();
168
168
  }
169
169
 
170
170
  /**
@@ -175,7 +175,7 @@ export class Eth2PeerDataStore extends BaseDatastore {
175
175
  let oldestKey: string | undefined = undefined;
176
176
  let oldestValue: Uint8Array | undefined = undefined;
177
177
 
178
- for (const [key, value] of this._memoryDatastore) {
178
+ for (const [key, value] of this.memoryDatastore) {
179
179
  if (value.lastAccessedMs < oldestAccessedMs) {
180
180
  oldestAccessedMs = value.lastAccessedMs;
181
181
  oldestKey = key;
@@ -184,8 +184,8 @@ export class Eth2PeerDataStore extends BaseDatastore {
184
184
  }
185
185
 
186
186
  if (oldestKey && oldestValue) {
187
- await this._dbDatastore.put(new Key(oldestKey), oldestValue);
188
- this._memoryDatastore.delete(oldestKey);
187
+ await this.dbDatastore.put(new Key(oldestKey), oldestValue);
188
+ this.memoryDatastore.delete(oldestKey);
189
189
  }
190
190
  }
191
191
  }
@@ -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;
@@ -1065,12 +1079,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1065
1079
  const {serializedData} = gossipData;
1066
1080
  const {fork} = topic.boundary;
1067
1081
  const attesterSlashing = sszDeserialize(topic, serializedData);
1068
- await validateGossipAttesterSlashing(chain, attesterSlashing);
1082
+ const verifiedDomains = await validateGossipAttesterSlashing(chain, attesterSlashing);
1069
1083
 
1070
1084
  // Handler - deferred to next event loop so the validation result propagates first
1071
1085
  callInNextEventLoop(() => {
1072
1086
  try {
1073
- chain.opPool.insertAttesterSlashing(fork, attesterSlashing);
1087
+ chain.opPool.insertAttesterSlashing(fork, attesterSlashing, verifiedDomains);
1074
1088
  chain.forkChoice.onAttesterSlashing(attesterSlashing);
1075
1089
  chain.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing);
1076
1090
  } catch (e) {
@@ -1089,12 +1103,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1089
1103
  }: GossipHandlerParamGeneric<GossipType.proposer_slashing>) => {
1090
1104
  const {serializedData} = gossipData;
1091
1105
  const proposerSlashing = sszDeserialize(topic, serializedData);
1092
- await validateGossipProposerSlashing(chain, proposerSlashing);
1106
+ const verifiedDomain = await validateGossipProposerSlashing(chain, proposerSlashing);
1093
1107
 
1094
1108
  // Handler - deferred to next event loop so the validation result propagates first
1095
1109
  callInNextEventLoop(() => {
1096
1110
  try {
1097
- chain.opPool.insertProposerSlashing(proposerSlashing);
1111
+ chain.opPool.insertProposerSlashing(proposerSlashing, verifiedDomain);
1098
1112
  chain.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing);
1099
1113
  } catch (e) {
1100
1114
  logger.debug(
@@ -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},