@lodestar/beacon-node 1.48.0-dev.fd70e5fc15 → 1.48.0-rc.0

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 +1 -0
  5. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  6. package/lib/chain/archiveStore/archiveStore.js +1 -0
  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 +1 -0
  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 +12 -1
  13. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  14. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -0
  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 +4 -1
  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 +31 -34
  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 +3 -2
  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 +9 -16
  27. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  28. package/lib/chain/chain.d.ts +4 -3
  29. package/lib/chain/chain.d.ts.map +1 -1
  30. package/lib/chain/chain.js +60 -35
  31. package/lib/chain/chain.js.map +1 -1
  32. package/lib/chain/errors/blockError.d.ts +16 -2
  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 +15 -2
  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 +10 -0
  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 +26 -4
  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 +38 -13
  50. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  51. package/lib/chain/validation/executionPayloadEnvelope.js +10 -0
  52. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  53. package/lib/db/beacon.d.ts +14 -5
  54. package/lib/db/beacon.d.ts.map +1 -1
  55. package/lib/db/beacon.js +17 -3
  56. package/lib/db/beacon.js.map +1 -1
  57. package/lib/db/dataColumnStore.d.ts +39 -0
  58. package/lib/db/dataColumnStore.d.ts.map +1 -0
  59. package/lib/db/dataColumnStore.js +81 -0
  60. package/lib/db/dataColumnStore.js.map +1 -0
  61. package/lib/db/flatFileStore/atomicWrite.d.ts +8 -0
  62. package/lib/db/flatFileStore/atomicWrite.d.ts.map +1 -0
  63. package/lib/db/flatFileStore/atomicWrite.js +61 -0
  64. package/lib/db/flatFileStore/atomicWrite.js.map +1 -0
  65. package/lib/db/flatFileStore/dcolFormat.d.ts +90 -0
  66. package/lib/db/flatFileStore/dcolFormat.d.ts.map +1 -0
  67. package/lib/db/flatFileStore/dcolFormat.js +286 -0
  68. package/lib/db/flatFileStore/dcolFormat.js.map +1 -0
  69. package/lib/db/flatFileStore/errors.d.ts +82 -0
  70. package/lib/db/flatFileStore/errors.d.ts.map +1 -0
  71. package/lib/db/flatFileStore/errors.js +32 -0
  72. package/lib/db/flatFileStore/errors.js.map +1 -0
  73. package/lib/db/flatFileStore/flatFileStore.d.ts +41 -0
  74. package/lib/db/flatFileStore/flatFileStore.d.ts.map +1 -0
  75. package/lib/db/flatFileStore/flatFileStore.js +251 -0
  76. package/lib/db/flatFileStore/flatFileStore.js.map +1 -0
  77. package/lib/db/flatFileStore/index.d.ts +4 -0
  78. package/lib/db/flatFileStore/index.d.ts.map +1 -0
  79. package/lib/db/flatFileStore/index.js +2 -0
  80. package/lib/db/flatFileStore/index.js.map +1 -0
  81. package/lib/db/flatFileStore/interface.d.ts +25 -0
  82. package/lib/db/flatFileStore/interface.d.ts.map +1 -0
  83. package/lib/db/flatFileStore/interface.js +2 -0
  84. package/lib/db/flatFileStore/interface.js.map +1 -0
  85. package/lib/db/flatFileStore/metrics.d.ts +24 -0
  86. package/lib/db/flatFileStore/metrics.d.ts.map +1 -0
  87. package/lib/db/flatFileStore/metrics.js +25 -0
  88. package/lib/db/flatFileStore/metrics.js.map +1 -0
  89. package/lib/db/flatFileStore/path.d.ts +9 -0
  90. package/lib/db/flatFileStore/path.d.ts.map +1 -0
  91. package/lib/db/flatFileStore/path.js +21 -0
  92. package/lib/db/flatFileStore/path.js.map +1 -0
  93. package/lib/db/flatFileStore/slotIndex.d.ts +14 -0
  94. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -0
  95. package/lib/db/flatFileStore/slotIndex.js +46 -0
  96. package/lib/db/flatFileStore/slotIndex.js.map +1 -0
  97. package/lib/db/flatFileStore/snappy.d.ts +3 -0
  98. package/lib/db/flatFileStore/snappy.d.ts.map +1 -0
  99. package/lib/db/flatFileStore/snappy.js +12 -0
  100. package/lib/db/flatFileStore/snappy.js.map +1 -0
  101. package/lib/db/index.d.ts +1 -0
  102. package/lib/db/index.d.ts.map +1 -1
  103. package/lib/db/interface.d.ts +8 -1
  104. package/lib/db/interface.d.ts.map +1 -1
  105. package/lib/db/repositories/blockArchive.d.ts +8 -1
  106. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  107. package/lib/db/repositories/blockArchive.js +65 -28
  108. package/lib/db/repositories/blockArchive.js.map +1 -1
  109. package/lib/db/repositories/blockArchiveIndex.d.ts +1 -2
  110. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  111. package/lib/db/repositories/blockArchiveIndex.js +3 -7
  112. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  113. package/lib/metrics/metrics/lodestar.d.ts +18 -0
  114. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  115. package/lib/metrics/metrics/lodestar.js +55 -1
  116. package/lib/metrics/metrics/lodestar.js.map +1 -1
  117. package/lib/monitoring/clientStats.js +1 -1
  118. package/lib/monitoring/clientStats.js.map +1 -1
  119. package/lib/network/gossip/encoding.d.ts.map +1 -1
  120. package/lib/network/gossip/encoding.js +0 -7
  121. package/lib/network/gossip/encoding.js.map +1 -1
  122. package/lib/network/gossip/topic.d.ts +2 -1
  123. package/lib/network/gossip/topic.d.ts.map +1 -1
  124. package/lib/network/gossip/topic.js +4 -20
  125. package/lib/network/gossip/topic.js.map +1 -1
  126. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  127. package/lib/network/processor/gossipHandlers.js +6 -7
  128. package/lib/network/processor/gossipHandlers.js.map +1 -1
  129. package/lib/network/processor/index.d.ts +49 -3
  130. package/lib/network/processor/index.d.ts.map +1 -1
  131. package/lib/network/processor/index.js +232 -98
  132. package/lib/network/processor/index.js.map +1 -1
  133. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  134. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +112 -91
  135. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  136. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  137. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +2 -0
  138. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  139. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  140. package/lib/network/reqresp/protocols.js +6 -22
  141. package/lib/network/reqresp/protocols.js.map +1 -1
  142. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +2 -1
  143. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  144. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -9
  145. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  146. package/lib/node/nodejs.d.ts +2 -1
  147. package/lib/node/nodejs.d.ts.map +1 -1
  148. package/lib/node/nodejs.js +4 -2
  149. package/lib/node/nodejs.js.map +1 -1
  150. package/lib/util/sszBytes.d.ts +6 -0
  151. package/lib/util/sszBytes.d.ts.map +1 -1
  152. package/lib/util/sszBytes.js +1 -1
  153. package/lib/util/sszBytes.js.map +1 -1
  154. package/package.json +17 -18
  155. package/src/api/impl/beacon/blocks/index.ts +8 -4
  156. package/src/chain/archiveStore/archiveStore.ts +6 -1
  157. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -0
  158. package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
  159. package/src/chain/archiveStore/historicalState/types.ts +2 -0
  160. package/src/chain/archiveStore/historicalState/worker.ts +4 -1
  161. package/src/chain/archiveStore/utils/archiveBlocks.ts +34 -39
  162. package/src/chain/blocks/index.ts +13 -2
  163. package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
  164. package/src/chain/chain.ts +75 -34
  165. package/src/chain/errors/blockError.ts +20 -8
  166. package/src/chain/errors/dataColumnSidecarError.ts +24 -8
  167. package/src/chain/errors/executionPayloadEnvelope.ts +14 -2
  168. package/src/chain/interface.ts +2 -2
  169. package/src/chain/prepareNextSlot.ts +16 -16
  170. package/src/chain/validation/block.ts +26 -4
  171. package/src/chain/validation/dataColumnSidecar.ts +47 -14
  172. package/src/chain/validation/executionPayloadEnvelope.ts +10 -0
  173. package/src/db/beacon.ts +33 -6
  174. package/src/db/dataColumnStore.ts +113 -0
  175. package/src/db/flatFileStore/atomicWrite.ts +65 -0
  176. package/src/db/flatFileStore/dcolFormat.ts +389 -0
  177. package/src/db/flatFileStore/errors.ts +53 -0
  178. package/src/db/flatFileStore/flatFileStore.ts +310 -0
  179. package/src/db/flatFileStore/index.ts +3 -0
  180. package/src/db/flatFileStore/interface.ts +21 -0
  181. package/src/db/flatFileStore/metrics.ts +48 -0
  182. package/src/db/flatFileStore/path.ts +32 -0
  183. package/src/db/flatFileStore/slotIndex.ts +56 -0
  184. package/src/db/flatFileStore/snappy.ts +14 -0
  185. package/src/db/index.ts +1 -0
  186. package/src/db/interface.ts +9 -1
  187. package/src/db/repositories/blockArchive.ts +89 -30
  188. package/src/db/repositories/blockArchiveIndex.ts +4 -9
  189. package/src/metrics/metrics/lodestar.ts +57 -1
  190. package/src/monitoring/clientStats.ts +1 -1
  191. package/src/network/gossip/encoding.ts +0 -7
  192. package/src/network/gossip/topic.ts +3 -25
  193. package/src/network/processor/gossipHandlers.ts +11 -11
  194. package/src/network/processor/index.ts +263 -148
  195. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +139 -105
  196. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +2 -0
  197. package/src/network/reqresp/protocols.ts +7 -24
  198. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +15 -7
  199. package/src/node/nodejs.ts +6 -1
  200. package/src/util/sszBytes.ts +1 -1
