@lodestar/beacon-node 1.48.0-dev.e25eb75f31 → 1.48.0-dev.ec85b38ca5

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 (237) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +4 -4
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/validator/index.d.ts.map +1 -1
  5. package/lib/api/impl/validator/index.js +39 -39
  6. package/lib/api/impl/validator/index.js.map +1 -1
  7. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  8. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  9. package/lib/chain/archiveStore/archiveStore.js +1 -0
  10. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  11. package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
  12. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -0
  13. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  14. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
  16. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  17. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -0
  18. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  19. package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
  20. package/lib/chain/archiveStore/historicalState/worker.js +4 -1
  21. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  22. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  23. package/lib/chain/archiveStore/utils/archiveBlocks.js +31 -34
  24. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  25. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  26. package/lib/chain/blocks/importBlock.js +0 -13
  27. package/lib/chain/blocks/importBlock.js.map +1 -1
  28. package/lib/chain/blocks/index.d.ts.map +1 -1
  29. package/lib/chain/blocks/index.js +3 -2
  30. package/lib/chain/blocks/index.js.map +1 -1
  31. package/lib/chain/blocks/utils/chainSegment.d.ts +1 -1
  32. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  33. package/lib/chain/blocks/utils/chainSegment.js +10 -1
  34. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  35. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  36. package/lib/chain/blocks/writeBlockInputToDb.js +9 -16
  37. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  38. package/lib/chain/chain.d.ts +9 -3
  39. package/lib/chain/chain.d.ts.map +1 -1
  40. package/lib/chain/chain.js +127 -73
  41. package/lib/chain/chain.js.map +1 -1
  42. package/lib/chain/emitter.d.ts +1 -1
  43. package/lib/chain/emitter.js +1 -1
  44. package/lib/chain/errors/blockError.d.ts +19 -7
  45. package/lib/chain/errors/blockError.d.ts.map +1 -1
  46. package/lib/chain/errors/blockError.js +2 -2
  47. package/lib/chain/errors/blockError.js.map +1 -1
  48. package/lib/chain/errors/dataColumnSidecarError.d.ts +15 -2
  49. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  50. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  51. package/lib/chain/errors/executionPayloadEnvelope.d.ts +10 -11
  52. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  53. package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
  54. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  55. package/lib/chain/interface.d.ts +2 -2
  56. package/lib/chain/interface.d.ts.map +1 -1
  57. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  58. package/lib/chain/prepareNextSlot.js +16 -14
  59. package/lib/chain/prepareNextSlot.js.map +1 -1
  60. package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -2
  61. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  62. package/lib/chain/produceBlock/produceBlockBody.js +57 -72
  63. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  64. package/lib/chain/validation/block.d.ts.map +1 -1
  65. package/lib/chain/validation/block.js +35 -54
  66. package/lib/chain/validation/block.js.map +1 -1
  67. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  68. package/lib/chain/validation/dataColumnSidecar.js +38 -13
  69. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  70. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  71. package/lib/chain/validation/executionPayloadEnvelope.js +10 -29
  72. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  73. package/lib/chain/validatorMonitor.d.ts +7 -1
  74. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  75. package/lib/chain/validatorMonitor.js +22 -5
  76. package/lib/chain/validatorMonitor.js.map +1 -1
  77. package/lib/db/beacon.d.ts +14 -5
  78. package/lib/db/beacon.d.ts.map +1 -1
  79. package/lib/db/beacon.js +17 -3
  80. package/lib/db/beacon.js.map +1 -1
  81. package/lib/db/dataColumnStore.d.ts +39 -0
  82. package/lib/db/dataColumnStore.d.ts.map +1 -0
  83. package/lib/db/dataColumnStore.js +81 -0
  84. package/lib/db/dataColumnStore.js.map +1 -0
  85. package/lib/db/flatFileStore/atomicWrite.d.ts +8 -0
  86. package/lib/db/flatFileStore/atomicWrite.d.ts.map +1 -0
  87. package/lib/db/flatFileStore/atomicWrite.js +61 -0
  88. package/lib/db/flatFileStore/atomicWrite.js.map +1 -0
  89. package/lib/db/flatFileStore/dcolFormat.d.ts +90 -0
  90. package/lib/db/flatFileStore/dcolFormat.d.ts.map +1 -0
  91. package/lib/db/flatFileStore/dcolFormat.js +286 -0
  92. package/lib/db/flatFileStore/dcolFormat.js.map +1 -0
  93. package/lib/db/flatFileStore/errors.d.ts +82 -0
  94. package/lib/db/flatFileStore/errors.d.ts.map +1 -0
  95. package/lib/db/flatFileStore/errors.js +32 -0
  96. package/lib/db/flatFileStore/errors.js.map +1 -0
  97. package/lib/db/flatFileStore/flatFileStore.d.ts +41 -0
  98. package/lib/db/flatFileStore/flatFileStore.d.ts.map +1 -0
  99. package/lib/db/flatFileStore/flatFileStore.js +251 -0
  100. package/lib/db/flatFileStore/flatFileStore.js.map +1 -0
  101. package/lib/db/flatFileStore/index.d.ts +4 -0
  102. package/lib/db/flatFileStore/index.d.ts.map +1 -0
  103. package/lib/db/flatFileStore/index.js +2 -0
  104. package/lib/db/flatFileStore/index.js.map +1 -0
  105. package/lib/db/flatFileStore/interface.d.ts +25 -0
  106. package/lib/db/flatFileStore/interface.d.ts.map +1 -0
  107. package/lib/db/flatFileStore/interface.js +2 -0
  108. package/lib/db/flatFileStore/interface.js.map +1 -0
  109. package/lib/db/flatFileStore/metrics.d.ts +24 -0
  110. package/lib/db/flatFileStore/metrics.d.ts.map +1 -0
  111. package/lib/db/flatFileStore/metrics.js +25 -0
  112. package/lib/db/flatFileStore/metrics.js.map +1 -0
  113. package/lib/db/flatFileStore/path.d.ts +9 -0
  114. package/lib/db/flatFileStore/path.d.ts.map +1 -0
  115. package/lib/db/flatFileStore/path.js +21 -0
  116. package/lib/db/flatFileStore/path.js.map +1 -0
  117. package/lib/db/flatFileStore/slotIndex.d.ts +14 -0
  118. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -0
  119. package/lib/db/flatFileStore/slotIndex.js +46 -0
  120. package/lib/db/flatFileStore/slotIndex.js.map +1 -0
  121. package/lib/db/flatFileStore/snappy.d.ts +3 -0
  122. package/lib/db/flatFileStore/snappy.d.ts.map +1 -0
  123. package/lib/db/flatFileStore/snappy.js +12 -0
  124. package/lib/db/flatFileStore/snappy.js.map +1 -0
  125. package/lib/db/index.d.ts +1 -0
  126. package/lib/db/index.d.ts.map +1 -1
  127. package/lib/db/interface.d.ts +8 -1
  128. package/lib/db/interface.d.ts.map +1 -1
  129. package/lib/db/repositories/blockArchive.d.ts +8 -1
  130. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  131. package/lib/db/repositories/blockArchive.js +65 -28
  132. package/lib/db/repositories/blockArchive.js.map +1 -1
  133. package/lib/db/repositories/blockArchiveIndex.d.ts +1 -2
  134. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  135. package/lib/db/repositories/blockArchiveIndex.js +3 -7
  136. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  137. package/lib/execution/engine/mock.d.ts.map +1 -1
  138. package/lib/execution/engine/mock.js +4 -1
  139. package/lib/execution/engine/mock.js.map +1 -1
  140. package/lib/metrics/metrics/lodestar.d.ts +18 -0
  141. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  142. package/lib/metrics/metrics/lodestar.js +55 -1
  143. package/lib/metrics/metrics/lodestar.js.map +1 -1
  144. package/lib/monitoring/clientStats.js +1 -1
  145. package/lib/monitoring/clientStats.js.map +1 -1
  146. package/lib/network/gossip/encoding.d.ts.map +1 -1
  147. package/lib/network/gossip/encoding.js +0 -7
  148. package/lib/network/gossip/encoding.js.map +1 -1
  149. package/lib/network/gossip/topic.d.ts +2 -1
  150. package/lib/network/gossip/topic.d.ts.map +1 -1
  151. package/lib/network/gossip/topic.js +4 -20
  152. package/lib/network/gossip/topic.js.map +1 -1
  153. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  154. package/lib/network/processor/gossipHandlers.js +6 -7
  155. package/lib/network/processor/gossipHandlers.js.map +1 -1
  156. package/lib/network/processor/index.d.ts +49 -3
  157. package/lib/network/processor/index.d.ts.map +1 -1
  158. package/lib/network/processor/index.js +232 -98
  159. package/lib/network/processor/index.js.map +1 -1
  160. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  161. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +112 -91
  162. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  163. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  164. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +2 -0
  165. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  166. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  167. package/lib/network/reqresp/protocols.js +6 -22
  168. package/lib/network/reqresp/protocols.js.map +1 -1
  169. package/lib/network/reqresp/rateLimit.js +2 -1
  170. package/lib/network/reqresp/rateLimit.js.map +1 -1
  171. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +2 -1
  172. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  173. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -9
  174. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  175. package/lib/node/nodejs.d.ts +2 -1
  176. package/lib/node/nodejs.d.ts.map +1 -1
  177. package/lib/node/nodejs.js +4 -2
  178. package/lib/node/nodejs.js.map +1 -1
  179. package/lib/util/sszBytes.d.ts +6 -0
  180. package/lib/util/sszBytes.d.ts.map +1 -1
  181. package/lib/util/sszBytes.js +1 -1
  182. package/lib/util/sszBytes.js.map +1 -1
  183. package/package.json +18 -19
  184. package/src/api/impl/beacon/blocks/index.ts +8 -4
  185. package/src/api/impl/validator/index.ts +44 -43
  186. package/src/chain/archiveStore/archiveStore.ts +6 -1
  187. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -0
  188. package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
  189. package/src/chain/archiveStore/historicalState/types.ts +2 -0
  190. package/src/chain/archiveStore/historicalState/worker.ts +4 -1
  191. package/src/chain/archiveStore/utils/archiveBlocks.ts +34 -39
  192. package/src/chain/blocks/importBlock.ts +0 -13
  193. package/src/chain/blocks/index.ts +13 -2
  194. package/src/chain/blocks/utils/chainSegment.ts +10 -1
  195. package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
  196. package/src/chain/chain.ts +149 -80
  197. package/src/chain/emitter.ts +1 -1
  198. package/src/chain/errors/blockError.ts +22 -10
  199. package/src/chain/errors/dataColumnSidecarError.ts +24 -8
  200. package/src/chain/errors/executionPayloadEnvelope.ts +14 -11
  201. package/src/chain/interface.ts +2 -2
  202. package/src/chain/prepareNextSlot.ts +21 -17
  203. package/src/chain/produceBlock/produceBlockBody.ts +62 -79
  204. package/src/chain/validation/block.ts +35 -70
  205. package/src/chain/validation/dataColumnSidecar.ts +47 -14
  206. package/src/chain/validation/executionPayloadEnvelope.ts +10 -37
  207. package/src/chain/validatorMonitor.ts +24 -7
  208. package/src/db/beacon.ts +33 -6
  209. package/src/db/dataColumnStore.ts +113 -0
  210. package/src/db/flatFileStore/atomicWrite.ts +65 -0
  211. package/src/db/flatFileStore/dcolFormat.ts +389 -0
  212. package/src/db/flatFileStore/errors.ts +53 -0
  213. package/src/db/flatFileStore/flatFileStore.ts +310 -0
  214. package/src/db/flatFileStore/index.ts +3 -0
  215. package/src/db/flatFileStore/interface.ts +21 -0
  216. package/src/db/flatFileStore/metrics.ts +48 -0
  217. package/src/db/flatFileStore/path.ts +32 -0
  218. package/src/db/flatFileStore/slotIndex.ts +56 -0
  219. package/src/db/flatFileStore/snappy.ts +14 -0
  220. package/src/db/index.ts +1 -0
  221. package/src/db/interface.ts +9 -1
  222. package/src/db/repositories/blockArchive.ts +89 -30
  223. package/src/db/repositories/blockArchiveIndex.ts +4 -9
  224. package/src/execution/engine/mock.ts +4 -1
  225. package/src/metrics/metrics/lodestar.ts +57 -1
  226. package/src/monitoring/clientStats.ts +1 -1
  227. package/src/network/gossip/encoding.ts +0 -7
  228. package/src/network/gossip/topic.ts +3 -25
  229. package/src/network/processor/gossipHandlers.ts +11 -11
  230. package/src/network/processor/index.ts +263 -148
  231. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +139 -105
  232. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +2 -0
  233. package/src/network/reqresp/protocols.ts +7 -24
  234. package/src/network/reqresp/rateLimit.ts +2 -1
  235. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +15 -7
  236. package/src/node/nodejs.ts +6 -1
  237. package/src/util/sszBytes.ts +1 -1
