@lodestar/beacon-node 1.48.0-dev.e9c9b8835d → 1.48.0-dev.fd70e5fc15

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 (200) 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/chain/archiveStore/archiveStore.d.ts +0 -1
  5. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  6. package/lib/chain/archiveStore/archiveStore.js +0 -1
  7. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  8. package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
  9. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -1
  10. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  11. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  12. package/lib/chain/archiveStore/historicalState/metrics.js +1 -12
  13. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  14. package/lib/chain/archiveStore/historicalState/types.d.ts +0 -2
  15. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  16. package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
  17. package/lib/chain/archiveStore/historicalState/worker.js +1 -4
  18. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  19. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  20. package/lib/chain/archiveStore/utils/archiveBlocks.js +34 -31
  21. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  22. package/lib/chain/blocks/index.d.ts.map +1 -1
  23. package/lib/chain/blocks/index.js +2 -3
  24. package/lib/chain/blocks/index.js.map +1 -1
  25. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  26. package/lib/chain/blocks/writeBlockInputToDb.js +16 -9
  27. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  28. package/lib/chain/chain.d.ts +3 -4
  29. package/lib/chain/chain.d.ts.map +1 -1
  30. package/lib/chain/chain.js +35 -60
  31. package/lib/chain/chain.js.map +1 -1
  32. package/lib/chain/errors/blockError.d.ts +2 -16
  33. package/lib/chain/errors/blockError.d.ts.map +1 -1
  34. package/lib/chain/errors/blockError.js.map +1 -1
  35. package/lib/chain/errors/dataColumnSidecarError.d.ts +2 -15
  36. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  37. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  38. package/lib/chain/errors/executionPayloadEnvelope.d.ts +0 -10
  39. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  40. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  41. package/lib/chain/interface.d.ts +2 -2
  42. package/lib/chain/interface.d.ts.map +1 -1
  43. package/lib/chain/prepareNextSlot.js +13 -13
  44. package/lib/chain/prepareNextSlot.js.map +1 -1
  45. package/lib/chain/validation/block.d.ts.map +1 -1
  46. package/lib/chain/validation/block.js +4 -26
  47. package/lib/chain/validation/block.js.map +1 -1
  48. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  49. package/lib/chain/validation/dataColumnSidecar.js +13 -38
  50. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  51. package/lib/chain/validation/executionPayloadEnvelope.js +0 -10
  52. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  53. package/lib/db/beacon.d.ts +5 -14
  54. package/lib/db/beacon.d.ts.map +1 -1
  55. package/lib/db/beacon.js +3 -17
  56. package/lib/db/beacon.js.map +1 -1
  57. package/lib/db/index.d.ts +0 -1
  58. package/lib/db/index.d.ts.map +1 -1
  59. package/lib/db/interface.d.ts +1 -8
  60. package/lib/db/interface.d.ts.map +1 -1
  61. package/lib/db/repositories/blockArchive.d.ts +1 -8
  62. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  63. package/lib/db/repositories/blockArchive.js +28 -65
  64. package/lib/db/repositories/blockArchive.js.map +1 -1
  65. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -1
  66. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  67. package/lib/db/repositories/blockArchiveIndex.js +7 -3
  68. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  69. package/lib/metrics/metrics/lodestar.d.ts +0 -18
  70. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  71. package/lib/metrics/metrics/lodestar.js +1 -55
  72. package/lib/metrics/metrics/lodestar.js.map +1 -1
  73. package/lib/monitoring/clientStats.js +1 -1
  74. package/lib/monitoring/clientStats.js.map +1 -1
  75. package/lib/network/gossip/encoding.d.ts.map +1 -1
  76. package/lib/network/gossip/encoding.js +6 -0
  77. package/lib/network/gossip/encoding.js.map +1 -1
  78. package/lib/network/gossip/topic.d.ts +1 -2
  79. package/lib/network/gossip/topic.d.ts.map +1 -1
  80. package/lib/network/gossip/topic.js +20 -4
  81. package/lib/network/gossip/topic.js.map +1 -1
  82. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  83. package/lib/network/processor/gossipHandlers.js +7 -6
  84. package/lib/network/processor/gossipHandlers.js.map +1 -1
  85. package/lib/network/processor/index.d.ts +3 -49
  86. package/lib/network/processor/index.d.ts.map +1 -1
  87. package/lib/network/processor/index.js +98 -232
  88. package/lib/network/processor/index.js.map +1 -1
  89. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  90. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +91 -112
  91. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  92. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  93. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +0 -2
  94. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  95. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  96. package/lib/network/reqresp/protocols.js +22 -6
  97. package/lib/network/reqresp/protocols.js.map +1 -1
  98. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +1 -2
  99. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  100. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +9 -14
  101. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  102. package/lib/node/nodejs.d.ts +1 -2
  103. package/lib/node/nodejs.d.ts.map +1 -1
  104. package/lib/node/nodejs.js +2 -4
  105. package/lib/node/nodejs.js.map +1 -1
  106. package/lib/util/sszBytes.d.ts +0 -6
  107. package/lib/util/sszBytes.d.ts.map +1 -1
  108. package/lib/util/sszBytes.js +1 -1
  109. package/lib/util/sszBytes.js.map +1 -1
  110. package/package.json +15 -15
  111. package/src/api/impl/beacon/blocks/index.ts +4 -8
  112. package/src/chain/archiveStore/archiveStore.ts +1 -6
  113. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +0 -1
  114. package/src/chain/archiveStore/historicalState/metrics.ts +1 -14
  115. package/src/chain/archiveStore/historicalState/types.ts +0 -2
  116. package/src/chain/archiveStore/historicalState/worker.ts +1 -4
  117. package/src/chain/archiveStore/utils/archiveBlocks.ts +39 -34
  118. package/src/chain/blocks/index.ts +2 -13
  119. package/src/chain/blocks/writeBlockInputToDb.ts +17 -10
  120. package/src/chain/chain.ts +34 -75
  121. package/src/chain/errors/blockError.ts +8 -20
  122. package/src/chain/errors/dataColumnSidecarError.ts +8 -24
  123. package/src/chain/errors/executionPayloadEnvelope.ts +2 -14
  124. package/src/chain/interface.ts +2 -2
  125. package/src/chain/prepareNextSlot.ts +16 -16
  126. package/src/chain/validation/block.ts +4 -26
  127. package/src/chain/validation/dataColumnSidecar.ts +14 -47
  128. package/src/chain/validation/executionPayloadEnvelope.ts +0 -10
  129. package/src/db/beacon.ts +6 -33
  130. package/src/db/index.ts +0 -1
  131. package/src/db/interface.ts +1 -9
  132. package/src/db/repositories/blockArchive.ts +30 -89
  133. package/src/db/repositories/blockArchiveIndex.ts +9 -4
  134. package/src/metrics/metrics/lodestar.ts +1 -57
  135. package/src/monitoring/clientStats.ts +1 -1
  136. package/src/network/gossip/encoding.ts +6 -0
  137. package/src/network/gossip/topic.ts +25 -3
  138. package/src/network/processor/gossipHandlers.ts +11 -11
  139. package/src/network/processor/index.ts +148 -263
  140. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +105 -139
  141. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +0 -2
  142. package/src/network/reqresp/protocols.ts +24 -7
  143. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +7 -15
  144. package/src/node/nodejs.ts +1 -6
  145. package/src/util/sszBytes.ts +1 -1
  146. package/lib/db/dataColumnStore.d.ts +0 -39
  147. package/lib/db/dataColumnStore.d.ts.map +0 -1
  148. package/lib/db/dataColumnStore.js +0 -81
  149. package/lib/db/dataColumnStore.js.map +0 -1
  150. package/lib/db/flatFileStore/atomicWrite.d.ts +0 -8
  151. package/lib/db/flatFileStore/atomicWrite.d.ts.map +0 -1
  152. package/lib/db/flatFileStore/atomicWrite.js +0 -61
  153. package/lib/db/flatFileStore/atomicWrite.js.map +0 -1
  154. package/lib/db/flatFileStore/dcolFormat.d.ts +0 -90
  155. package/lib/db/flatFileStore/dcolFormat.d.ts.map +0 -1
  156. package/lib/db/flatFileStore/dcolFormat.js +0 -286
  157. package/lib/db/flatFileStore/dcolFormat.js.map +0 -1
  158. package/lib/db/flatFileStore/errors.d.ts +0 -82
  159. package/lib/db/flatFileStore/errors.d.ts.map +0 -1
  160. package/lib/db/flatFileStore/errors.js +0 -32
  161. package/lib/db/flatFileStore/errors.js.map +0 -1
  162. package/lib/db/flatFileStore/flatFileStore.d.ts +0 -41
  163. package/lib/db/flatFileStore/flatFileStore.d.ts.map +0 -1
  164. package/lib/db/flatFileStore/flatFileStore.js +0 -251
  165. package/lib/db/flatFileStore/flatFileStore.js.map +0 -1
  166. package/lib/db/flatFileStore/index.d.ts +0 -4
  167. package/lib/db/flatFileStore/index.d.ts.map +0 -1
  168. package/lib/db/flatFileStore/index.js +0 -2
  169. package/lib/db/flatFileStore/index.js.map +0 -1
  170. package/lib/db/flatFileStore/interface.d.ts +0 -25
  171. package/lib/db/flatFileStore/interface.d.ts.map +0 -1
  172. package/lib/db/flatFileStore/interface.js +0 -2
  173. package/lib/db/flatFileStore/interface.js.map +0 -1
  174. package/lib/db/flatFileStore/metrics.d.ts +0 -24
  175. package/lib/db/flatFileStore/metrics.d.ts.map +0 -1
  176. package/lib/db/flatFileStore/metrics.js +0 -25
  177. package/lib/db/flatFileStore/metrics.js.map +0 -1
  178. package/lib/db/flatFileStore/path.d.ts +0 -9
  179. package/lib/db/flatFileStore/path.d.ts.map +0 -1
  180. package/lib/db/flatFileStore/path.js +0 -21
  181. package/lib/db/flatFileStore/path.js.map +0 -1
  182. package/lib/db/flatFileStore/slotIndex.d.ts +0 -14
  183. package/lib/db/flatFileStore/slotIndex.d.ts.map +0 -1
  184. package/lib/db/flatFileStore/slotIndex.js +0 -46
  185. package/lib/db/flatFileStore/slotIndex.js.map +0 -1
  186. package/lib/db/flatFileStore/snappy.d.ts +0 -3
  187. package/lib/db/flatFileStore/snappy.d.ts.map +0 -1
  188. package/lib/db/flatFileStore/snappy.js +0 -12
  189. package/lib/db/flatFileStore/snappy.js.map +0 -1
  190. package/src/db/dataColumnStore.ts +0 -113
  191. package/src/db/flatFileStore/atomicWrite.ts +0 -65
  192. package/src/db/flatFileStore/dcolFormat.ts +0 -389
  193. package/src/db/flatFileStore/errors.ts +0 -53
  194. package/src/db/flatFileStore/flatFileStore.ts +0 -310
  195. package/src/db/flatFileStore/index.ts +0 -3
  196. package/src/db/flatFileStore/interface.ts +0 -21
  197. package/src/db/flatFileStore/metrics.ts +0 -48
  198. package/src/db/flatFileStore/path.ts +0 -32
  199. package/src/db/flatFileStore/slotIndex.ts +0 -56
  200. package/src/db/flatFileStore/snappy.ts +0 -14