@@ -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} from "@lodestar/params";
4
+ import {ForkSeq, GENESIS_SLOT, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
5
5
  import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
6
6
  import {computeEpochAtSlot} from "@lodestar/state-transition";
7
- import {ColumnIndex, Epoch, fulu} from "@lodestar/types";
8
- import {fromHex} from "@lodestar/utils";
7
+ import {ColumnIndex, Epoch, RootHex, fulu} from "@lodestar/types";
8
+ import {fromHex, toRootHex} 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,121 +50,155 @@ export async function* onDataColumnSidecarsByRange(
50
50
  );
51
51
  }
52
52
 
53
- const finalized = db.dataColumnSidecarArchive;
54
53
  const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
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.
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.
61
56
  const isPostGloasFinalized = chain.config.getForkSeq(finalizedSlot) >= ForkSeq.gloas;
62
57
  const archiveMaxSlot = isPostGloasFinalized ? finalizedSlot - 1 : finalizedSlot;
58
+ const headBlock = chain.forkChoice.getHead();
59
+ const headChain = chain.forkChoice.getAllAncestorBlocks(headBlock.blockRoot, headBlock.payloadStatus);
63
60
 
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
- }
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]);
85
85
  }
86
+ }
86
87
 
87
- if (unavailableColumnIndices.length) {
88
- await handleColumnSidecarUnavailability({
89
- chain,
90
- db,
91
- metrics: chain.metrics,
92
- unavailableColumnIndices,
93
- slot,
94
- requestedColumns,
95
- availableColumns,
96
- });
97
- }
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
+ });
98
100
  }