@@ -1,11 +1,4 @@
1
1
  import {PayloadStatus} from "@lodestar/fork-choice";
2
- import {
3
- MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
4
- MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
5
- MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
6
- MAX_WITHDRAWALS_PER_PAYLOAD,
7
- MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
8
- } from "@lodestar/params";
9
2
  import {
10
3
  computeStartSlotAtEpoch,
11
4
  getExecutionPayloadEnvelopeSignatureSet,
@@ -89,6 +82,8 @@ async function validateExecutionPayloadEnvelope(
89
82
  if (block.slot !== payload.slotNumber) {
90
83
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
91
84
  code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
85
+ slot: payload.slotNumber,
86
+ root: blockRootHex,
92
87
  envelopeSlot: payload.slotNumber,
93
88
  blockSlot: block.slot,
94
89
  });
@@ -98,6 +93,8 @@ async function validateExecutionPayloadEnvelope(
98
93
  if (envelope.builderIndex !== payloadInput.getBuilderIndex()) {
99
94
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
100
95
  code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
96
+ slot: payload.slotNumber,
97
+ root: blockRootHex,
101
98
  envelopeBuilderIndex: envelope.builderIndex,
102
99
  bidBuilderIndex: payloadInput.getBuilderIndex(),
103
100
  });
@@ -107,6 +104,8 @@ async function validateExecutionPayloadEnvelope(
107
104
  if (toRootHex(payload.blockHash) !== payloadInput.getBlockHashHex()) {
108
105
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
109
106
  code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
107
+ slot: payload.slotNumber,
108
+ root: blockRootHex,
110
109
  envelopeBlockHash: toRootHex(payload.blockHash),
111
110
  bidBlockHash: payloadInput.getBlockHashHex(),
112
111
  });
@@ -117,41 +116,13 @@ async function validateExecutionPayloadEnvelope(
117
116
  if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
118
117
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
119
118
  code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
119
+ slot: payload.slotNumber,
120
+ root: blockRootHex,
120
121
  envelopeRequestsRoot: toRootHex(requestsRoot),
121
122
  bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
122
123
  });