@@ -1,11 +1,11 @@
1
1
  import {PeerId} from "@libp2p/interface";
2
2
  import {ChainConfig} from "@lodestar/config";
3
3
  import {PayloadStatus} from "@lodestar/fork-choice";
4
- import {ForkSeq, GENESIS_SLOT, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
4
+ import {ForkSeq, GENESIS_SLOT} from "@lodestar/params";
5
5
  import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
6
6
  import {computeEpochAtSlot} from "@lodestar/state-transition";
7
- import {ColumnIndex, Epoch, RootHex, fulu} from "@lodestar/types";
8
- import {fromHex, toRootHex} from "@lodestar/utils";
7
+ import {ColumnIndex, Epoch, fulu} from "@lodestar/types";
8
+ import {fromHex} from "@lodestar/utils";
9
9
  import {IBeaconChain} from "../../../chain/index.js";
10
10
  import {IBeaconDb} from "../../../db/index.js";
11
11
  import {prettyPrintPeerId} from "../../util.js";
@@ -50,155 +50,121 @@ export async function* onDataColumnSidecarsByRange(
50
50
  );
51
51
  }
52
52
 
53
+ const finalized = db.dataColumnSidecarArchive;
53
54
  const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
54
- // At Gloas, finalizing the beacon block does not finalize its payload. Keep the boundary block in the
55
- // fork-choice-backed section until the following finalization, matching ExecutionPayloadEnvelopesByRange.
55
+ // Columns of the last finalized block live in different DBs depending on fork:
56
+ // - Pre-gloas (fulu): migrated to dataColumnSidecarArchive in the same finalization run.
57
+ // - Post-gloas: stay in the hot db (db.dataColumnSidecar) until the next finalization run,
58
+ // because the migration filter requires payloadStatus === FULL for gloas blocks.
59
+ // archiveMaxSlot is the last slot whose columns are served by the archive loop below;
60
+ // anything above it is served by the headChain loop.
56
61
  const isPostGloasFinalized = chain.config.getForkSeq(finalizedSlot) >= ForkSeq.gloas;