99
101
  }
102
+ }
100
103
 
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
- }
104
+ type CanonicalDataColumnBlock = {
105
+ slot: number;
106
+ blockRoot: RootHex | null;
107
+ unavailabilityBlockRoot?: RootHex;
108
+ finalized: boolean;
109
+ };
161
110
 
162
- // If block is after endSlot, stop iterating
163
- else if (block.slot >= endSlot) {
164
- break;
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 {
130
+ slot,
131
+ blockRoot: block.blockRoot,
132
+ unavailabilityBlockRoot: block.blockRoot,
133
+ finalized: true,
134
+ };
165
135
  }
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);
166
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
+ }
152
+ yield {
153
+ slot,
154
+ blockRoot,
155
+ unavailabilityBlockRoot: chain.config.getForkSeq(slot) >= ForkSeq.gloas ? (blockRoot ?? undefined) : undefined,
156
+ finalized: true,
157
+ };
158
+ }
159
+
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
+ }
190
+
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;
167
200
  }
201
+ return (await db.executionPayloadEnvelopeArchive.getBinary(slot)) !== null;
168
202
  }
169
203
 
170
204
  export function validateDataColumnSidecarsByRangeRequest(
@@ -81,12 +81,14 @@ export async function* onDataColumnSidecarsByRoot(
81
81
  }
82
82
 
83
83
  if (unavailableColumnIndices.length) {
84
+ const finalized = !block || (await db.blockArchive.getSlotByRoot(blockRoot)) === slot;
84
85
  await handleColumnSidecarUnavailability({
85
86
  chain,
86
87
  db,
87
88
  metrics: chain.metrics,
88
89
  slot,
89
90
  blockRoot,
91
+ finalized,
90
92
  unavailableColumnIndices,
91
93
  requestedColumns,
92
94
  availableColumns,
@@ -1,5 +1,5 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
- import {ForkName, MAX_DATA_COLUMN_SIDECAR_SIZE, isForkPostGloas} from "@lodestar/params";
2
+ import {ForkName} 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,35 +151,18 @@ 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, protocol.method, fork, config),
155
- responseSizes: (fork) =>
156
- clampTypeSizes(responseSszTypeByMethod[protocol.method](fork, protocol.version), protocol.method, fork, config),
154
+ requestSizes: requestType === null ? null : clampTypeSizes(requestType, config),
155
+ responseSizes: (fork) => clampTypeSizes(responseSszTypeByMethod[protocol.method](fork, protocol.version), config),
157
156
  };
158
157
  };
159
158
  }
