@lodestar/beacon-node 1.49.0 → 1.50.0-dev.01cc10ce70

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 (268) 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 +28 -0
  11. package/lib/api/impl/beacon/state/index.js.map +1 -1
  12. package/lib/api/impl/validator/index.d.ts +1 -0
  13. package/lib/api/impl/validator/index.d.ts.map +1 -1
  14. package/lib/api/impl/validator/index.js +17 -1
  15. package/lib/api/impl/validator/index.js.map +1 -1
  16. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  17. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/archiveStore.js +27 -4
  19. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  20. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
  21. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  24. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
  25. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  26. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
  27. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  28. package/lib/chain/archiveStore/historicalState/worker.js +1 -1
  29. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  30. package/lib/chain/archiveStore/interface.d.ts +1 -1
  31. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  32. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
  33. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  34. package/lib/chain/archiveStore/utils/pruneHistory.js +4 -3
  35. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  36. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  37. package/lib/chain/blocks/importBlock.js +4 -0
  38. package/lib/chain/blocks/importBlock.js.map +1 -1
  39. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  40. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  41. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  42. package/lib/chain/blocks/verifyBlock.js +1 -1
  43. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  44. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
  45. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  46. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
  47. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  48. package/lib/chain/chain.d.ts +5 -4
  49. package/lib/chain/chain.d.ts.map +1 -1
  50. package/lib/chain/chain.js +32 -16
  51. package/lib/chain/chain.js.map +1 -1
  52. package/lib/chain/emitter.d.ts +7 -1
  53. package/lib/chain/emitter.d.ts.map +1 -1
  54. package/lib/chain/emitter.js +5 -0
  55. package/lib/chain/emitter.js.map +1 -1
  56. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  57. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  58. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  59. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  60. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  61. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  62. package/lib/chain/errors/gossipValidation.js +2 -0
  63. package/lib/chain/errors/gossipValidation.js.map +1 -1
  64. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  65. package/lib/chain/forkChoice/index.js +2 -0
  66. package/lib/chain/forkChoice/index.js.map +1 -1
  67. package/lib/chain/initState.d.ts +2 -9
  68. package/lib/chain/initState.d.ts.map +1 -1
  69. package/lib/chain/initState.js +5 -38
  70. package/lib/chain/initState.js.map +1 -1
  71. package/lib/chain/interface.d.ts +2 -2
  72. package/lib/chain/interface.d.ts.map +1 -1
  73. package/lib/chain/opPools/opPool.d.ts +5 -4
  74. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  75. package/lib/chain/opPools/opPool.js +62 -18
  76. package/lib/chain/opPools/opPool.js.map +1 -1
  77. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
  78. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  79. package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
  80. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  81. package/lib/chain/opPools/utils.d.ts +4 -1
  82. package/lib/chain/opPools/utils.d.ts.map +1 -1
  83. package/lib/chain/opPools/utils.js +11 -1
  84. package/lib/chain/opPools/utils.js.map +1 -1
  85. package/lib/chain/options.d.ts +1 -1
  86. package/lib/chain/options.d.ts.map +1 -1
  87. package/lib/chain/options.js +1 -1
  88. package/lib/chain/options.js.map +1 -1
  89. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  90. package/lib/chain/prepareNextSlot.js +5 -2
  91. package/lib/chain/prepareNextSlot.js.map +1 -1
  92. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
  93. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  94. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  95. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  96. package/lib/chain/produceBlock/produceBlockBody.js +6 -2
  97. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  98. package/lib/chain/regen/regen.d.ts +2 -0
  99. package/lib/chain/regen/regen.d.ts.map +1 -1
  100. package/lib/chain/regen/regen.js +10 -4
  101. package/lib/chain/regen/regen.js.map +1 -1
  102. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
  103. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  104. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
  105. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  106. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  107. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  108. package/lib/chain/validation/attesterSlashing.js +3 -0
  109. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  110. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  111. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  112. package/lib/chain/validation/proposerSlashing.js +3 -0
  113. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  114. package/lib/chain/validatorMonitor.d.ts +5 -1
  115. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  116. package/lib/chain/validatorMonitor.js +64 -54
  117. package/lib/chain/validatorMonitor.js.map +1 -1
  118. package/lib/db/beacon.d.ts.map +1 -1
  119. package/lib/db/beacon.js +2 -0
  120. package/lib/db/beacon.js.map +1 -1
  121. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  122. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  123. package/lib/db/repositories/proposerSlashing.js +5 -1
  124. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  125. package/lib/execution/engine/disabled.d.ts +0 -2
  126. package/lib/execution/engine/disabled.d.ts.map +1 -1
  127. package/lib/execution/engine/disabled.js +0 -6
  128. package/lib/execution/engine/disabled.js.map +1 -1
  129. package/lib/execution/engine/http.d.ts +1 -3
  130. package/lib/execution/engine/http.d.ts.map +1 -1
  131. package/lib/execution/engine/http.js +7 -21
  132. package/lib/execution/engine/http.js.map +1 -1
  133. package/lib/execution/engine/interface.d.ts +2 -4
  134. package/lib/execution/engine/interface.d.ts.map +1 -1
  135. package/lib/execution/engine/jsonRpcHttpClient.d.ts +2 -1
  136. package/lib/execution/engine/jsonRpcHttpClient.d.ts.map +1 -1
  137. package/lib/execution/engine/jsonRpcHttpClient.js +6 -1
  138. package/lib/execution/engine/jsonRpcHttpClient.js.map +1 -1
  139. package/lib/execution/engine/mock.d.ts +0 -2
  140. package/lib/execution/engine/mock.d.ts.map +1 -1
  141. package/lib/execution/engine/mock.js +0 -8
  142. package/lib/execution/engine/mock.js.map +1 -1
  143. package/lib/execution/engine/transportMetrics.d.ts +13 -0
  144. package/lib/execution/engine/transportMetrics.d.ts.map +1 -0
  145. package/lib/execution/engine/transportMetrics.js +2 -0
  146. package/lib/execution/engine/transportMetrics.js.map +1 -0
  147. package/lib/execution/engine/types.d.ts +0 -8
  148. package/lib/execution/engine/types.d.ts.map +1 -1
  149. package/lib/execution/engine/types.js.map +1 -1
  150. package/lib/index.d.ts +1 -1
  151. package/lib/index.d.ts.map +1 -1
  152. package/lib/index.js +1 -1
  153. package/lib/index.js.map +1 -1
  154. package/lib/metrics/metrics/beacon.d.ts +1 -1
  155. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  156. package/lib/metrics/metrics/beacon.js.map +1 -1
  157. package/lib/metrics/metrics/lodestar.d.ts +6 -0
  158. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  159. package/lib/metrics/metrics/lodestar.js +10 -0
  160. package/lib/metrics/metrics/lodestar.js.map +1 -1
  161. package/lib/metrics/metrics.d.ts +7 -3
  162. package/lib/metrics/metrics.d.ts.map +1 -1
  163. package/lib/metrics/metrics.js +4 -4
  164. package/lib/metrics/metrics.js.map +1 -1
  165. package/lib/network/metadata.d.ts +1 -1
  166. package/lib/network/metadata.d.ts.map +1 -1
  167. package/lib/network/metadata.js +17 -17
  168. package/lib/network/metadata.js.map +1 -1
  169. package/lib/network/peers/datastore.d.ts +9 -9
  170. package/lib/network/peers/datastore.d.ts.map +1 -1
  171. package/lib/network/peers/datastore.js +36 -36
  172. package/lib/network/peers/datastore.js.map +1 -1
  173. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  174. package/lib/network/processor/gossipHandlers.js +7 -4
  175. package/lib/network/processor/gossipHandlers.js.map +1 -1
  176. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  177. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  178. package/lib/network/processor/gossipValidatorFn.js +7 -3
  179. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  180. package/lib/network/processor/index.d.ts +9 -0
  181. package/lib/network/processor/index.d.ts.map +1 -1
  182. package/lib/network/processor/index.js +77 -10
  183. package/lib/network/processor/index.js.map +1 -1
  184. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  185. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
  186. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  187. package/lib/node/nodejs.d.ts.map +1 -1
  188. package/lib/node/nodejs.js +25 -3
  189. package/lib/node/nodejs.js.map +1 -1
  190. package/lib/sync/range/batch.d.ts +6 -0
  191. package/lib/sync/range/batch.d.ts.map +1 -1
  192. package/lib/sync/range/batch.js +10 -0
  193. package/lib/sync/range/batch.js.map +1 -1
  194. package/lib/sync/range/chain.d.ts.map +1 -1
  195. package/lib/sync/range/chain.js +5 -1
  196. package/lib/sync/range/chain.js.map +1 -1
  197. package/lib/sync/range/range.d.ts.map +1 -1
  198. package/lib/sync/range/range.js +50 -18
  199. package/lib/sync/range/range.js.map +1 -1
  200. package/lib/sync/unknownBlock.d.ts +9 -5
  201. package/lib/sync/unknownBlock.d.ts.map +1 -1
  202. package/lib/sync/unknownBlock.js +88 -46
  203. package/lib/sync/unknownBlock.js.map +1 -1
  204. package/lib/util/execution.d.ts +11 -11
  205. package/lib/util/execution.d.ts.map +1 -1
  206. package/lib/util/execution.js +11 -7
  207. package/lib/util/execution.js.map +1 -1
  208. package/package.json +16 -16
  209. package/src/api/impl/beacon/blocks/index.ts +7 -4
  210. package/src/api/impl/beacon/index.ts +15 -0
  211. package/src/api/impl/beacon/pool/index.ts +4 -4
  212. package/src/api/impl/beacon/state/index.ts +36 -0
  213. package/src/api/impl/validator/index.ts +16 -2
  214. package/src/chain/archiveStore/archiveStore.ts +30 -5
  215. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
  216. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
  217. package/src/chain/archiveStore/historicalState/types.ts +2 -2
  218. package/src/chain/archiveStore/historicalState/worker.ts +1 -2
  219. package/src/chain/archiveStore/interface.ts +1 -1
  220. package/src/chain/archiveStore/utils/pruneHistory.ts +6 -4
  221. package/src/chain/blocks/importBlock.ts +4 -0
  222. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  223. package/src/chain/blocks/verifyBlock.ts +1 -0
  224. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
  225. package/src/chain/chain.ts +38 -19
  226. package/src/chain/emitter.ts +7 -1
  227. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  228. package/src/chain/errors/gossipValidation.ts +2 -0
  229. package/src/chain/forkChoice/index.ts +2 -0
  230. package/src/chain/initState.ts +8 -60
  231. package/src/chain/interface.ts +2 -2
  232. package/src/chain/opPools/opPool.ts +91 -18
  233. package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
  234. package/src/chain/opPools/utils.ts +31 -3
  235. package/src/chain/options.ts +2 -2
  236. package/src/chain/prepareNextSlot.ts +6 -3
  237. package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
  238. package/src/chain/produceBlock/produceBlockBody.ts +8 -1
  239. package/src/chain/regen/regen.ts +12 -4
  240. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
  241. package/src/chain/validation/attesterSlashing.ts +7 -4
  242. package/src/chain/validation/proposerSlashing.ts +7 -4
  243. package/src/chain/validatorMonitor.ts +75 -57
  244. package/src/db/beacon.ts +2 -0
  245. package/src/db/repositories/proposerSlashing.ts +6 -1
  246. package/src/execution/engine/disabled.ts +0 -8
  247. package/src/execution/engine/http.ts +7 -34
  248. package/src/execution/engine/interface.ts +2 -6
  249. package/src/execution/engine/jsonRpcHttpClient.ts +9 -2
  250. package/src/execution/engine/mock.ts +1 -19
  251. package/src/execution/engine/transportMetrics.ts +9 -0
  252. package/src/execution/engine/types.ts +0 -10
  253. package/src/index.ts +1 -1
  254. package/src/metrics/metrics/beacon.ts +1 -1
  255. package/src/metrics/metrics/lodestar.ts +10 -0
  256. package/src/metrics/metrics.ts +21 -6
  257. package/src/network/metadata.ts +17 -17
  258. package/src/network/peers/datastore.ts +36 -36
  259. package/src/network/processor/gossipHandlers.ts +7 -4
  260. package/src/network/processor/gossipValidatorFn.ts +13 -3
  261. package/src/network/processor/index.ts +89 -10
  262. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
  263. package/src/node/nodejs.ts +31 -3
  264. package/src/sync/range/batch.ts +11 -0
  265. package/src/sync/range/chain.ts +6 -1
  266. package/src/sync/range/range.ts +50 -18
  267. package/src/sync/unknownBlock.ts +99 -50
  268. package/src/util/execution.ts +26 -14