57
62
  const archiveMaxSlot = isPostGloasFinalized ? finalizedSlot - 1 : finalizedSlot;
58
- const headBlock = chain.forkChoice.getHead();
59
- const headChain = chain.forkChoice.getAllAncestorBlocks(headBlock.blockRoot, headBlock.payloadStatus);
60
63
 
61
- for await (const block of resolveCanonicalDataColumnBlocks(
62
- chain,
63
- db,
64
- headChain,
65
- startSlot,
66
- endSlot,
67
- archiveMaxSlot,
68
- finalizedSlot
69
- )) {
70
- const dataColumnSidecars =
71
- block.blockRoot === null
72
- ? await db.dataColumnSidecarArchive.getManyBinary(block.slot, availableColumns)
73
- : await chain.getSerializedDataColumnSidecars(block.slot, block.blockRoot, availableColumns);
74
- if (block.blockRoot === null && dataColumnSidecars.every((sidecar) => sidecar === undefined)) continue;
75
- const unavailableColumnIndices: ColumnIndex[] = [];
76
- for (let i = 0; i < dataColumnSidecars.length; i++) {
77
- const dataColumnSidecarBytes = dataColumnSidecars[i];
78
- if (dataColumnSidecarBytes) {
79
- yield {
80
- data: dataColumnSidecarBytes,
81
- boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(block.slot)),
82
- };
83
- } else {
84
- unavailableColumnIndices.push(availableColumns[i]);
64
+ // Finalized range of columns
65
+ if (startSlot <= archiveMaxSlot) {
66
+ const archiveEnd = Math.min(endSlot, archiveMaxSlot + 1);
67
+ for (let slot = startSlot; slot < archiveEnd; slot++) {
68
+ const dataColumnSidecars = await finalized.getManyBinary(slot, availableColumns);
69
+
70
+ const unavailableColumnIndices: ColumnIndex[] = [];
71
+ for (let i = 0; i < dataColumnSidecars.length; i++) {
72
+ const dataColumnSidecarBytes = dataColumnSidecars[i];
73
+ if (dataColumnSidecarBytes) {
74
+ yield {
75
+ data: dataColumnSidecarBytes,
76
+ boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(slot)),
77
+ };
78
+ }
79
+
80
+ // TODO: Check blobs for that block and respond resource_unavailable
81
+ // After we have consensus from other teams on the specs
82
+ else {
83
+ unavailableColumnIndices.push(availableColumns[i]);
84
+ }
85
85
  }
86
- }
87
-
88
- if (unavailableColumnIndices.length > 0) {
89
- await handleColumnSidecarUnavailability({
90
- chain,
91
- db,
92
- metrics: chain.metrics,
93
- unavailableColumnIndices,
94
- blockRoot: block.unavailabilityBlockRoot ? fromHex(block.unavailabilityBlockRoot) : undefined,
95
- finalized: block.finalized,
96
- slot: block.slot,
97
- requestedColumns,
98
- availableColumns,
99
- });
100
- }
101
- }
102
- }
103
86
 