123
124
  }
124
125
 
125
- // [REJECT] The counts of `execution_requests` are within their respective limits.
126
- // New in Gloas:EIP7688 — progressive lists are unbounded at the type level, so bounds
127
- // are enforced here in gossip validation.
128
- const {executionRequests} = envelope;
129
- const requestCountLimits: [string, number, number][] = [
130
- ["withdrawals", executionRequests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
131
- ["consolidations", executionRequests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
132
- ["builderDeposits", executionRequests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD],
133
- ["builderExits", executionRequests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD],
134
- ];
135
- for (const [name, count, limit] of requestCountLimits) {
136
- if (count > limit) {
137
- throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
138
- code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED,
139
- name,
140
- count,
141
- limit,
142
- });
143
- }
144
- }
145
-
146
- // [REJECT] The number of withdrawals is within the limit.
147
- if (payload.withdrawals.length > MAX_WITHDRAWALS_PER_PAYLOAD) {
148
- throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
149
- code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED,
150
- count: payload.withdrawals.length,
151
- limit: MAX_WITHDRAWALS_PER_PAYLOAD,
152
- });
153
- }
154
-
155
126
  // Get the block state to verify the builder's signature.
156
127
  const blockState = await chain.regen
157
128
  .getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
@@ -178,6 +149,8 @@ async function validateExecutionPayloadEnvelope(
178
149
  if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
179
150
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
180
151
  code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
152
+ slot: payload.slotNumber,
153
+ root: blockRootHex,
181
154
  });