@@ -71,7 +71,7 @@ import {BufferPool} from "../util/bufferPool.js";
71
71
  import {Clock, ClockEvent, IClock} from "../util/clock.js";
72
72
  import {CustodyConfig, getValidatorsCustodyRequirement} from "../util/dataColumns.js";
73
73
  import {callInNextEventLoop} from "../util/eventLoop.js";
74
- import {ReconstructMismatchPolicy, isRebuildMiss, reconstructExecutionPayloadEnvelopes} from "../util/execution.js";
74
+ import {ReconstructMissPolicy, isRebuildMiss, reconstructExecutionPayloadEnvelopes} from "../util/execution.js";
75
75
  import {ensureDir, writeIfNotExist} from "../util/file.js";
76
76
  import {isOptimisticBlock} from "../util/forkChoice.js";
77
77
  import {JobItemQueue} from "../util/queue/itemQueue.js";
@@ -432,6 +432,7 @@ export class BeaconChain implements IBeaconChain {
432
432
  blockStateCache,
433
433
  checkpointStateCache,
434
434
  seenBlockInputCache: this.seenBlockInputCache,
435
+ serializedCache: this.serializedCache,
435
436
  db,
436
437
  metrics,
437
438
  validatorMonitor,
@@ -611,7 +612,7 @@ export class BeaconChain implements IBeaconChain {
611
612
  /** Populate in-memory caches with persisted data. Call at least once on startup */
612
613
  async loadFromDisk(): Promise<void> {
613
614
  await this.regen.init();
614
- await this.opPool.fromPersisted(this.db);
615
+ await this.opPool.fromPersisted(this.db, this.getHeadState(), this.bls, this.clock.currentSlot);
615
616
  }
616
617
 
617
618
  /** Persist in-memory data to the DB. Call at least once before stopping the process */
@@ -702,6 +703,26 @@ export class BeaconChain implements IBeaconChain {
702
703
  stateRoot: RootHex,
703
704
  opts?: StateGetOpts
704
705
  ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
706
+ const finalizedBlock = this.forkChoice.getFinalizedBlock();
707
+ const finalizedCheckpoint = this.forkChoice.getFinalizedCheckpoint();
708
+ // Checkpoint state only equals the block post-state if the block is at the epoch start slot
709
+ if (
710
+ finalizedBlock.stateRoot === stateRoot &&
711
+ finalizedBlock.slot === computeStartSlotAtEpoch(finalizedCheckpoint.epoch)
712
+ ) {
713
+ const state = this.regen.getCheckpointStateSync({
714
+ epoch: finalizedCheckpoint.epoch,
715
+ rootHex: finalizedCheckpoint.rootHex,
716
+ });
717
+ if (state) {
718
+ return {
719
+ state,
720
+ executionOptimistic: isOptimisticBlock(finalizedBlock),
721
+ finalized: finalizedCheckpoint.epoch !== GENESIS_EPOCH,
722
+ };
723
+ }
724
+ }
725
+
705
726
  if (opts?.allowRegen) {
706
727
  const state = await this.regen.getState(stateRoot, RegenCaller.restApi);
707
728
  const block = this.forkChoice.getBlockDefaultStatus(
@@ -934,12 +955,13 @@ export class BeaconChain implements IBeaconChain {
934
955
 
935
956
  /**
936
957
  * Batch variant: archived header envelopes are rebuilt 32 per EL round-trip. Aligned with `requests`.
937
- * A body root mismatch is a local inconsistency: `"throw"` surfaces it, `"omit"` logs it and
938
- * returns null for that entry, for peer-facing paths where the spec allows omission.
958
+ * An archived envelope that cannot be rebuilt (EL-unavailable body or body root mismatch) is not the
959
+ * same as an unknown one: `"throw"` surfaces it, `"omit"` returns null for that entry, for
960
+ * peer-facing paths where the spec allows omission.
939
961
  */
940
962
  async getSerializedExecutionPayloadEnvelopes(
941
963
  requests: {blockSlot: Slot; blockRootHex: RootHex}[],
942
- onMismatch: ReconstructMismatchPolicy = "throw"
964
+ onMiss: ReconstructMissPolicy = "throw"
943
965
  ): Promise<(Uint8Array | null)[]> {
944
966
  const out: (Uint8Array | null)[] = new Array(requests.length).fill(null);
945
967
  const headerEnvelopes: gloas.SignedExecutionPayloadHeaderEnvelope[] = [];
@@ -978,14 +1000,14 @@ export class BeaconChain implements IBeaconChain {
978
1000
  for (let j = 0; j < rebuilt.length; j++) {
979
1001
  const result = rebuilt[j];
980
1002
  if (isRebuildMiss(result)) {
981
- if (result.reason === "mismatch") {
982
- if (onMismatch === "throw") throw result.error;
983
- this.logger.debug(
984
- "Archived envelope failed body root check against EL bodies",
985
- {slot: result.slot},
986
- result.error
987
- );
988
- }
1003
+ if (onMiss === "throw") throw result.error;
1004
+ this.logger.debug(
1005
+ result.reason === "mismatch"
1006
+ ? "Archived envelope failed body root check against EL bodies"
1007
+ : "EL cannot serve bodies for archived envelope, omitting",
1008
+ {slot: result.slot},
1009
+ result.error
1010
+ );
989
1011
  continue;
990
1012
  }
991
1013
  out[headerEnvelopeIdxs[j]] = ssz.gloas.SignedExecutionPayloadEnvelope.serialize(result);
@@ -1016,10 +1038,7 @@ export class BeaconChain implements IBeaconChain {
1016
1038
  const [result] = await reconstructExecutionPayloadEnvelopes(this.executionEngine, this.metrics, [
1017
1039
  archived.headerEnvelope,
1018
1040
  ]);
1019
- if (isRebuildMiss(result)) {
1020
- if (result.reason === "mismatch") throw result.error;
1021
- return null;
1022
- }
1041
+ if (isRebuildMiss(result)) throw result.error;
1023
1042
  return result;
1024
1043
  }
1025
1044
 
@@ -1329,13 +1348,13 @@ export class BeaconChain implements IBeaconChain {
1329
1348
  };
1330
1349
 
1331
1350
  try {
1332
- await validateApiProposerSlashing(this, proposerSlashing);
1351
+ const verifiedDomain = await validateApiProposerSlashing(this, proposerSlashing);
1352
+ this.opPool.insertProposerSlashing(proposerSlashing, verifiedDomain);
1333
1353
  } catch (e) {
1334
1354
  this.logger.debug("Produced proposer slashing is not valid", {slot: blockSlot, proposerIndex}, e as Error);
1335
1355
  return;
1336
1356
  }
1337
1357
 
1338
- this.opPool.insertProposerSlashing(proposerSlashing);
1339
1358
  this.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing);
1340
1359
  this.emitter.emit(ChainEvent.publishProposerSlashing, proposerSlashing);
1341
1360
  this.metrics?.opPool.proposerSlashingsProduced.inc();
@@ -1,7 +1,7 @@
1
1
  import {EventEmitter} from "node:events";
2
2
  import {StrictEventEmitter} from "strict-event-emitter-types";
3
3
  import {routes} from "@lodestar/api";
4
- import {CheckpointWithHex} from "@lodestar/fork-choice";
4
+ import {CheckpointWithHex, PtcQuorumEvent} from "@lodestar/fork-choice";
5
5
  import {IBeaconStateView} from "@lodestar/state-transition";
6
6
  import {DataColumnSidecar, RootHex, Slot, deneb, phase0} from "@lodestar/types";
7
7
  import {PeerIdStr} from "../util/peerId.js";
@@ -36,6 +36,11 @@ export enum ChainEvent {
36
36
  * This event is guaranteed to be triggered whenever the fork choice finalized checkpoint is updated. This is either in response to a newly processed block or a new clock tick.
37
37
  */
38
38
  forkChoiceFinalized = "forkChoice:finalized",
39
+ /**
40
+ * This event signals that the PTC verdict on a block was decided: a majority against payload presence or blob data
41
+ * availability, or a majority for both.
42
+ */
43
+ forkChoicePtcQuorum = "forkChoice:ptcQuorum",
39
44
  /**
40
45
  * This event signals that dependent services (e.g. custody sampling) should update to account for the new target group count.
41
46
  */
@@ -112,6 +117,7 @@ export type IChainEvents = ApiEvents & {
112
117
 
113
118
  [ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithHex) => void;
114
119
  [ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithHex) => void;
120
+ [ChainEvent.forkChoicePtcQuorum]: (data: PtcQuorumEvent) => void;
115
121
 
116
122
  [ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;
117
123
 
@@ -4,6 +4,8 @@ import {LodestarError} from "@lodestar/utils";
4
4
  export enum EnvelopeReconstructionErrorCode {
5
5
  /** The EL failed to answer engine_getPayloadBodiesByHashV2 (transport / EL down). Transient. */
6
6
  ENGINE_UNAVAILABLE = "ENVELOPE_RECONSTRUCTION_ERROR_ENGINE_UNAVAILABLE",
7
+ /** The EL does not have the block or its block access list (pruned, or not yet re-indexed after a restart). May recover. */
8
+ BODY_UNAVAILABLE = "ENVELOPE_RECONSTRUCTION_ERROR_BODY_UNAVAILABLE",
7
9
  /** A body returned by the EL does not hash to the root stored in the header envelope. Local DB/EL inconsistency. */
8
10
  BODY_ROOT_MISMATCH = "ENVELOPE_RECONSTRUCTION_ERROR_BODY_ROOT_MISMATCH",
9
11
  /** A by-range stream stopped short at a slot that cannot be served; the response so far is still valid */
@@ -12,6 +14,7 @@ export enum EnvelopeReconstructionErrorCode {
12
14
 
13
15
  export type EnvelopeReconstructionErrorType =
14
16
  | {code: EnvelopeReconstructionErrorCode.ENGINE_UNAVAILABLE}
17
+ | {code: EnvelopeReconstructionErrorCode.BODY_UNAVAILABLE; slot: Slot}
15
18
  | {
16
19
  code: EnvelopeReconstructionErrorCode.BODY_ROOT_MISMATCH;
17
20
  slot: Slot;
@@ -9,6 +9,8 @@ export enum GossipErrorCode {
9
9
  INVALID_SERIALIZED_BYTES_ERROR_CODE = "GOSSIP_ERROR_INVALID_SERIALIZED_BYTES",
10
10
  PAST_SLOT = "GOSSIP_ERROR_PAST_SLOT",
11
11
  FUTURE_SLOT = "GOSSIP_ERROR_FUTURE_SLOT",
12
+ FATAL_PEER = "GOSSIP_ERROR_FATAL_PEER",
13
+ INVALID_SLOT = "GOSSIP_ERROR_INVALID_SLOT",
12
14
  }
13
15
 
14
16
  export class GossipActionError<T extends {code: string}> extends LodestarError<T> {
@@ -124,6 +124,7 @@ export function initializeForkChoiceFromFinalizedState(
124
124
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
125
125
  onFastConfirmation: ({block, slot, currentSlot}) =>
126
126
  emitter.emit(routes.events.EventType.fastConfirmation, {block, slot, currentSlot}),
127
+ onPtcQuorum: (data) => emitter.emit(ChainEvent.forkChoicePtcQuorum, data),
127
128
  }
128
129
  ),
129
130
 
@@ -222,6 +223,7 @@ export function initializeForkChoiceFromUnfinalizedState(
222
223
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
223
224
  onFastConfirmation: ({block, slot, currentSlot}) =>
224
225
  emitter.emit(routes.events.EventType.fastConfirmation, {block, slot, currentSlot}),
226
+ onPtcQuorum: (data) => emitter.emit(ChainEvent.forkChoicePtcQuorum, data),
225
227
  }
226
228
  );
227
229
 
@@ -1,14 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {ForkPostGloas, ForkSeq, ZERO_HASH} from "@lodestar/params";
3
- import {
4
- BeaconStateAllForks,
5
- BeaconStateGloas,
6
- IBeaconStateView,
7
- computeEpochAtSlot,
8
- computeStartSlotAtEpoch,
9
- } from "@lodestar/state-transition";
3
+ import {BeaconStateAllForks, IBeaconStateView, computeEpochAtSlot, isStatePostGloas} from "@lodestar/state-transition";
10
4
  import {SignedBeaconBlock, ssz} from "@lodestar/types";
11
- import {Logger, byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
5
+ import {LodestarError, Logger, byteArrayEquals, toRootHex} from "@lodestar/utils";
12
6
  import {GENESIS_SLOT} from "../constants/index.js";
13
7
  import {IBeaconDb} from "../db/index.js";
14
8
  import {Metrics} from "../metrics/index.js";
@@ -17,7 +11,7 @@ import {getStateTypeFromBytes} from "../util/multifork.js";
17
11
  export async function persistAnchorState(
18
12
  config: ChainForkConfig,
19
13
  db: IBeaconDb,
20
- anchorState: BeaconStateAllForks,
14
+ anchorState: IBeaconStateView,
21
15
  anchorStateBytes: Uint8Array
22
16
  ): Promise<void> {
23
17
  if (anchorState.slot === GENESIS_SLOT) {
@@ -33,7 +27,8 @@ export async function persistAnchorState(
33
27
  const latestBlockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(latestBlockHeader);
34
28
 
35
29
  if (!byteArrayEquals(blockRoot, latestBlockRoot)) {
36
- throw Error(
30
+ throw new LodestarError(
31
+ {code: "GENESIS_BLOCK_ROOT_MISMATCH"},
37
32
  `Genesis block root ${toRootHex(blockRoot)} does not match genesis state latest block root ${toRootHex(latestBlockRoot)}`
38
33
  );
39
34
  }
@@ -48,16 +43,15 @@ export async function persistAnchorState(
48
43
  }
49
44
  }
50
45
 
51
- export function createGenesisBlock(config: ChainForkConfig, genesisState: BeaconStateAllForks): SignedBeaconBlock {
46
+ export function createGenesisBlock(config: ChainForkConfig, genesisState: IBeaconStateView): SignedBeaconBlock {
52
47
  const types = config.getForkTypes(GENESIS_SLOT);
53
48
  const genesisBlock = types.SignedBeaconBlock.defaultValue();
54
49
  const stateRoot = genesisState.hashTreeRoot();
55
50
  genesisBlock.message.stateRoot = stateRoot;
56
51
 
57
- if (config.getForkSeq(GENESIS_SLOT) >= ForkSeq.gloas) {
52
+ if (config.getForkSeq(GENESIS_SLOT) >= ForkSeq.gloas && isStatePostGloas(genesisState)) {
58
53
  const gloasBlock = genesisBlock as SignedBeaconBlock<ForkPostGloas>;
59
- const gloasState = genesisState as BeaconStateGloas;
60
- gloasBlock.message.body.signedExecutionPayloadBid.message = gloasState.latestExecutionPayloadBid.toValue();
54
+ gloasBlock.message.body.signedExecutionPayloadBid.message = genesisState.latestExecutionPayloadBid;
61
55
  }
62
56
 
63
57
  return genesisBlock;
@@ -86,52 +80,6 @@ export async function initStateFromDb(
86
80
  return state;
87
81
  }
88
82
 
89
- /**
90
- * Initialize and persist an anchor state (either weak subjectivity or genesis)
91
- */
92
- export async function checkAndPersistAnchorState(
93
- config: ChainForkConfig,
94
- db: IBeaconDb,
95
- logger: Logger,
96
- anchorState: BeaconStateAllForks,
97
- anchorStateBytes: Uint8Array,
98
- {
99
- isWithinWeakSubjectivityPeriod,
100
- isCheckpointState,
101
- }: {isWithinWeakSubjectivityPeriod: boolean; isCheckpointState: boolean}
102
- ): Promise<void> {
103
- const expectedFork = config.getForkInfo(computeStartSlotAtEpoch(anchorState.fork.epoch));
104
- const expectedForkVersion = toHex(expectedFork.version);
105
- const stateFork = toHex(anchorState.fork.currentVersion);
106
- if (stateFork !== expectedForkVersion) {
107
- throw Error(
108
- `State current fork version ${stateFork} not equal to current config ${expectedForkVersion}. Maybe caused by importing a state from a different network`
109
- );
110
- }
111
-
112
- const stateInfo = isCheckpointState ? "checkpoint" : "db";
113
- if (isWithinWeakSubjectivityPeriod) {
114
- logger.info(`Initializing beacon from a valid ${stateInfo} state`, {
115
- slot: anchorState.slot,
116
- epoch: computeEpochAtSlot(anchorState.slot),
117
- stateRoot: toRootHex(anchorState.hashTreeRoot()),
118
- isWithinWeakSubjectivityPeriod,
119
- });
120
- } else {
121
- logger.warn(`Initializing from a stale ${stateInfo} state vulnerable to long range attacks`, {
122
- slot: anchorState.slot,
123
- epoch: computeEpochAtSlot(anchorState.slot),
124
- stateRoot: toRootHex(anchorState.hashTreeRoot()),
125
- isWithinWeakSubjectivityPeriod,
126
- });
127
- logger.warn("Checkpoint sync recommended, please use --help to see checkpoint sync options");
128
- }
129
-
130
- if (isCheckpointState || anchorState.slot === GENESIS_SLOT) {
131
- await persistAnchorState(config, db, anchorState, anchorStateBytes);
132
- }
133
- }
134
-
135
83
  export function initBeaconMetrics(metrics: Metrics, state: IBeaconStateView): void {
136
84
  metrics.headSlot.set(state.slot);
137
85
  metrics.previousJustifiedEpoch.set(state.previousJustifiedCheckpoint.epoch);
@@ -30,7 +30,7 @@ import {Metrics} from "../metrics/metrics.js";
30
30
  import {BufferPool} from "../util/bufferPool.js";
31
31
  import {IClock} from "../util/clock.js";
32
32
  import {CustodyConfig} from "../util/dataColumns.js";
33
- import {ReconstructMismatchPolicy} from "../util/execution.js";
33
+ import {ReconstructMissPolicy} from "../util/execution.js";
34
34
  import {SerializedCache} from "../util/serializedCache.js";
35
35
  import {IArchiveStore} from "./archiveStore/interface.js";
36
36
  import {CheckpointBalancesCache} from "./balancesCache.js";
@@ -239,7 +239,7 @@ export interface IBeaconChain {
239
239
  getSerializedExecutionPayloadEnvelope(blockSlot: Slot, blockRootHex: string): Promise<Uint8Array | null>;
240
240
  getSerializedExecutionPayloadEnvelopes(
241
241
  requests: {blockSlot: Slot; blockRootHex: RootHex}[],
242
- onMismatch?: ReconstructMismatchPolicy
242
+ onMiss?: ReconstructMissPolicy
243
243
  ): Promise<(Uint8Array | null)[]>;
244
244
  getExecutionPayloadEnvelope(
245
245
  blockSlot: Slot,
@@ -12,38 +12,54 @@ import {
12
12
  } from "@lodestar/params";
13
13
  import {
14
14
  IBeaconStateView,
15
+ assertValidAttesterSlashing,
15
16
  computeEpochAtSlot,
16
17
  computeStartSlotAtEpoch,
17
18
  getAttesterSlashableIndices,
19
+ getAttesterSlashingSignatureSets,
20
+ getProposerSlashingSignatureSets,
18
21
  } from "@lodestar/state-transition";
19
22
  import {
20
23
  AttesterSlashing,
24
+ Domain,
21
25
  Epoch,
22
26
  SignedBeaconBlock,
27
+ Slot,
23
28
  ValidatorIndex,
24
29
  capella,
25
30
  phase0,
31
+ ssz,
26
32
  sszTypesFor,
27
33
  } from "@lodestar/types";
28
- import {fromHex, toHex, toRootHex} from "@lodestar/utils";
34
+ import {byteArrayEquals, fromHex, toHex, toRootHex} from "@lodestar/utils";
29
35
  import {IBeaconDb} from "../../db/index.js";
30
36
  import {Metrics} from "../../metrics/metrics.js";
31
37
  import {SignedBLSToExecutionChangeVersioned} from "../../util/types.js";
38
+ import {IBlsVerifier} from "../bls/interface.js";
32
39
  import {BlockType} from "../interface.js";
33
40
  import {BlockProductionStep} from "../produceBlock/produceBlockBody.js";
34
- import {isValidBlsToExecutionChangeForBlockInclusion} from "./utils.js";
41
+ import {
42
+ getAttesterSlashingSignatureDomains,
43
+ getProposerSlashingSignatureDomain,
44
+ isValidBlsToExecutionChangeForBlockInclusion,
45
+ } from "./utils.js";
35
46
 
36
47
  type HexRoot = string;
37
48
  type AttesterSlashingCached = {
38
49
  attesterSlashing: AttesterSlashing;
39
50
  intersectingIndices: number[];
51
+ verifiedDomains: [Domain, Domain];
52
+ };
53
+ type ProposerSlashingCached = {
54
+ proposerSlashing: phase0.ProposerSlashing;
55
+ verifiedDomain: Domain;
40
56
  };
41
57
 
42
58
  export class OpPool {
43
59
  /** Map of uniqueId(AttesterSlashing) -> AttesterSlashing */
44
60
  private readonly attesterSlashings = new Map<HexRoot, AttesterSlashingCached>();
45
61
  /** Map of to slash validator index -> ProposerSlashing */
46
- private readonly proposerSlashings = new Map<ValidatorIndex, phase0.ProposerSlashing>();
62
+ private readonly proposerSlashings = new Map<ValidatorIndex, ProposerSlashingCached>();
47
63
  /** Map of to exit validator index -> SignedVoluntaryExit */
48
64
  private readonly voluntaryExits = new Map<ValidatorIndex, phase0.SignedVoluntaryExit>();
49
65
  /** Set of seen attester slashing indexes. No need to prune */
@@ -68,7 +84,7 @@ export class OpPool {
68
84
  return this.blsToExecutionChanges.size;
69
85
  }
70
86
 
71
- async fromPersisted(db: IBeaconDb): Promise<void> {
87
+ async fromPersisted(db: IBeaconDb, state: IBeaconStateView, bls: IBlsVerifier, currentSlot: Slot): Promise<void> {
72
88
  const [attesterSlashings, proposerSlashings, voluntaryExits, blsToExecutionChanges] = await Promise.all([
73
89
  db.attesterSlashing.entries(),
74
90
  db.proposerSlashing.values(),
@@ -76,11 +92,42 @@ export class OpPool {
76
92
  db.blsToExecutionChange.values(),
77
93
  ]);
78
94
 
79
- for (const attesterSlashing of attesterSlashings) {
80
- this.insertAttesterSlashing(ForkName.electra, attesterSlashing.value, attesterSlashing.key);
95
+ // Persisted operations have no verification context. Rebuild it before they can enter the pool.
96
+ // The startup anchor may precede a fork whose slashings are already persisted.
97
+ for (const {key, value: attesterSlashing} of attesterSlashings) {
98
+ try {
99
+ assertValidAttesterSlashing(this.config, currentSlot, state.validatorCount, attesterSlashing, false);
100
+ } catch {
101
+ continue;
102
+ }
103
+ const verifiedDomains = getAttesterSlashingSignatureDomains(this.config, currentSlot, attesterSlashing);
104
+ if (
105
+ !(await bls.verifySignatureSets(getAttesterSlashingSignatureSets(this.config, currentSlot, attesterSlashing)))
106
+ ) {
107
+ continue;
108
+ }
109
+ this.insertAttesterSlashing(ForkName.electra, attesterSlashing, verifiedDomains, key);
81
110
  }
82
111
  for (const proposerSlashing of proposerSlashings) {
83
- this.insertProposerSlashing(proposerSlashing);
112
+ const header1 = proposerSlashing.signedHeader1.message;
113
+ const header2 = proposerSlashing.signedHeader2.message;
114
+ // Startup may restore a state from before activation. Current slashability is checked
115
+ // when selecting operations, so it must not prevent restoring an otherwise valid slashing.
116
+ if (
117
+ header1.slot !== header2.slot ||
118
+ header1.proposerIndex !== header2.proposerIndex ||
119
+ header1.proposerIndex >= state.validatorCount ||
120
+ ssz.phase0.BeaconBlockHeaderBigint.equals(header1, header2)
121
+ ) {
122
+ continue;
123
+ }
124
+ const verifiedDomain = getProposerSlashingSignatureDomain(this.config, currentSlot, proposerSlashing);
125
+ if (
126
+ !(await bls.verifySignatureSets(getProposerSlashingSignatureSets(this.config, currentSlot, proposerSlashing)))
127
+ ) {
128
+ continue;
129
+ }
130
+ this.insertProposerSlashing(proposerSlashing, verifiedDomain);
84
131
  }
85
132
  for (const voluntaryExit of voluntaryExits) {
86
133
  this.insertVoluntaryExit(voluntaryExit);
@@ -102,8 +149,12 @@ export class OpPool {
102
149
  ),
103
150
  persistDiff(
104
151
  db.proposerSlashing,
105
- Array.from(this.proposerSlashings.entries()).map(([key, value]) => ({key, value})),
106
- (index) => index
152
+ Array.from(this.proposerSlashings.entries()).map(([key, value]) => ({key, value: value.proposerSlashing})),
153
+ (index) => index,
154
+ // A slashing invalidated by a fork may remain on disk after eviction from the pool.
155
+ // A new valid slashing for that validator has the same database key, so comparing
156
+ // keys alone would retain the invalid slashing and lose the replacement on restart.
157
+ {updateExisting: true}
107
158
  ),
108
159
  persistDiff(
109
160
  db.voluntaryExit,
@@ -143,7 +194,12 @@ export class OpPool {
143
194
  }
144
195
 
145
196
  /** Must be validated beforehand */
146
- insertAttesterSlashing(fork: ForkName, attesterSlashing: AttesterSlashing, rootHash?: Uint8Array): void {
197
+ insertAttesterSlashing(
198
+ fork: ForkName,
199
+ attesterSlashing: AttesterSlashing,
200
+ verifiedDomains: [Domain, Domain],
201
+ rootHash?: Uint8Array
202
+ ): void {
147
203
  if (!rootHash) {
148
204
  rootHash = sszTypesFor(fork).AttesterSlashing.hashTreeRoot(attesterSlashing);
149
205
  }
@@ -153,6 +209,7 @@ export class OpPool {
153
209
  this.attesterSlashings.set(toRootHex(rootHash), {
154
210
  attesterSlashing,
155
211
  intersectingIndices,
212
+ verifiedDomains,
156
213
  });
157
214
  for (const index of intersectingIndices) {
158
215
  this.attesterSlashingIndexes.add(index);
@@ -160,8 +217,11 @@ export class OpPool {
160
217
  }
161
218
 
162
219
  /** Must be validated beforehand */
163
- insertProposerSlashing(proposerSlashing: phase0.ProposerSlashing): void {
164
- this.proposerSlashings.set(proposerSlashing.signedHeader1.message.proposerIndex, proposerSlashing);
220
+ insertProposerSlashing(proposerSlashing: phase0.ProposerSlashing, verifiedDomain: Domain): void {
221
+ this.proposerSlashings.set(proposerSlashing.signedHeader1.message.proposerIndex, {
222
+ proposerSlashing,
223
+ verifiedDomain,
224
+ });
165
225
  }
166
226
 
167
227
  /** Must be validated beforehand */
@@ -204,7 +264,14 @@ export class OpPool {
204
264
  : metrics?.builderBlockProductionTimeSteps;
205
265
 
206
266
  const endProposerSlashing = stepsMetrics?.startTimer();
207
- for (const proposerSlashing of this.proposerSlashings.values()) {
267
+ for (const [key, {proposerSlashing, verifiedDomain}] of this.proposerSlashings) {
268
+ // A fork can change the required domain even for verified slashings, including those with future-slot headers.
269
+ if (
270
+ !byteArrayEquals(verifiedDomain, getProposerSlashingSignatureDomain(this.config, state.slot, proposerSlashing))
271
+ ) {
272
+ this.proposerSlashings.delete(key);
273
+ continue;
274
+ }
208
275
  const index = proposerSlashing.signedHeader1.message.proposerIndex;
209
276
  const validator = state.getValidator(index);
210
277
  if (!validator.slashed && validator.activationEpoch <= stateEpoch && stateEpoch < validator.withdrawableEpoch) {
@@ -223,7 +290,12 @@ export class OpPool {
223
290
  const endAttesterSlashings = stepsMetrics?.startTimer();
224
291
  const attesterSlashings: AttesterSlashing[] = [];
225
292
  const maxAttesterSlashings = stateFork >= ForkSeq.electra ? MAX_ATTESTER_SLASHINGS_ELECTRA : MAX_ATTESTER_SLASHINGS;
226
- attesterSlashing: for (const attesterSlashing of this.attesterSlashings.values()) {
293
+ attesterSlashing: for (const [key, attesterSlashing] of this.attesterSlashings) {
294
+ const domains = getAttesterSlashingSignatureDomains(this.config, state.slot, attesterSlashing.attesterSlashing);
295
+ if (!attesterSlashing.verifiedDomains.every((domain, i) => byteArrayEquals(domain, domains[i]))) {
296
+ this.attesterSlashings.delete(key);
297
+ continue;
298
+ }
227
299
  /** Indices slashable in this attester slashing */
228
300
  const slashableIndices = new Set<ValidatorIndex>();
229
301
  for (let i = 0; i < attesterSlashing.intersectingIndices.length; i++) {
@@ -303,7 +375,7 @@ export class OpPool {
303
375
 
304
376
  /** For beacon pool API */
305
377
  getAllProposerSlashings(): phase0.ProposerSlashing[] {
306
- return Array.from(this.proposerSlashings.values());
378
+ return Array.from(this.proposerSlashings.values()).map(({proposerSlashing}) => proposerSlashing);
307
379
  }
308
380
 
309
381
  /** For beacon pool API */
@@ -356,7 +428,7 @@ export class OpPool {
356
428
  */
357
429
  private pruneProposerSlashings(headState: IBeaconStateView): void {
358
430
  const finalizedEpoch = headState.finalizedCheckpoint.epoch;
359
- for (const [key, proposerSlashing] of this.proposerSlashings.entries()) {
431
+ for (const [key, {proposerSlashing}] of this.proposerSlashings.entries()) {
360
432
  const index = proposerSlashing.signedHeader1.message.proposerIndex;
361
433
  if (headState.getValidator(index).exitEpoch <= finalizedEpoch) {
362
434
  this.proposerSlashings.delete(key);
@@ -436,14 +508,15 @@ function isSlashableAtEpoch(validator: phase0.Validator, epoch: Epoch): boolean
436
508
  async function persistDiff<K extends Id, V>(
437
509
  dbRepo: Repository<K, V>,
438
510
  items: {key: K; value: V}[],
439
- serializeKey: (key: K) => number | string
511
+ serializeKey: (key: K) => number | string,
512
+ opts: {updateExisting?: boolean} = {}
440
513
  ): Promise<void> {
441
514
  const persistedKeys = await dbRepo.keys();
442
515
  const batch: DbBatch<K, V> = [];
443
516
 
444
517
  const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
445
518
  for (const item of items) {
446
- if (!persistedKeysSerialized.has(serializeKey(item.key))) {
519
+ if (opts.updateExisting || !persistedKeysSerialized.has(serializeKey(item.key))) {
447
520
  batch.push({type: "put", key: item.key, value: item.value});
448
521
  }
449
522
  }
@@ -6,8 +6,8 @@ import {toRootHex} from "@lodestar/utils";
6
6
  *
7
7
  * The primary consumer is `validateExecutionPayloadBid`, which looks up the matching
8
8
  * preferences via `get(bid.slot, dependent_root)` to enforce the IGNORE-existence and
9
- * REJECT-equality rules from the gloas spec. The beacon API `/pool/proposer_preferences`
10
- * GET endpoint reads from the same pool via `getAll`.
9
+ * REJECT-equality rules from the gloas spec. The beacon API `getProposerPreferences`
10
+ * endpoint reads from the same pool via `getAll`.
11
11
  *
12
12
  * `validator_index` is intentionally not part of the key: gossip validation enforces
13
13
  * `proposers[proposalSlot % SLOTS_PER_EPOCH] === validatorIndex` against the shuffling
@@ -37,15 +37,19 @@ export class ProposerPreferencesPool {
37
37
  byRoot.set(rootHex, signed);
38
38
  }
39
39
 
40
- /** API read-out: flatten across branches, optionally filtered by slot. */
41
- getAll(slot?: Slot): gloas.SignedProposerPreferences[] {
42
- if (slot !== undefined) {
43
- const byRoot = this.bySlot.get(slot);
44
- return byRoot ? Array.from(byRoot.values()) : [];
45
- }
40
+ /** API read-out: flatten across branches, optionally filtered by slot and dependent root. */
41
+ getAll(slot?: Slot, dependentRootHex?: RootHex): gloas.SignedProposerPreferences[] {
46
42
  const out: gloas.SignedProposerPreferences[] = [];
47
- for (const byRoot of this.bySlot.values()) {
48
- for (const v of byRoot.values()) out.push(v);
43
+ for (const [s, byRoot] of this.bySlot) {
44
+ if (slot !== undefined && s !== slot) {
45
+ continue;
46
+ }
47
+ for (const [root, signed] of byRoot) {
48
+ if (dependentRootHex !== undefined && root !== dependentRootHex) {
49
+ continue;
50
+ }
51
+ out.push(signed);
52
+ }
49
53
  }
50
54
  return out;
51
55
  }
@@ -1,9 +1,37 @@
1
1
  import {Signature} from "@chainsafe/lodestar-z/blst";
2
- import {BLS_WITHDRAWAL_PREFIX} from "@lodestar/params";
3
- import {IBeaconStateView} from "@lodestar/state-transition";
4
- import {Slot, capella} from "@lodestar/types";
2
+ import {BeaconConfig} from "@lodestar/config";
3
+ import {BLS_WITHDRAWAL_PREFIX, DOMAIN_BEACON_ATTESTER, DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
4
+ import {IBeaconStateView, computeStartSlotAtEpoch} from "@lodestar/state-transition";
5
+ import {AttesterSlashing, Domain, Slot, capella, phase0} from "@lodestar/types";
5
6
  import {AggregateFast, AggregateFastElectra} from "./attestationPool.js";
6
7
 
8
+ export function getProposerSlashingSignatureDomain(
9
+ config: BeaconConfig,
10
+ stateSlot: Slot,
11
+ slashing: phase0.ProposerSlashing
12
+ ): Domain {
13
+ return config.getDomain(stateSlot, DOMAIN_BEACON_PROPOSER, Number(slashing.signedHeader1.message.slot));
14
+ }
15
+
16
+ export function getAttesterSlashingSignatureDomains(
17
+ config: BeaconConfig,
18
+ stateSlot: Slot,
19
+ slashing: AttesterSlashing
20
+ ): [Domain, Domain] {
21
+ return [
22
+ config.getDomain(
23
+ stateSlot,
24
+ DOMAIN_BEACON_ATTESTER,
25
+ computeStartSlotAtEpoch(Number(slashing.attestation1.data.target.epoch))
26
+ ),
27
+ config.getDomain(
28
+ stateSlot,
29
+ DOMAIN_BEACON_ATTESTER,
30
+ computeStartSlotAtEpoch(Number(slashing.attestation2.data.target.epoch))
31
+ ),
32
+ ];
33
+ }
34
+
7
35
  /**
8
36
  * Prune a Map indexed by slot to keep the most recent slots, up to `slotsRetained`
9
37
  */
@@ -52,7 +52,7 @@ export type IChainOptions = BlockProcessOpts &
52
52
  minSameMessageSignatureSetsToBatch: number;
53
53
  archiveDateEpochs?: number;
54
54
  nHistoricalStatesFileDataStore?: boolean;
55
- nativeStateView?: boolean;
55
+ nativeStateTransition?: boolean;
56
56
  /** Builder circuit breaker fault inspection window in slots */
57
57
  faultInspectionWindow?: number;
58
58
  /** Canonical EMPTY blocks allowed per `faultInspectionWindow` observed blocks */
@@ -131,7 +131,7 @@ export const defaultChainOptions: IChainOptions = {
131
131
  // - users can prune the persisted checkpoint state files manually to save disc space
132
132
  // - it helps debug easier when network is unfinalized
133
133
  nHistoricalStatesFileDataStore: true,
134
- nativeStateView: false,
134
+ nativeStateTransition: false,
135
135
  maxBlockStates: DEFAULT_MAX_BLOCK_STATES,
136
136
  maxCPStateEpochsInMemory: DEFAULT_MAX_CP_STATE_EPOCHS_IN_MEMORY,
137
137
  maxCPStateEpochsOnDisk: DEFAULT_MAX_CP_STATE_ON_DISK,