104
- type CanonicalDataColumnBlock = {
105
- slot: number;
106
- blockRoot: RootHex | null;
107
- unavailabilityBlockRoot?: RootHex;
108
- finalized: boolean;
109
- };
110
-
111
- async function* resolveCanonicalDataColumnBlocks(
112
- chain: IBeaconChain,
113
- db: IBeaconDb,
114
- headChain: ReturnType<IBeaconChain["forkChoice"]["getAllAncestorBlocks"]>,
115
- startSlot: number,
116
- endSlot: number,
117
- archiveMaxSlot: number,
118
- finalizedSlot: number
119
- ): AsyncIterable<CanonicalDataColumnBlock> {
120
- const canonicalBlocksBySlot = new Map(headChain.map((block) => [block.slot, block]));
121
- const oldestForkChoiceSlot = headChain.at(-1)?.slot ?? Number.POSITIVE_INFINITY;
122
- const archiveEnd = Math.min(endSlot, archiveMaxSlot + 1);
123
- const recentRoots = getRecentCanonicalRoots(chain, startSlot, Math.min(archiveEnd, oldestForkChoiceSlot));
124
-
125
- for (let slot = startSlot; slot < archiveEnd; slot++) {
126
- if (slot >= oldestForkChoiceSlot) {
127
- const block = canonicalBlocksBySlot.get(slot);
128
- if (block?.payloadStatus === PayloadStatus.FULL) {
129
- yield {
87
+ if (unavailableColumnIndices.length) {
88
+ await handleColumnSidecarUnavailability({
89
+ chain,
90
+ db,
91
+ metrics: chain.metrics,
92
+ unavailableColumnIndices,
130
93
  slot,
131
- blockRoot: block.blockRoot,
132
- unavailabilityBlockRoot: block.blockRoot,
133
- finalized: true,
134
- };
94
+ requestedColumns,
95
+ availableColumns,
96
+ });
135
97
  }
136
- continue;
137
- }
138
-
139
- let blockRoot = recentRoots.get(slot);
140
- if (blockRoot === null) continue;
141
- if (blockRoot === undefined) {
142
- const root = await db.blockArchive.getRootBySlot(slot);
143
- blockRoot = root === null ? null : toRootHex(root);
144
- }
145
- if (blockRoot === null && (db.lastLegacyArchiveSlot === null || slot > db.lastLegacyArchiveSlot)) continue;
146
- if (
147
- chain.config.getForkSeq(slot) >= ForkSeq.gloas &&
148
- !(await hasExecutionPayloadEnvelope(chain, db, slot, blockRoot))
149
- ) {
150
- continue;
151
98
  }
152
- yield {
153
- slot,
154
- blockRoot,
155
- unavailabilityBlockRoot: chain.config.getForkSeq(slot) >= ForkSeq.gloas ? (blockRoot ?? undefined) : undefined,
156
- finalized: true,
157
- };
158
99
  }
159
100
 
160
- for (let i = headChain.length - 1; i >= 0; i--) {
161
- const block = headChain[i];
162
- if (block.slot >= endSlot) break;
163
- if (block.slot <= archiveMaxSlot || block.slot < startSlot || block.payloadStatus !== PayloadStatus.FULL) continue;
164
- yield {
165
- slot: block.slot,
166
- blockRoot: block.blockRoot,
167
- unavailabilityBlockRoot: block.blockRoot,
168
- finalized: block.slot <= finalizedSlot,
169
- };
170
- }
171
- }
172
-
173
- /** Copy roots synchronously so serving a request never retains a head-state reference across I/O. */
174
- function getRecentCanonicalRoots(chain: IBeaconChain, startSlot: number, endSlot: number): Map<number, RootHex | null> {
175
- const roots = new Map<number, RootHex | null>();
176
- if (startSlot >= endSlot) return roots;
177
- const state = chain.getHeadState();
178
- // Skipped slots repeat the prior root. At the oldest ring entry, that prior root is unavailable.
179
- const start = Math.max(startSlot, state.slot - SLOTS_PER_HISTORICAL_ROOT + 1, 1);
180
- const end = Math.min(endSlot, state.slot);
181
- if (start >= end) return roots;
182
- let previousRoot = toRootHex(state.getBlockRootAtSlot(start - 1));
183
- for (let slot = start; slot < end; slot++) {
184
- const root = toRootHex(state.getBlockRootAtSlot(slot));
185
- roots.set(slot, root === previousRoot ? null : root);
186
- previousRoot = root;
187
- }
188
- return roots;
189
- }
101
+ // Non-finalized range of columns
102
+ if (endSlot > archiveMaxSlot) {
103
+ const headBlock = chain.forkChoice.getHead();
104
+ const headRoot = headBlock.blockRoot;
105
+ // getAllAncestorBlocks includes the last finalized block as its final element.
106
+ // Skip anything the archive loop above already served via the block.slot > archiveMaxSlot
107
+ // filter below (pre-gloas this skips finalizedSlot, post-gloas it keeps it).
108
+ const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
109
+
110
+ // Iterate head chain with ascending block numbers
111
+ for (let i = headChain.length - 1; i >= 0; i--) {
112
+ const block = headChain[i];
113
+
114
+ // Must include only columns in the range requested
115
+ if (block.slot > archiveMaxSlot && block.slot >= startSlot && block.slot < endSlot) {
116
+ // Post-gloas, columns exist only for FULL blocks (pre-gloas blocks are always FULL)
117
+ if (block.payloadStatus !== PayloadStatus.FULL) {
118
+ continue;
119
+ }
120
+
121
+ // Note: Here the forkChoice head may change due to a re-org, so the headChain reflects the canonical chain
122
+ // at the time of the start of the request. Spec is clear the chain of columns must be consistent, but on
123
+ // re-org there's no need to abort the request
124
+ // Spec: https://github.com/ethereum/consensus-specs/blob/ad36024441cf910d428d03f87f331fbbd2b3e5f1/specs/fulu/p2p-interface.md#L425-L429
125
+ const dataColumnSidecars = await chain.getSerializedDataColumnSidecars(
126
+ block.slot,
127
+ block.blockRoot,
128
+ availableColumns
129
+ );
130
+
131
+ const unavailableColumnIndices: ColumnIndex[] = [];
132
+ for (let i = 0; i < dataColumnSidecars.length; i++) {
133
+ const dataColumnSidecarBytes = dataColumnSidecars[i];
134
+ if (dataColumnSidecarBytes) {
135
+ yield {
136
+ data: dataColumnSidecarBytes,
137
+ boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(block.slot)),
138
+ };
139
+ }
140
+
141
+ // TODO: Check blobs for that block and respond resource_unavailable
142
+ // After we have consensus from other teams on the specs
143
+ else {
144
+ unavailableColumnIndices.push(availableColumns[i]);
145
+ }
146
+ }
147
+
148
+ if (unavailableColumnIndices.length) {
149
+ await handleColumnSidecarUnavailability({
150
+ chain,
151
+ db,
152
+ metrics: chain.metrics,
153
+ unavailableColumnIndices,
154
+ blockRoot: fromHex(block.blockRoot),
155
+ slot: block.slot,
156
+ requestedColumns,
157
+ availableColumns,
158
+ });
159
+ }
160
+ }
190
161
 
191
- async function hasExecutionPayloadEnvelope(
192
- chain: IBeaconChain,
193
- db: IBeaconDb,
194
- slot: number,
195
- blockRoot: RootHex | null
196
- ): Promise<boolean> {
197
- if (blockRoot !== null) {
198
- if (chain.seenPayloadEnvelopeInputCache.hasPayload(blockRoot)) return true;
199
- if ((await db.executionPayloadEnvelope.getBinary(fromHex(blockRoot))) !== null) return true;
162
+ // If block is after endSlot, stop iterating
163
+ else if (block.slot >= endSlot) {
164
+ break;
165
+ }
166
+ }
200
167
  }
201
- return (await db.executionPayloadEnvelopeArchive.getBinary(slot)) !== null;
202
168
  }
203
169
 
204
170
  export function validateDataColumnSidecarsByRangeRequest(
@@ -81,14 +81,12 @@ export async function* onDataColumnSidecarsByRoot(
81
81
  }
82
82
 
83
83
  if (unavailableColumnIndices.length) {
84
- const finalized = !block || (await db.blockArchive.getSlotByRoot(blockRoot)) === slot;
85
84
  await handleColumnSidecarUnavailability({
86
85
  chain,
87
86
  db,
88
87
  metrics: chain.metrics,
89
88
  slot,
90
89
  blockRoot,
91
- finalized,
92
90
  unavailableColumnIndices,
93
91
  requestedColumns,
94
92
  availableColumns,
@@ -1,5 +1,5 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
- import {ForkName} from "@lodestar/params";
2
+ import {ForkName, MAX_DATA_COLUMN_SIDECAR_SIZE, isForkPostGloas} from "@lodestar/params";
3
3
  import {ContextBytesFactory, ContextBytesType, Encoding, TypeSizes} from "@lodestar/reqresp";
4
4
  import {rateLimitQuotas} from "./rateLimit.js";
5
5
  import {ProtocolNoHandler, ReqRespMethod, Version, requestSszTypeByMethod, responseSszTypeByMethod} from "./types.js";
@@ -151,18 +151,35 @@ function toProtocol(protocol: ProtocolSummary) {
151
151
  encoding: Encoding.SSZ_SNAPPY,
152
152
  contextBytes: toContextBytes(protocol.contextBytesType, config),
153
153
  inboundRateLimits: rateLimitQuotas(fork, config)[protocol.method],
154
- requestSizes: requestType === null ? null : clampTypeSizes(requestType, config),
155
- responseSizes: (fork) => clampTypeSizes(responseSszTypeByMethod[protocol.method](fork, protocol.version), config),
154
+ requestSizes: requestType === null ? null : clampTypeSizes(requestType, protocol.method, fork, config),
155
+ responseSizes: (fork) =>
156
+ clampTypeSizes(responseSszTypeByMethod[protocol.method](fork, protocol.version), protocol.method, fork, config),
156
157
  };
157
158
  };
158
159
  }
159
160
 
160
161
  /**
161
- * Length-prefix must be within the SSZ type bounds or MAX_PAYLOAD_SIZE, whichever is smaller
162
- * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.12/specs/phase0/p2p-interface.md#encoding-strategies
162
+ * Bound the sizes accepted from the ssz-snappy length-prefix. Gloas progressive containers have broad
163
+ * theoretical SSZ max sizes so the preset p2p bounds must be used instead.
164
+ *
165
+ * The length-prefix must be within the size bounds derived from the payload SSZ type or `MAX_PAYLOAD_SIZE`,
166
+ * whichever is smaller, see
167
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.12/specs/phase0/p2p-interface.md#encoding-strategies.
168
+ * Type-specific SSZ bounds supersede the bounds derived from the SSZ type, see
169
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.12/specs/gloas/p2p-interface.md#type-specific-ssz-bounds.
163
170
  */
164
- function clampTypeSizes(type: TypeSizes, config: BeaconConfig): TypeSizes {
165
- return {minSize: type.minSize, maxSize: Math.min(type.maxSize, config.MAX_PAYLOAD_SIZE)};
171
+ function clampTypeSizes(type: TypeSizes, method: ReqRespMethod, fork: ForkName, config: BeaconConfig): TypeSizes {
172
+ let typeSpecificBound = config.MAX_PAYLOAD_SIZE;
173
+ if (isForkPostGloas(fork)) {
174
+ switch (method) {
175
+ case ReqRespMethod.DataColumnSidecarsByRange:
176
+ case ReqRespMethod.DataColumnSidecarsByRoot:
177
+ typeSpecificBound = MAX_DATA_COLUMN_SIDECAR_SIZE;
178
+ break;
179
+ }
180
+ }
181
+
182
+ return {minSize: type.minSize, maxSize: Math.min(type.maxSize, typeSpecificBound)};
166
183
  }
167
184
 
168
185
  function toContextBytes(type: ContextBytesType, config: BeaconConfig): ContextBytesFactory {
@@ -16,14 +16,12 @@ export async function handleColumnSidecarUnavailability({
16
16
  availableColumns,
17
17
  slot,
18
18
  blockRoot,
19
- finalized,
20
19
  }: {
21
20
  chain: IBeaconChain;
22
21
  db: IBeaconDb;
23
22
  metrics: Metrics | null;
24
23
  slot: Slot;
25
24
  blockRoot?: Uint8Array;
26
- finalized: boolean;
27
25
  unavailableColumnIndices: ColumnIndex[];
28
26
  requestedColumns: ColumnIndex[];
29
27
  availableColumns: ColumnIndex[];
@@ -40,23 +38,17 @@ export async function handleColumnSidecarUnavailability({
40
38
 
41
39
  chain.logger.debug("dataColumnSidecar requested unavailable", logData);
42
40
 
43
- // Post-gloas, columns exist only for FULL blocks. The envelope may still be hot while finalization
44
- // archiving is in progress. Bid blobsCount is unreliable since an EMPTY block's bid may still commit to blobs.
45
- if (chain.config.getForkSeq(slot) >= ForkSeq.gloas) {
46
- const hasCachedEnvelope = blockRoot ? chain.seenPayloadEnvelopeInputCache.hasPayload(toRootHex(blockRoot)) : false;
47
- if (!hasCachedEnvelope) {
48
- const hotEnvelopeBytes = blockRoot ? await db.executionPayloadEnvelope.getBinary(blockRoot) : null;
49
- const envelopeBytes =
50
- hotEnvelopeBytes ?? (finalized ? await db.executionPayloadEnvelopeArchive.getBinary(slot) : null);
51
- if (!envelopeBytes) return;
52
- }
41
+ // Post-gloas, columns exist only for FULL blocks; a finalized block is FULL if its envelope was
42
+ // archived. Bid blobsCount is unreliable here since an EMPTY block's bid may still commit to blobs
43
+ if (blockRoot === undefined && chain.config.getForkSeq(slot) >= ForkSeq.gloas) {
44
+ const envelopeBytes = await db.executionPayloadEnvelopeArchive.getBinary(slot);
45
+ if (!envelopeBytes) return;
53
46
  }
54
47
 
55
- const hotBlockBytes = blockRoot ? await db.block.getBinary(blockRoot) : null;
56
- const blockBytes = hotBlockBytes ?? (finalized ? await db.blockArchive.getBinary(slot) : null);
48
+ const blockBytes = blockRoot ? await db.block.getBinary(blockRoot) : await db.blockArchive.getBinary(slot);
57
49
  if (!blockBytes) {
58
50
  chain.logger.verbose(
59
- `Expected ${finalized ? "finalized" : "unfinalized"} block not found while handling unavailable dataColumnSidecar`,
51
+ `Expected ${blockRoot ? "unfinalized" : "finalized"} block not found while handling unavailable dataColumnSidecar`,
60
52
  {
61
53
  slot,
62
54
  blockRoot: blockRoot ? toRootHex(blockRoot) : "unknown",
@@ -54,7 +54,6 @@ export type BeaconNodeInitModules = {
54
54
  processShutdownCallback: ProcessShutdownCallback;
55
55
  privateKey: PrivateKey;
56
56
  dataDir: string;
57
- dataColumnDir: string;
58
57
  peerStoreDir?: string;
59
58
  anchorState: IBeaconStateView;
60
59
  isAnchorStateFinalized: boolean;
@@ -157,7 +156,6 @@ export class BeaconNode {
157
156
  processShutdownCallback,
158
157
  privateKey,
159
158
  dataDir,
160
- dataColumnDir,
161
159
  peerStoreDir,
162
160
  anchorState,
163
161
  isAnchorStateFinalized,
@@ -183,7 +181,7 @@ export class BeaconNode {
183
181
  metrics = createMetrics(opts.metrics, anchorState.genesisTime, metricsRegistries);
184
182
  initBeaconMetrics(metrics, anchorState);
185
183
  // Since the db is instantiated before this, metrics must be injected manually afterwards
186
- db.setMetrics(metrics.db, metrics.flatFileStore);
184
+ db.setMetrics(metrics.db);
187
185
  signal.addEventListener("abort", metrics.close, {once: true});
188
186
  }
189
187
 
@@ -200,8 +198,6 @@ export class BeaconNode {
200
198
 
201
199
  const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
202
200
 
203
- await db.init();
204
-
205
201
  // Prune hot db repos
206
202
  // TODO: Should this call be awaited?
207
203
  await db.pruneHotDb();
@@ -248,7 +244,6 @@ export class BeaconNode {
248
244
  clock,
249
245
  pubkeyCache,
250
246
  dataDir,
251
- dataColumnDir,
252
247
  db,
253
248
  dbName: opts.db.name,
254
249
  logger: logger.child({module: LoggerModule.chain}),
@@ -754,7 +754,7 @@ export function getParentBlockRootFromSignedExecutionPayloadBidSerialized(data:
754
754
  *
755
755
  * If the high bytes are not zero, return null
756
756
  */
757
- export function getSlotFromOffset(data: Uint8Array, offset: number): Slot | null {
757
+ function getSlotFromOffset(data: Uint8Array, offset: number): Slot | null {
758
758
  return checkSlotHighBytes(data, offset) ? getSlotFromOffsetTrusted(data, offset) : null;
759
759
  }
760
760
 
@@ -1,39 +0,0 @@
1
- import { ColumnIndex, DataColumnSidecar, RootHex, Slot } from "@lodestar/types";
2
- import type { IFlatFileStore } from "./flatFileStore/interface.js";
3
- import type { BlockArchiveRepository, DataColumnSidecarArchiveRepository, DataColumnSidecarRepository } from "./repositories/index.js";
4
- export type DataColumnKey = {
5
- slot: Slot;
6
- blockRoot: RootHex;
7
- };
8
- export type IndexedDataColumnBytes = {
9
- index: ColumnIndex;
10
- data: Uint8Array;
11
- };
12
- type LegacyHotDataColumnStore = Pick<DataColumnSidecarRepository, "values" | "getManyBinary" | "deleteMany">;
13
- type LegacyArchiveDataColumnStore = Pick<DataColumnSidecarArchiveRepository, "values" | "getManyBinary" | "keys" | "deleteMany">;
14
- type BlockRootIndex = Pick<BlockArchiveRepository, "getSlotByRoot">;
15
- export interface IDataColumnStore {
16
- getAll(key: DataColumnKey): Promise<DataColumnSidecar[]>;
17
- getManyBinary(key: DataColumnKey, indices: ColumnIndex[]): Promise<(Uint8Array | undefined)[]>;
18
- putManyBinary(key: DataColumnKey, columns: IndexedDataColumnBytes[]): Promise<void>;
19
- deleteMany(keys: DataColumnKey[]): Promise<void>;
20
- pruneBefore(slot: Slot): Promise<Slot[]>;
21
- }
22
- /**
23
- * Bridges pre-flat-file LevelDB columns during the supported in-place upgrade window.
24
- * Remove the legacy repositories after upgrades from the last pre-flat-file release are no longer supported.
25
- */
26
- export declare class LegacyDataColumnStore implements IDataColumnStore {
27
- private readonly flatFiles;
28
- private readonly legacyHot;
29
- private readonly legacyArchive;
30
- private readonly blockArchive;
31
- constructor(flatFiles: IFlatFileStore, legacyHot: LegacyHotDataColumnStore, legacyArchive: LegacyArchiveDataColumnStore, blockArchive: BlockRootIndex);
32
- getAll({ slot, blockRoot }: DataColumnKey): Promise<DataColumnSidecar[]>;
33
- getManyBinary({ slot, blockRoot }: DataColumnKey, indices: ColumnIndex[]): Promise<(Uint8Array | undefined)[]>;
34
- putManyBinary({ slot, blockRoot }: DataColumnKey, columns: IndexedDataColumnBytes[]): Promise<void>;
35
- deleteMany(keys: DataColumnKey[]): Promise<void>;
36
- pruneBefore(slot: Slot): Promise<Slot[]>;
37
- }
38
- export {};
39
- //# sourceMappingURL=dataColumnStore.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dataColumnStore.d.ts","sourceRoot":"","sources":["../../src/db/dataColumnStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAE9E,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EACV,sBAAsB,EACtB,kCAAkC,EAClC,2BAA2B,EAC5B,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,aAAa,GAAG;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAC,CAAC;AAC7D,MAAM,MAAM,sBAAsB,GAAG;IAAC,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAC,CAAC;AAE5E,KAAK,wBAAwB,GAAG,IAAI,CAAC,2BAA2B,EAAE,QAAQ,GAAG,eAAe,GAAG,YAAY,CAAC,CAAC;AAC7G,KAAK,4BAA4B,GAAG,IAAI,CACtC,kCAAkC,EAClC,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,YAAY,CACnD,CAAC;AACF,KAAK,cAAc,GAAG,IAAI,CAAC,sBAAsB,EAAE,eAAe,CAAC,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACzD,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAC/F,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,qBAAa,qBAAsB,YAAW,gBAAgB;IAE1D,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJ/B,YACmB,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,wBAAwB,EACnC,aAAa,EAAE,4BAA4B,EAC3C,YAAY,EAAE,cAAc,EAC3C;IAEE,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiB3E;IAEK,aAAa,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAAC,CAiBjH;IAEK,aAAa,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,aAAa,EAAE,OAAO,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtG;IAEK,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrD;IAEK,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAM7C;CACF"}
@@ -1,81 +0,0 @@
1
- import { fromHex } from "@lodestar/utils";
2
- /**
3
- * Bridges pre-flat-file LevelDB columns during the supported in-place upgrade window.
4
- * Remove the legacy repositories after upgrades from the last pre-flat-file release are no longer supported.
5
- */
6
- export class LegacyDataColumnStore {
7
- flatFiles;
8
- legacyHot;
9
- legacyArchive;
10
- blockArchive;
11
- constructor(flatFiles, legacyHot, legacyArchive, blockArchive) {
12
- this.flatFiles = flatFiles;
13
- this.legacyHot = legacyHot;
14
- this.legacyArchive = legacyArchive;
15
- this.blockArchive = blockArchive;
16
- }
17
- async getAll({ slot, blockRoot }) {
18
- const sidecars = await this.flatFiles.getDataColumns(slot, blockRoot);
19
- if (sidecars.length > 0)
20
- return sidecars;
21
- const sidecarsByIndex = new Map();
22
- const root = fromHex(blockRoot);
23
- for (const sidecar of await this.legacyHot.values(root)) {
24
- if (!sidecarsByIndex.has(sidecar.index))
25
- sidecarsByIndex.set(sidecar.index, sidecar);
26
- }
27
- if ((await this.blockArchive.getSlotByRoot(root)) === slot) {
28
- for (const sidecar of await this.legacyArchive.values(slot)) {
29
- if (!sidecarsByIndex.has(sidecar.index))
30
- sidecarsByIndex.set(sidecar.index, sidecar);
31
- }
32
- }
33
- return [...sidecarsByIndex.values()].sort((a, b) => a.index - b.index);
34
- }
35
- async getManyBinary({ slot, blockRoot }, indices) {
36
- const flatFileSidecars = await this.flatFiles.getDataColumnsBinary(slot, blockRoot, indices);
37
- if (flatFileSidecars !== null)
38
- return flatFileSidecars;
39
- if (indices.length === 0)
40
- return [];
41
- const root = fromHex(blockRoot);
42
- const result = await this.legacyHot.getManyBinary(root, indices);
43
- const missingPositions = getMissingPositions(result);
44
- if (missingPositions.length === 0 || (await this.blockArchive.getSlotByRoot(root)) !== slot)
45
- return result;
46
- const archivedSidecars = await this.legacyArchive.getManyBinary(slot, missingPositions.map((position) => indices[position]));
47
- fillMissing(result, missingPositions, archivedSidecars);
48
- return result;
49
- }
50
- async putManyBinary({ slot, blockRoot }, columns) {
51
- await this.flatFiles.putDataColumnsBinary(slot, blockRoot, columns);
52
- }
53
- async deleteMany(keys) {
54
- await this.flatFiles.deleteMany(keys);
55
- await this.legacyHot.deleteMany(keys.map(({ blockRoot }) => fromHex(blockRoot)));
56
- }
57
- async pruneBefore(slot) {
58
- const flatFileSlots = await this.flatFiles.pruneBefore(slot);
59
- const prefixedKeys = await this.legacyArchive.keys({ lt: { prefix: slot, id: 0 } });
60
- const slots = [...new Set(prefixedKeys.map(({ prefix }) => prefix))];
61
- if (slots.length > 0)
62
- await this.legacyArchive.deleteMany(slots);
63
- return [...new Set([...flatFileSlots, ...slots])].sort((a, b) => a - b);
64
- }
65
- }
66
- function getMissingPositions(sidecars) {
67
- const positions = [];
68
- for (let i = 0; i < sidecars.length; i++) {
69
- if (sidecars[i] === undefined)
70
- positions.push(i);
71
- }
72
- return positions;
73
- }
74
- function fillMissing(result, missingPositions, sidecars) {
75
- for (let i = 0; i < missingPositions.length; i++) {
76
- const sidecar = sidecars[i];
77
- if (sidecar != null)
78
- result[missingPositions[i]] = sidecar;
79
- }
80
- }
81
- //# sourceMappingURL=dataColumnStore.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dataColumnStore.js","sourceRoot":"","sources":["../../src/db/dataColumnStore.ts"],"names":[],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- /**
2
- * Atomic write: write to `.part` temp file -> datasync -> rename to target.
3
- * This guarantees crash-safe writes — if the process crashes mid-write,
4
- * only the `.part` file is corrupted, never the target.
5
- */
6
- export declare function atomicWrite(targetPath: string, data: Uint8Array): Promise<void>;
7
- export declare function mkdirDurable(dir: string): Promise<void>;
8
- //# sourceMappingURL=atomicWrite.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"atomicWrite.d.ts","sourceRoot":"","sources":["../../../src/db/flatFileStore/atomicWrite.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBrF;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB7D"}
@@ -1,61 +0,0 @@
1
- import { mkdir, open, rename, rm } from "node:fs/promises";
2
- import path from "node:path";
3
- const pendingDirectorySyncs = new Map();
4
- /**
5
- * Atomic write: write to `.part` temp file -> datasync -> rename to target.
6
- * This guarantees crash-safe writes — if the process crashes mid-write,
7
- * only the `.part` file is corrupted, never the target.
8
- */
9
- export async function atomicWrite(targetPath, data) {
10
- const dir = path.dirname(targetPath);
11
- const partPath = `${targetPath}.part-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
12
- await mkdirDurable(dir);
13
- try {
14
- const fd = await open(partPath, "wx");
15
- try {
16
- await fd.writeFile(data);
17
- await fd.datasync();
18
- }
19
- finally {
20
- await fd.close();
21
- }
22
- await rename(partPath, targetPath);
23
- // Ensure rename metadata is durable on crash.
24
- await syncDirectory(dir);
25
- }
26
- catch (e) {
27
- await rm(partPath, { force: true }).catch(() => { });
28
- throw e;
29
- }
30
- }
31
- export async function mkdirDurable(dir) {
32
- const targetDir = path.resolve(dir);
33
- let directoriesToSync = pendingDirectorySyncs.get(targetDir);
34
- if (directoriesToSync === undefined) {
35
- const firstCreatedDir = await mkdir(targetDir, { recursive: true });
36
- if (firstCreatedDir === undefined)
37
- return;
38
- let createdDir = path.resolve(firstCreatedDir);
39
- directoriesToSync = [path.dirname(createdDir)];
40
- while (createdDir !== targetDir) {
41
- directoriesToSync.push(createdDir);
42
- const [nextSegment] = path.relative(createdDir, targetDir).split(path.sep);
43
- createdDir = path.join(createdDir, nextSegment);
44
- }
45
- pendingDirectorySyncs.set(targetDir, directoriesToSync);
46
- }
47
- for (const directory of directoriesToSync) {
48
- await syncDirectory(directory);
49
- }
50
- pendingDirectorySyncs.delete(targetDir);
51
- }
52
- async function syncDirectory(dir) {
53
- const dirFd = await open(dir, "r");
54
- try {
55
- await dirFd.sync();
56
- }
57
- finally {
58
- await dirFd.close();
59
- }
60
- }
61
- //# sourceMappingURL=atomicWrite.js.map