182
155
  }
183
156
  }
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
3
3
  import {
4
4
  IBeaconStateView,
5
5
  ParticipationFlags,
@@ -1117,7 +1117,11 @@ function isCanonical(rootCache: RootHexCache, block: AttestationBlockInclusion):
1117
1117
 
1118
1118
  /** Returns true if root at slot is the same at slot - 1 == there was no new block at slot */
1119
1119
  function isMissedSlot(rootCache: RootHexCache, slot: Slot): boolean {
1120
- return slot > 0 && rootCache.getBlockRootAtSlot(slot) === rootCache.getBlockRootAtSlot(slot - 1);
1120
+ if (slot <= 0) {
1121
+ return false;
1122
+ }
1123
+ const root = rootCache.getBlockRootAtSlot(slot);
1124
+ return root !== null && root === rootCache.getBlockRootAtSlot(slot - 1);
1121
1125
  }
1122
1126
 
1123
1127
  function renderBlockProposalSummary(
@@ -1131,7 +1135,11 @@ function renderBlockProposalSummary(
1131
1135
  return "not_submitted";
1132
1136
  }
1133
1137
 
1134
- if (rootCache.getBlockRootAtSlot(proposalSlot) === proposal.blockRoot) {
1138
+ const canonicalRoot = rootCache.getBlockRootAtSlot(proposalSlot);
1139
+ if (canonicalRoot === null) {
1140
+ return "unknown";
1141
+ }
1142
+ if (canonicalRoot === proposal.blockRoot) {
1135
1143
  // Canonical state includes our block
1136
1144
  return "canonical";
1137
1145
  }
@@ -1157,14 +1165,23 @@ function renderBlockProposalSummary(
1157
1165
  * In normal network conditions the same root is read multiple times, specially the target.
1158
1166
  */
1159
1167
  export class RootHexCache {
1160
- private readonly blockRootSlotCache = new Map<Slot, RootHex>();
1168
+ private readonly blockRootSlotCache = new Map<Slot, RootHex | null>();
1161
1169
 
1162
1170
  constructor(private readonly state: IBeaconStateView) {}
1163
1171
 
1164
- getBlockRootAtSlot(slot: Slot): RootHex {
1172
+ /**
1173
+ * Block root at `slot`, null if the state does not cover it: slots at or after the state's own slot (an inclusion
1174
+ * in a block the head state does not know yet, e.g. an orphaned block, or the slot after the last attestation of
1175
+ * the epoch) or slots older than `SLOTS_PER_HISTORICAL_ROOT`. Callers treat null as unknown instead of throwing
1176
+ * and aborting the summaries of all remaining validators.
1177
+ */
1178
+ getBlockRootAtSlot(slot: Slot): RootHex | null {
1165
1179
  let root = this.blockRootSlotCache.get(slot);
1166
- if (!root) {
1167
- root = toRootHex(this.state.getBlockRootAtSlot(slot));
1180
+ if (root === undefined) {
1181
+ root =
1182
+ slot < this.state.slot && slot >= this.state.slot - SLOTS_PER_HISTORICAL_ROOT
1183
+ ? toRootHex(this.state.getBlockRootAtSlot(slot))
1184
+ : null;
1168
1185
  this.blockRootSlotCache.set(slot, root);
1169
1186
  }
1170
1187
  return root;
package/src/db/beacon.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Db, LevelDbControllerMetrics, encodeKey} from "@lodestar/db";
3
+ import {Slot} from "@lodestar/types";
4
+ import {Logger} from "@lodestar/utils";
3
5
  import {Bucket} from "./buckets.js";
6
+ import {type IDataColumnStore, LegacyDataColumnStore} from "./dataColumnStore.js";
7
+ import {FlatFileStore} from "./flatFileStore/flatFileStore.js";
8
+ import type {FlatFileStoreMetrics} from "./flatFileStore/metrics.js";
4
9
  import {IBeaconDb} from "./interface.js";
5
10
  import {CheckpointStateRepository} from "./repositories/checkpointState.js";
6
11
  import {
@@ -24,9 +29,10 @@ import {
24
29
  VoluntaryExitRepository,
25
30
  } from "./repositories/index.js";
26
31
 
27
- export type BeaconDbModules = {
28
- config: ChainForkConfig;
29
- db: Db;
32
+ export type BeaconDbOpts = {
33
+ dataColumnDir: string;
34
+ logger: Logger;
35
+ metrics?: FlatFileStoreMetrics | null;
30
36
  };
31
37
 
32
38
  export class BeaconDb implements IBeaconDb {
@@ -57,9 +63,14 @@ export class BeaconDb implements IBeaconDb {
57
63
 
58
64
  backfilledRanges: BackfilledRanges;
59
65
 
66
+ readonly dataColumns: IDataColumnStore;
67
+ lastLegacyArchiveSlot: Slot | null = null;
68
+ private readonly flatFileStore: FlatFileStore;
69
+
60
70
  constructor(
61
71
  config: ChainForkConfig,
62
- protected readonly db: Db
72
+ protected readonly db: Db,
73
+ opts: BeaconDbOpts
63
74
  ) {
64
75
  // Warning: If code is ever run in the constructor, must change this stub to not extend 'packages/beacon-node/test/utils/stub/beaconDb.ts' -
65
76
  this.block = new BlockRepository(config, db);
@@ -87,14 +98,30 @@ export class BeaconDb implements IBeaconDb {
87
98
  this.syncCommitteeWitness = new SyncCommitteeWitnessRepository(config, db);
88
99
 
89
100
  this.backfilledRanges = new BackfilledRanges(config, db);
101
+
102
+ this.flatFileStore = new FlatFileStore(opts.dataColumnDir, config, opts.logger, opts.metrics);
103
+ this.dataColumns = new LegacyDataColumnStore(
104
+ this.flatFileStore,
105
+ this.dataColumnSidecar,
106
+ this.dataColumnSidecarArchive,
107
+ this.blockArchive
108
+ );
109
+ }
110
+
111
+ async init(): Promise<void> {
112
+ await this.flatFileStore.init();
113
+ const [lastLegacyKey] = await this.dataColumnSidecarArchive.keys({reverse: true, limit: 1});
114
+ this.lastLegacyArchiveSlot = lastLegacyKey?.prefix ?? null;
90
115
  }
91
116
 
92
- close(): Promise<void> {
117
+ async close(): Promise<void> {
118
+ await this.flatFileStore.close();
93
119
  return this.db.close();
94
120
  }
95
121
 
96
- setMetrics(metrics: LevelDbControllerMetrics): void {
122
+ setMetrics(metrics: LevelDbControllerMetrics, flatFileStoreMetrics: FlatFileStoreMetrics | null = null): void {
97
123
  this.db.setMetrics(metrics);
124
+ this.flatFileStore.setMetrics(flatFileStoreMetrics);
98
125
  }
99
126
 
100
127
  async pruneHotDb(): Promise<void> {
@@ -0,0 +1,113 @@
1
+ import {ColumnIndex, DataColumnSidecar, RootHex, Slot} from "@lodestar/types";
2
+ import {fromHex} from "@lodestar/utils";
3
+ import type {IFlatFileStore} from "./flatFileStore/interface.js";
4
+ import type {
5
+ BlockArchiveRepository,
6
+ DataColumnSidecarArchiveRepository,
7
+ DataColumnSidecarRepository,
8
+ } from "./repositories/index.js";
9
+
10
+ export type DataColumnKey = {slot: Slot; blockRoot: RootHex};
11
+ export type IndexedDataColumnBytes = {index: ColumnIndex; data: Uint8Array};
12
+
13
+ type LegacyHotDataColumnStore = Pick<DataColumnSidecarRepository, "values" | "getManyBinary" | "deleteMany">;
14
+ type LegacyArchiveDataColumnStore = Pick<
15
+ DataColumnSidecarArchiveRepository,
16
+ "values" | "getManyBinary" | "keys" | "deleteMany"
17
+ >;
18
+ type BlockRootIndex = Pick<BlockArchiveRepository, "getSlotByRoot">;
19
+
20
+ export interface IDataColumnStore {
21
+ getAll(key: DataColumnKey): Promise<DataColumnSidecar[]>;
22
+ getManyBinary(key: DataColumnKey, indices: ColumnIndex[]): Promise<(Uint8Array | undefined)[]>;
23
+ putManyBinary(key: DataColumnKey, columns: IndexedDataColumnBytes[]): Promise<void>;
24
+ deleteMany(keys: DataColumnKey[]): Promise<void>;
25
+ pruneBefore(slot: Slot): Promise<Slot[]>;
26
+ }
27
+
28
+ /**
29
+ * Bridges pre-flat-file LevelDB columns during the supported in-place upgrade window.
30
+ * Remove the legacy repositories after upgrades from the last pre-flat-file release are no longer supported.
31
+ */
32
+ export class LegacyDataColumnStore implements IDataColumnStore {
33
+ constructor(
34
+ private readonly flatFiles: IFlatFileStore,
35
+ private readonly legacyHot: LegacyHotDataColumnStore,
36
+ private readonly legacyArchive: LegacyArchiveDataColumnStore,
37
+ private readonly blockArchive: BlockRootIndex
38
+ ) {}
39
+
40
+ async getAll({slot, blockRoot}: DataColumnKey): Promise<DataColumnSidecar[]> {
41
+ const sidecars = await this.flatFiles.getDataColumns(slot, blockRoot);
42
+ if (sidecars.length > 0) return sidecars;
43
+
44
+ const sidecarsByIndex = new Map<ColumnIndex, DataColumnSidecar>();
45
+ const root = fromHex(blockRoot);
46
+ for (const sidecar of await this.legacyHot.values(root)) {
47
+ if (!sidecarsByIndex.has(sidecar.index)) sidecarsByIndex.set(sidecar.index, sidecar);
48
+ }
49
+
50
+ if ((await this.blockArchive.getSlotByRoot(root)) === slot) {
51
+ for (const sidecar of await this.legacyArchive.values(slot)) {
52
+ if (!sidecarsByIndex.has(sidecar.index)) sidecarsByIndex.set(sidecar.index, sidecar);
53
+ }
54
+ }
55
+
56
+ return [...sidecarsByIndex.values()].sort((a, b) => a.index - b.index);
57
+ }
58
+
59
+ async getManyBinary({slot, blockRoot}: DataColumnKey, indices: ColumnIndex[]): Promise<(Uint8Array | undefined)[]> {
60
+ const flatFileSidecars = await this.flatFiles.getDataColumnsBinary(slot, blockRoot, indices);
61
+ if (flatFileSidecars !== null) return flatFileSidecars;
62
+ if (indices.length === 0) return [];
63
+
64
+ const root = fromHex(blockRoot);
65
+ const result = await this.legacyHot.getManyBinary(root, indices);
66
+
67
+ const missingPositions = getMissingPositions(result);
68
+ if (missingPositions.length === 0 || (await this.blockArchive.getSlotByRoot(root)) !== slot) return result;
69
+
70
+ const archivedSidecars = await this.legacyArchive.getManyBinary(
71
+ slot,
72
+ missingPositions.map((position) => indices[position])
73
+ );
74
+ fillMissing(result, missingPositions, archivedSidecars);
75
+ return result;
76
+ }
77
+
78
+ async putManyBinary({slot, blockRoot}: DataColumnKey, columns: IndexedDataColumnBytes[]): Promise<void> {
79
+ await this.flatFiles.putDataColumnsBinary(slot, blockRoot, columns);
80
+ }
81
+
82
+ async deleteMany(keys: DataColumnKey[]): Promise<void> {
83
+ await this.flatFiles.deleteMany(keys);
84
+ await this.legacyHot.deleteMany(keys.map(({blockRoot}) => fromHex(blockRoot)));
85
+ }
86
+
87
+ async pruneBefore(slot: Slot): Promise<Slot[]> {
88
+ const flatFileSlots = await this.flatFiles.pruneBefore(slot);
89
+ const prefixedKeys = await this.legacyArchive.keys({lt: {prefix: slot, id: 0}});
90
+ const slots = [...new Set(prefixedKeys.map(({prefix}) => prefix))];
91
+ if (slots.length > 0) await this.legacyArchive.deleteMany(slots);
92
+ return [...new Set([...flatFileSlots, ...slots])].sort((a, b) => a - b);
93
+ }
94
+ }
95
+
96
+ function getMissingPositions(sidecars: (Uint8Array | undefined)[]): number[] {
97
+ const positions: number[] = [];
98
+ for (let i = 0; i < sidecars.length; i++) {
99
+ if (sidecars[i] === undefined) positions.push(i);
100
+ }
101
+ return positions;
102
+ }
103
+
104
+ function fillMissing(
105
+ result: (Uint8Array | undefined)[],
106
+ missingPositions: number[],
107
+ sidecars: (Uint8Array | null | undefined)[]
108
+ ): void {
109
+ for (let i = 0; i < missingPositions.length; i++) {
110
+ const sidecar = sidecars[i];
111
+ if (sidecar != null) result[missingPositions[i]] = sidecar;
112
+ }
113
+ }
@@ -0,0 +1,65 @@
1
+ import {mkdir, open, rename, rm} from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ const pendingDirectorySyncs = new Map<string, string[]>();
5
+
6
+ /**
7
+ * Atomic write: write to `.part` temp file -> datasync -> rename to target.
8
+ * This guarantees crash-safe writes — if the process crashes mid-write,
9
+ * only the `.part` file is corrupted, never the target.
10
+ */
11
+ export async function atomicWrite(targetPath: string, data: Uint8Array): Promise<void> {
12
+ const dir = path.dirname(targetPath);
13
+ const partPath = `${targetPath}.part-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
14
+ await mkdirDurable(dir);
15
+
16
+ try {
17
+ const fd = await open(partPath, "wx");
18
+ try {
19
+ await fd.writeFile(data);
20
+ await fd.datasync();
21
+ } finally {
22
+ await fd.close();
23
+ }
24
+
25
+ await rename(partPath, targetPath);
26
+
27
+ // Ensure rename metadata is durable on crash.
28
+ await syncDirectory(dir);
29
+ } catch (e) {
30
+ await rm(partPath, {force: true}).catch(() => {});
31
+ throw e;
32
+ }
33
+ }
34
+
35
+ export async function mkdirDurable(dir: string): Promise<void> {
36
+ const targetDir = path.resolve(dir);
37
+ let directoriesToSync = pendingDirectorySyncs.get(targetDir);
38
+ if (directoriesToSync === undefined) {
39
+ const firstCreatedDir = await mkdir(targetDir, {recursive: true});
40
+ if (firstCreatedDir === undefined) return;
41
+
42
+ let createdDir = path.resolve(firstCreatedDir);
43
+ directoriesToSync = [path.dirname(createdDir)];
44
+ while (createdDir !== targetDir) {
45
+ directoriesToSync.push(createdDir);
46
+ const [nextSegment] = path.relative(createdDir, targetDir).split(path.sep);
47
+ createdDir = path.join(createdDir, nextSegment);
48
+ }
49
+ pendingDirectorySyncs.set(targetDir, directoriesToSync);
50
+ }
51
+
52
+ for (const directory of directoriesToSync) {
53
+ await syncDirectory(directory);
54
+ }
55
+ pendingDirectorySyncs.delete(targetDir);
56
+ }
57
+
58
+ async function syncDirectory(dir: string): Promise<void> {
59
+ const dirFd = await open(dir, "r");
60
+ try {
61
+ await dirFd.sync();
62
+ } finally {
63
+ await dirFd.close();
64
+ }
65
+ }