160
159
 
161
160
  /**
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.
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
170
163
  */
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)};
164
+ function clampTypeSizes(type: TypeSizes, config: BeaconConfig): TypeSizes {
165
+ return {minSize: type.minSize, maxSize: Math.min(type.maxSize, config.MAX_PAYLOAD_SIZE)};
183
166
  }
184
167
 
185
168
  function toContextBytes(type: ContextBytesType, config: BeaconConfig): ContextBytesFactory {
@@ -16,12 +16,14 @@ export async function handleColumnSidecarUnavailability({
16
16
  availableColumns,
17
17
  slot,
18
18
  blockRoot,
19
+ finalized,
19
20
  }: {
20
21
  chain: IBeaconChain;
21
22
  db: IBeaconDb;
22
23
  metrics: Metrics | null;
23
24
  slot: Slot;
24
25
  blockRoot?: Uint8Array;
26
+ finalized: boolean;
25
27
  unavailableColumnIndices: ColumnIndex[];
26
28
  requestedColumns: ColumnIndex[];
27
29
  availableColumns: ColumnIndex[];
@@ -38,17 +40,23 @@ export async function handleColumnSidecarUnavailability({
38
40
 
39
41
  chain.logger.debug("dataColumnSidecar requested unavailable", logData);
40
42
 
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;
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
+ }
46
53
  }
47
54
 
48
- const blockBytes = blockRoot ? await db.block.getBinary(blockRoot) : await db.blockArchive.getBinary(slot);
55
+ const hotBlockBytes = blockRoot ? await db.block.getBinary(blockRoot) : null;
56
+ const blockBytes = hotBlockBytes ?? (finalized ? await db.blockArchive.getBinary(slot) : null);
49
57
  if (!blockBytes) {
50
58
  chain.logger.verbose(
51
- `Expected ${blockRoot ? "unfinalized" : "finalized"} block not found while handling unavailable dataColumnSidecar`,
59
+ `Expected ${finalized ? "finalized" : "unfinalized"} block not found while handling unavailable dataColumnSidecar`,
52
60
  {
53
61
  slot,
54
62
  blockRoot: blockRoot ? toRootHex(blockRoot) : "unknown",
@@ -54,6 +54,7 @@ export type BeaconNodeInitModules = {
54
54
  processShutdownCallback: ProcessShutdownCallback;
55
55
  privateKey: PrivateKey;
56
56
  dataDir: string;
57
+ dataColumnDir: string;
57
58
  peerStoreDir?: string;
58
59
  anchorState: IBeaconStateView;
59
60
  isAnchorStateFinalized: boolean;
@@ -156,6 +157,7 @@ export class BeaconNode {
156
157
  processShutdownCallback,
157
158
  privateKey,
158
159
  dataDir,
160
+ dataColumnDir,
159
161
  peerStoreDir,
160
162
  anchorState,
161
163
  isAnchorStateFinalized,
@@ -181,7 +183,7 @@ export class BeaconNode {
181
183
  metrics = createMetrics(opts.metrics, anchorState.genesisTime, metricsRegistries);
182
184
  initBeaconMetrics(metrics, anchorState);
183
185
  // Since the db is instantiated before this, metrics must be injected manually afterwards
184
- db.setMetrics(metrics.db);
186
+ db.setMetrics(metrics.db, metrics.flatFileStore);
185
187
  signal.addEventListener("abort", metrics.close, {once: true});
186
188
  }
187
189
 
@@ -198,6 +200,8 @@ export class BeaconNode {
198
200
 
199
201
  const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
200
202
 
203
+ await db.init();
204
+
201
205
  // Prune hot db repos
202
206
  // TODO: Should this call be awaited?
203
207
  await db.pruneHotDb();
@@ -244,6 +248,7 @@ export class BeaconNode {
244
248
  clock,
245
249
  pubkeyCache,
246
250
  dataDir,
251
+ dataColumnDir,
247
252
  db,
248
253
  dbName: opts.db.name,
249
254
  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
- function getSlotFromOffset(data: Uint8Array, offset: number): Slot | null {
757
+ export function getSlotFromOffset(data: Uint8Array, offset: number): Slot | null {
758
758
  return checkSlotHighBytes(data, offset) ? getSlotFromOffsetTrusted(data, offset) : null;
759
759
  }
760
760