@lodestar/beacon-node 1.43.0-dev.a140dd987e → 1.43.0-dev.a45ba75824

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 (233) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +16 -5
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +45 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/lodestar/index.js +1 -1
  8. package/lib/api/impl/lodestar/index.js.map +1 -1
  9. package/lib/api/impl/validator/index.d.ts.map +1 -1
  10. package/lib/api/impl/validator/index.js +66 -1
  11. package/lib/api/impl/validator/index.js.map +1 -1
  12. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  13. package/lib/chain/blocks/importBlock.js +10 -18
  14. package/lib/chain/blocks/importBlock.js.map +1 -1
  15. package/lib/chain/blocks/importExecutionPayload.d.ts +19 -6
  16. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  17. package/lib/chain/blocks/importExecutionPayload.js +43 -19
  18. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  19. package/lib/chain/blocks/index.d.ts +5 -3
  20. package/lib/chain/blocks/index.d.ts.map +1 -1
  21. package/lib/chain/blocks/index.js +31 -10
  22. package/lib/chain/blocks/index.js.map +1 -1
  23. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +1 -0
  24. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  25. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +5 -1
  26. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  27. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
  28. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  29. package/lib/chain/blocks/payloadEnvelopeProcessor.js +2 -2
  30. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  31. package/lib/chain/blocks/types.d.ts +2 -2
  32. package/lib/chain/blocks/types.d.ts.map +1 -1
  33. package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
  34. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  35. package/lib/chain/blocks/utils/chainSegment.js +81 -12
  36. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  37. package/lib/chain/blocks/verifyBlock.d.ts +5 -3
  38. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  39. package/lib/chain/blocks/verifyBlock.js +51 -7
  40. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  41. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  42. package/lib/chain/blocks/verifyBlocksSanityChecks.js +15 -4
  43. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  44. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  45. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  46. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  47. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  48. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  49. package/lib/chain/chain.d.ts +3 -2
  50. package/lib/chain/chain.d.ts.map +1 -1
  51. package/lib/chain/chain.js +12 -4
  52. package/lib/chain/chain.js.map +1 -1
  53. package/lib/chain/emitter.d.ts +0 -11
  54. package/lib/chain/emitter.d.ts.map +1 -1
  55. package/lib/chain/emitter.js +0 -4
  56. package/lib/chain/emitter.js.map +1 -1
  57. package/lib/chain/errors/blockError.d.ts +8 -1
  58. package/lib/chain/errors/blockError.d.ts.map +1 -1
  59. package/lib/chain/errors/blockError.js +2 -0
  60. package/lib/chain/errors/blockError.js.map +1 -1
  61. package/lib/chain/errors/index.d.ts +1 -0
  62. package/lib/chain/errors/index.d.ts.map +1 -1
  63. package/lib/chain/errors/index.js +1 -0
  64. package/lib/chain/errors/index.js.map +1 -1
  65. package/lib/chain/errors/proposerPreferences.d.ts +33 -0
  66. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
  67. package/lib/chain/errors/proposerPreferences.js +13 -0
  68. package/lib/chain/errors/proposerPreferences.js.map +1 -0
  69. package/lib/chain/interface.d.ts +3 -2
  70. package/lib/chain/interface.d.ts.map +1 -1
  71. package/lib/chain/interface.js.map +1 -1
  72. package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
  73. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  74. package/lib/chain/opPools/payloadAttestationPool.js +26 -4
  75. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  76. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  77. package/lib/chain/prepareNextSlot.js +15 -17
  78. package/lib/chain/prepareNextSlot.js.map +1 -1
  79. package/lib/chain/produceBlock/produceBlockBody.d.ts +11 -3
  80. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  81. package/lib/chain/produceBlock/produceBlockBody.js +41 -18
  82. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  83. package/lib/chain/regen/interface.d.ts +1 -0
  84. package/lib/chain/regen/interface.d.ts.map +1 -1
  85. package/lib/chain/regen/interface.js +1 -0
  86. package/lib/chain/regen/interface.js.map +1 -1
  87. package/lib/chain/seenCache/index.d.ts +1 -0
  88. package/lib/chain/seenCache/index.d.ts.map +1 -1
  89. package/lib/chain/seenCache/index.js +1 -0
  90. package/lib/chain/seenCache/index.js.map +1 -1
  91. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +8 -2
  92. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  93. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -4
  94. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  95. package/lib/chain/seenCache/seenProposerPreferences.d.ts +15 -0
  96. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
  97. package/lib/chain/seenCache/seenProposerPreferences.js +25 -0
  98. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
  99. package/lib/chain/validation/block.d.ts.map +1 -1
  100. package/lib/chain/validation/block.js +1 -0
  101. package/lib/chain/validation/block.js.map +1 -1
  102. package/lib/chain/validation/proposerPreferences.d.ts +8 -0
  103. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
  104. package/lib/chain/validation/proposerPreferences.js +69 -0
  105. package/lib/chain/validation/proposerPreferences.js.map +1 -0
  106. package/lib/metrics/metrics/lodestar.d.ts +1 -0
  107. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  108. package/lib/metrics/metrics/lodestar.js +4 -0
  109. package/lib/metrics/metrics/lodestar.js.map +1 -1
  110. package/lib/network/gossip/interface.d.ts +7 -1
  111. package/lib/network/gossip/interface.d.ts.map +1 -1
  112. package/lib/network/gossip/interface.js +1 -0
  113. package/lib/network/gossip/interface.js.map +1 -1
  114. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  115. package/lib/network/gossip/scoringParameters.js +12 -1
  116. package/lib/network/gossip/scoringParameters.js.map +1 -1
  117. package/lib/network/gossip/topic.d.ts +8 -0
  118. package/lib/network/gossip/topic.d.ts.map +1 -1
  119. package/lib/network/gossip/topic.js +6 -0
  120. package/lib/network/gossip/topic.js.map +1 -1
  121. package/lib/network/interface.d.ts +1 -0
  122. package/lib/network/interface.d.ts.map +1 -1
  123. package/lib/network/network.d.ts +1 -0
  124. package/lib/network/network.d.ts.map +1 -1
  125. package/lib/network/network.js +5 -0
  126. package/lib/network/network.js.map +1 -1
  127. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  128. package/lib/network/processor/gossipHandlers.js +24 -16
  129. package/lib/network/processor/gossipHandlers.js.map +1 -1
  130. package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
  131. package/lib/network/processor/gossipQueues/index.js +5 -0
  132. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  133. package/lib/network/processor/index.d.ts.map +1 -1
  134. package/lib/network/processor/index.js +6 -5
  135. package/lib/network/processor/index.js.map +1 -1
  136. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  137. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
  138. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  139. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  140. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
  141. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  142. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  143. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
  144. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  145. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  146. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
  147. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  148. package/lib/node/notifier.js +7 -1
  149. package/lib/node/notifier.js.map +1 -1
  150. package/lib/sync/range/batch.d.ts +23 -2
  151. package/lib/sync/range/batch.d.ts.map +1 -1
  152. package/lib/sync/range/batch.js +84 -33
  153. package/lib/sync/range/batch.js.map +1 -1
  154. package/lib/sync/range/chain.d.ts +6 -2
  155. package/lib/sync/range/chain.d.ts.map +1 -1
  156. package/lib/sync/range/chain.js +26 -7
  157. package/lib/sync/range/chain.js.map +1 -1
  158. package/lib/sync/range/range.d.ts.map +1 -1
  159. package/lib/sync/range/range.js +17 -6
  160. package/lib/sync/range/range.js.map +1 -1
  161. package/lib/sync/types.d.ts +34 -0
  162. package/lib/sync/types.d.ts.map +1 -1
  163. package/lib/sync/types.js +34 -0
  164. package/lib/sync/types.js.map +1 -1
  165. package/lib/sync/unknownBlock.d.ts +22 -1
  166. package/lib/sync/unknownBlock.d.ts.map +1 -1
  167. package/lib/sync/unknownBlock.js +602 -53
  168. package/lib/sync/unknownBlock.js.map +1 -1
  169. package/lib/sync/utils/downloadByRange.d.ts +46 -10
  170. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  171. package/lib/sync/utils/downloadByRange.js +153 -24
  172. package/lib/sync/utils/downloadByRange.js.map +1 -1
  173. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  174. package/lib/sync/utils/downloadByRoot.js +16 -2
  175. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  176. package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
  177. package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
  178. package/lib/sync/utils/pendingBlocksTree.js +0 -9
  179. package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
  180. package/package.json +15 -15
  181. package/src/api/impl/beacon/blocks/index.ts +21 -5
  182. package/src/api/impl/beacon/pool/index.ts +83 -1
  183. package/src/api/impl/lodestar/index.ts +1 -1
  184. package/src/api/impl/validator/index.ts +80 -0
  185. package/src/chain/blocks/importBlock.ts +10 -35
  186. package/src/chain/blocks/importExecutionPayload.ts +57 -21
  187. package/src/chain/blocks/index.ts +54 -14
  188. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +6 -1
  189. package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
  190. package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
  191. package/src/chain/blocks/types.ts +2 -2
  192. package/src/chain/blocks/utils/chainSegment.ts +106 -17
  193. package/src/chain/blocks/verifyBlock.ts +68 -9
  194. package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -7
  195. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
  196. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  197. package/src/chain/chain.ts +16 -3
  198. package/src/chain/emitter.ts +0 -11
  199. package/src/chain/errors/blockError.ts +4 -1
  200. package/src/chain/errors/index.ts +1 -0
  201. package/src/chain/errors/proposerPreferences.ts +39 -0
  202. package/src/chain/interface.ts +7 -1
  203. package/src/chain/opPools/payloadAttestationPool.ts +29 -8
  204. package/src/chain/prepareNextSlot.ts +20 -28
  205. package/src/chain/produceBlock/produceBlockBody.ts +51 -23
  206. package/src/chain/regen/interface.ts +1 -0
  207. package/src/chain/seenCache/index.ts +1 -0
  208. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +25 -5
  209. package/src/chain/seenCache/seenProposerPreferences.ts +29 -0
  210. package/src/chain/validation/block.ts +1 -0
  211. package/src/chain/validation/proposerPreferences.ts +91 -0
  212. package/src/metrics/metrics/lodestar.ts +4 -0
  213. package/src/network/gossip/interface.ts +6 -0
  214. package/src/network/gossip/scoringParameters.ts +14 -1
  215. package/src/network/gossip/topic.ts +6 -0
  216. package/src/network/interface.ts +1 -0
  217. package/src/network/network.ts +11 -0
  218. package/src/network/processor/gossipHandlers.ts +35 -17
  219. package/src/network/processor/gossipQueues/index.ts +5 -0
  220. package/src/network/processor/index.ts +6 -5
  221. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
  222. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
  223. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
  224. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
  225. package/src/node/notifier.ts +8 -1
  226. package/src/sync/range/batch.ts +142 -38
  227. package/src/sync/range/chain.ts +37 -9
  228. package/src/sync/range/range.ts +18 -6
  229. package/src/sync/types.ts +72 -0
  230. package/src/sync/unknownBlock.ts +760 -57
  231. package/src/sync/utils/downloadByRange.ts +262 -39
  232. package/src/sync/utils/downloadByRoot.ts +24 -2
  233. package/src/sync/utils/pendingBlocksTree.ts +0 -15
@@ -1,10 +1,11 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ForkName, isForkPostDeneb, isForkPostFulu} from "@lodestar/params";
2
+ import {ForkName, isForkPostDeneb, isForkPostFulu, isForkPostGloas} from "@lodestar/params";
3
3
  import {Epoch, RootHex, Slot, phase0} from "@lodestar/types";
4
- import {LodestarError} from "@lodestar/utils";
4
+ import {LodestarError, prettyPrintIndices} from "@lodestar/utils";
5
5
  import {isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
6
6
  import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
7
7
  import {isDaOutOfRange} from "../../chain/blocks/blockInput/utils.js";
8
+ import {PayloadEnvelopeInput} from "../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
8
9
  import {BlockError, BlockErrorCode} from "../../chain/errors/index.js";
9
10
  import {PeerSyncMeta} from "../../network/peers/peersData.js";
10
11
  import {IClock} from "../../util/clock.js";
@@ -46,25 +47,68 @@ export type Attempt = {
46
47
  export type AwaitingDownloadState = {
47
48
  status: BatchStatus.AwaitingDownload;
48
49
  blocks: IBlockInput[];
50
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
49
51
  };
50
52
 
51
53
  export type DownloadSuccessState = {
52
54
  status: BatchStatus.AwaitingProcessing;
53
55
  blocks: IBlockInput[];
56
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
54
57
  };
55
58
 
56
59
  export type BatchState =
57
60
  | AwaitingDownloadState
58
- | {status: BatchStatus.Downloading; peer: PeerIdStr; blocks: IBlockInput[]}
61
+ | {
62
+ status: BatchStatus.Downloading;
63
+ peer: PeerIdStr;
64
+ blocks: IBlockInput[];
65
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
66
+ }
59
67
  | DownloadSuccessState
60
- | {status: BatchStatus.Processing; blocks: IBlockInput[]; attempt: Attempt}
61
- | {status: BatchStatus.AwaitingValidation; blocks: IBlockInput[]; attempt: Attempt};
68
+ | {
69
+ status: BatchStatus.Processing;
70
+ blocks: IBlockInput[];
71
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
72
+ attempt: Attempt;
73
+ }
74
+ | {
75
+ status: BatchStatus.AwaitingValidation;
76
+ blocks: IBlockInput[];
77
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
78
+ attempt: Attempt;
79
+ };
62
80
 
63
81
  export type BatchMetadata = {
82
+ // Batch-level slot window (always present)
64
83
  startEpoch: Epoch;
84
+ startSlot: Slot;
85
+ count: number;
65
86
  status: BatchStatus;
87
+
88
+ // Per-type outstanding request shapes; only present when that sub-request exists.
89
+ // Format: "startSlot=<n>,count=<n>" (plus ",cols=<indices>" for columns).
90
+ blocksReq?: string;
91
+ blobsReq?: string;
92
+ columnsReq?: string;
93
+ envelopesReq?: string;
94
+
95
+ // Retry counters
96
+ downloadAttempts: number;
97
+ processingAttempts: number;
98
+
99
+ // Cumulative peer attribution for failed attempts (only present when non-empty)
100
+ failedDownloadPeers?: string;
101
+ failedProcessingPeers?: string;
66
102
  };
67
103
 
104
+ function formatRangeReq(req: {startSlot: Slot; count: number}): string {
105
+ return `startSlot=${req.startSlot},count=${req.count}`;
106
+ }
107
+
108
+ function formatColumnsReq(req: {startSlot: Slot; count: number; columns: number[]}): string {
109
+ return `startSlot=${req.startSlot},count=${req.count},cols=${prettyPrintIndices(req.columns)}`;
110
+ }
111
+
68
112
  /**
69
113
  * Batches are downloaded at the first block of the epoch.
70
114
  *
@@ -85,7 +129,7 @@ export class Batch {
85
129
  /** Block, blob and column requests that are used to determine the best peer and are used in downloadByRange */
86
130
  requests: DownloadByRangeRequests;
87
131
  /** State of the batch. */
88
- state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: []};
132
+ state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
89
133
  /** Peers that provided good data */
90
134
  goodPeers: PeerIdStr[] = [];
91
135
  /** The `Attempts` that have been made and failed to send us this batch. */
@@ -129,35 +173,33 @@ export class Batch {
129
173
  count: this.count,
130
174
  step: 1,
131
175
  };
132
- if (isForkPostFulu(this.forkName) && withinValidRequestWindow) {
133
- return {
134
- blocksRequest,
135
- columnsRequest: {
136
- startSlot: this.startSlot,
137
- count: this.count,
138
- columns: this.custodyConfig.sampledColumns,
139
- },
140
- };
176
+ const requests: DownloadByRangeRequests = {blocksRequest};
177
+
178
+ // Post-Gloas envelopes are required for block processing, independent of DA retention window.
179
+ if (isForkPostGloas(this.forkName)) {
180
+ requests.envelopesRequest = {startSlot: this.startSlot, count: this.count};
141
181
  }
142
- if (isForkPostDeneb(this.forkName) && withinValidRequestWindow) {
143
- return {
144
- blocksRequest,
145
- blobsRequest: {
146
- startSlot: this.startSlot,
147
- count: this.count,
148
- },
182
+
183
+ if (isForkPostFulu(this.forkName) && withinValidRequestWindow) {
184
+ requests.columnsRequest = {
185
+ startSlot: this.startSlot,
186
+ count: this.count,
187
+ columns: this.custodyConfig.sampledColumns,
149
188
  };
189
+ } else if (isForkPostDeneb(this.forkName) && withinValidRequestWindow) {
190
+ requests.blobsRequest = {startSlot: this.startSlot, count: this.count};
150
191
  }
151
- return {
152
- blocksRequest,
153
- };
192
+
193
+ return requests;
154
194
  }
155
195
 
156
196
  // subsequent request where part of the epoch has already been downloaded. Need to figure out what is the beginning
157
197
  // of the range where download needs to resume
158
198
  let blockStartSlot = this.startSlot;
159
199
  let dataStartSlot = this.startSlot;
200
+ let envelopeStartSlot = this.startSlot;
160
201
  const neededColumns = new Set<number>();
202
+ const envelopesBySlot = this.state.payloadEnvelopes ?? new Map<Slot, PayloadEnvelopeInput>();
161
203
 
162
204
  // ensure blocks are in slot-wise order
163
205
  for (const blockInput of blocks) {
@@ -175,6 +217,13 @@ export class Batch {
175
217
  if (blockInput.hasBlock() && blockStartSlot === blockSlot) {
176
218
  blockStartSlot = blockSlot + 1;
177
219
  }
220
+ if (
221
+ blockInput.hasBlock() &&
222
+ envelopeStartSlot === blockSlot &&
223
+ envelopesBySlot.get(blockSlot)?.hasPayloadEnvelope()
224
+ ) {
225
+ envelopeStartSlot = blockSlot + 1;
226
+ }
178
227
  if (!blockInput.hasAllData()) {
179
228
  if (isBlockInputColumns(blockInput)) {
180
229
  for (const index of blockInput.getMissingSampledColumnMeta().missing) {
@@ -216,6 +265,13 @@ export class Batch {
216
265
  // dataSlot will still have a value but do not create a request for preDeneb forks
217
266
  }
218
267
 
268
+ if (isForkPostGloas(this.forkName) && envelopeStartSlot <= endSlot) {
269
+ requests.envelopesRequest = {
270
+ startSlot: envelopeStartSlot,
271
+ count: endSlot - envelopeStartSlot + 1,
272
+ };
273
+ }
274
+
219
275
  return requests;
220
276
  }
221
277
 
@@ -256,13 +312,36 @@ export class Batch {
256
312
  }
257
313
 
258
314
  getMetadata(): BatchMetadata {
259
- return {startEpoch: this.startEpoch, status: this.state.status};
315
+ const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
316
+ const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
317
+ return {
318
+ startEpoch: this.startEpoch,
319
+ startSlot: this.startSlot,
320
+ count: this.count,
321
+ status: this.state.status,
322
+ ...(blocksRequest && {blocksReq: formatRangeReq(blocksRequest)}),
323
+ ...(blobsRequest && {blobsReq: formatRangeReq(blobsRequest)}),
324
+ ...(columnsRequest && {columnsReq: formatColumnsReq(columnsRequest)}),
325
+ ...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
326
+ downloadAttempts: this.failedDownloadAttempts.length,
327
+ processingAttempts: this.failedProcessingAttempts.length,
328
+ ...(this.failedDownloadAttempts.length > 0 && {
329
+ failedDownloadPeers: this.failedDownloadAttempts.join(","),
330
+ }),
331
+ ...(failedProcessingPeerList.length > 0 && {
332
+ failedProcessingPeers: failedProcessingPeerList.join(","),
333
+ }),
334
+ };
260
335
  }
261
336
 
262
337
  getBlocks(): IBlockInput[] {
263
338
  return this.state.blocks;
264
339
  }
265
340
 
341
+ getPayloadEnvelopes(): Map<Slot, PayloadEnvelopeInput> | null {
342
+ return this.state.payloadEnvelopes;
343
+ }
344
+
266
345
  /**
267
346
  * AwaitingDownload -> Downloading
268
347
  */
@@ -271,13 +350,22 @@ export class Batch {
271
350
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingDownload));
272
351
  }
273
352
 
274
- this.state = {status: BatchStatus.Downloading, peer, blocks: this.state.blocks};
353
+ this.state = {
354
+ status: BatchStatus.Downloading,
355
+ peer,
356
+ blocks: this.state.blocks,
357
+ payloadEnvelopes: this.state.payloadEnvelopes,
358
+ };
275
359
  }
276
360
 
277
361
  /**
278
362
  * Downloading -> AwaitingProcessing
279
363
  */
280
- downloadingSuccess(peer: PeerIdStr, blocks: IBlockInput[]): DownloadSuccessState {
364
+ downloadingSuccess(
365
+ peer: PeerIdStr,
366
+ blocks: IBlockInput[],
367
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null
368
+ ): DownloadSuccessState {
281
369
  if (this.state.status !== BatchStatus.Downloading) {
282
370
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.Downloading));
283
371
  }
@@ -305,11 +393,13 @@ export class Batch {
305
393
  status: this.state.status,
306
394
  });
307
395
  }
396
+ const newPayloadEnvelopes = payloadEnvelopes ?? this.state.payloadEnvelopes;
397
+
308
398
  if (allComplete) {
309
- this.state = {status: BatchStatus.AwaitingProcessing, blocks};
399
+ this.state = {status: BatchStatus.AwaitingProcessing, blocks, payloadEnvelopes: newPayloadEnvelopes};
310
400
  } else {
311
401
  this.requests = this.getRequests(blocks);
312
- this.state = {status: BatchStatus.AwaitingDownload, blocks};
402
+ this.state = {status: BatchStatus.AwaitingDownload, blocks, payloadEnvelopes: newPayloadEnvelopes};
313
403
  }
314
404
 
315
405
  return this.state as DownloadSuccessState;
@@ -328,25 +418,34 @@ export class Batch {
328
418
  throw new BatchError(this.errorType({code: BatchErrorCode.MAX_DOWNLOAD_ATTEMPTS}));
329
419
  }
330
420
 
331
- this.state = {status: BatchStatus.AwaitingDownload, blocks: this.state.blocks};
421
+ this.state = {
422
+ status: BatchStatus.AwaitingDownload,
423
+ blocks: this.state.blocks,
424
+ payloadEnvelopes: this.state.payloadEnvelopes,
425
+ };
332
426
  }
333
427
 
334
428
  /**
335
429
  * AwaitingProcessing -> Processing
336
430
  */
337
- startProcessing(): IBlockInput[] {
431
+ startProcessing(): {
432
+ blocks: IBlockInput[];
433
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
434
+ peers: PeerIdStr[];
435
+ } {
338
436
  if (this.state.status !== BatchStatus.AwaitingProcessing) {
339
437
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingProcessing));
340
438
  }
341
439
 
342
440
  const blocks = this.state.blocks;
441
+ const payloadEnvelopes = this.state.payloadEnvelopes;
343
442
  const hash = hashBlocks(blocks, this.config); // tracks blocks to report peer on processing error
344
443
  // Reset goodPeers in case another download attempt needs to be made. When Attempt is successful or not the peers
345
444
  // that the data came from will be handled by the Attempt that goes for processing
346
445
  const peers = this.goodPeers;
347
446
  this.goodPeers = [];
348
- this.state = {status: BatchStatus.Processing, blocks, attempt: {peers, hash}};
349
- return blocks;
447
+ this.state = {status: BatchStatus.Processing, blocks, payloadEnvelopes, attempt: {peers, hash}};
448
+ return {blocks, payloadEnvelopes, peers};
350
449
  }
351
450
 
352
451
  /**
@@ -357,7 +456,12 @@ export class Batch {
357
456
  throw new BatchError(this.wrongStatusErrorType(BatchStatus.Processing));
358
457
  }
359
458
 
360
- this.state = {status: BatchStatus.AwaitingValidation, blocks: this.state.blocks, attempt: this.state.attempt};
459
+ this.state = {
460
+ status: BatchStatus.AwaitingValidation,
461
+ blocks: this.state.blocks,
462
+ payloadEnvelopes: this.state.payloadEnvelopes,
463
+ attempt: this.state.attempt,
464
+ };
361
465
  }
362
466
 
363
467
  /**
@@ -408,7 +512,7 @@ export class Batch {
408
512
 
409
513
  // remove any downloaded blocks and re-attempt
410
514
  // TODO(fulu): need to remove the bad blocks from the SeenBlockInputCache
411
- this.state = {status: BatchStatus.AwaitingDownload, blocks: []};
515
+ this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
412
516
  }
413
517
 
414
518
  private onProcessingError(attempt: Attempt): void {
@@ -419,12 +523,12 @@ export class Batch {
419
523
 
420
524
  // remove any downloaded blocks and re-attempt
421
525
  // TODO(fulu): need to remove the bad blocks from the SeenBlockInputCache
422
- this.state = {status: BatchStatus.AwaitingDownload, blocks: []};
526
+ this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
423
527
  }
424
528
 
425
529
  /** Helper to construct typed BatchError. Stack traces are correct as the error is thrown above */
426
530
  private errorType(type: BatchErrorType): BatchErrorType & BatchErrorMetadata {
427
- return {...type, ...this.getMetadata()};
531
+ return {...type, startEpoch: this.startEpoch, status: this.state.status};
428
532
  }
429
533
 
430
534
  private wrongStatusErrorType(expectedStatus: BatchStatus): BatchErrorType & BatchErrorMetadata {
@@ -1,9 +1,10 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Epoch, Root, Slot} from "@lodestar/types";
3
- import {ErrorAborted, LodestarError, Logger, toRootHex} from "@lodestar/utils";
3
+ import {ErrorAborted, LodestarError, Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
4
4
  import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
5
5
  import {BlockInputErrorCode} from "../../chain/blocks/blockInput/errors.js";
6
6
  import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
7
+ import {PayloadEnvelopeInput} from "../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
7
8
  import {BlobSidecarErrorCode} from "../../chain/errors/blobSidecarError.js";
8
9
  import {DataColumnSidecarErrorCode} from "../../chain/errors/dataColumnSidecarError.js";
9
10
  import {Metrics} from "../../metrics/metrics.js";
@@ -44,13 +45,19 @@ export type SyncChainFns = {
44
45
  * Must return if ALL blocks are processed successfully
45
46
  * If SOME blocks are processed must throw BlockProcessorError()
46
47
  */
47
- processChainSegment: (blocks: IBlockInput[], syncType: RangeSyncType) => Promise<void>;
48
+ processChainSegment: (
49
+ blocks: IBlockInput[],
50
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
51
+ syncType: RangeSyncType
52
+ ) => Promise<void>;
48
53
  /** Must download blocks, and validate their range */
49
54
  downloadByRange: (
50
55
  peer: PeerSyncMeta,
51
56
  batch: Batch,
52
57
  syncType: RangeSyncType
53
- ) => Promise<WarnResult<IBlockInput[], DownloadByRangeError>>;
58
+ ) => Promise<
59
+ WarnResult<{blocks: IBlockInput[]; payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null}, DownloadByRangeError>
60
+ >;
54
61
  /** Report peer for negative actions. Decouples from the full network instance */
55
62
  reportPeer: (peer: PeerIdStr, action: PeerAction, actionName: string) => void;
56
63
  /** Gets current peer custodyColumns and earliestAvailableSlot */
@@ -215,12 +222,14 @@ export class SyncChain {
215
222
  */
216
223
  stopSyncing(): void {
217
224
  this.status = SyncChainStatus.Stopped;
225
+ this.logger.debug("SyncChain stopSyncing", {id: this.logId});
218
226
  }
219
227
 
220
228
  /**
221
229
  * Permanently remove this chain. Throws the main AsyncIterable
222
230
  */
223
231
  remove(): void {
232
+ this.logger.debug("SyncChain remove", {id: this.logId});
224
233
  this.batchProcessor.end(new ErrorAborted("SyncChain"));
225
234
  }
226
235
 
@@ -516,7 +525,8 @@ export class SyncChain {
516
525
  });
517
526
  this.metrics?.syncRange.downloadByRange.success.inc();
518
527
  const {warnings, result} = res.result;
519
- const downloadSuccessOutput = batch.downloadingSuccess(peer.peerId, result);
528
+ const {blocks: downloadedBlocks, payloadEnvelopes} = result;
529
+ const downloadSuccessOutput = batch.downloadingSuccess(peer.peerId, downloadedBlocks, payloadEnvelopes);
520
530
  const logMeta: Record<string, number> = {
521
531
  blockCount: downloadSuccessOutput.blocks.length,
522
532
  };
@@ -526,7 +536,7 @@ export class SyncChain {
526
536
  this.metrics?.syncRange.downloadByRange.warn.inc({client: peer.client, code: warning.type.code});
527
537
  this.logger.debug(
528
538
  "Batch downloaded with warning",
529
- {id: this.logId, epoch: batch.startEpoch, ...logMeta, peer: prettyPrintPeerIdStr(peer.peerId)},
539
+ {id: this.logId, ...batch.getMetadata(), ...logMeta, peer: prettyPrintPeerIdStr(peer.peerId)},
530
540
  warning
531
541
  );
532
542
  }
@@ -552,10 +562,17 @@ export class SyncChain {
552
562
  // the flow will continue to call triggerBatchDownloader() below
553
563
  }
554
564
 
565
+ const blockSlots = downloadSuccessOutput.blocks.map((b) => b.slot);
566
+ const envelopeSlots = downloadSuccessOutput.payloadEnvelopes
567
+ ? Array.from(downloadSuccessOutput.payloadEnvelopes.keys())
568
+ : null;
569
+
555
570
  this.logger.debug(logMessage, {
556
571
  id: this.logId,
557
- epoch: batch.startEpoch,
572
+ ...batch.getMetadata(),
558
573
  ...logMeta,
574
+ blockSlots: prettyPrintIndices(blockSlots),
575
+ ...(envelopeSlots ? {envelopeSlots: prettyPrintIndices(envelopeSlots)} : {}),
559
576
  peer: prettyPrintPeerIdStr(peer.peerId),
560
577
  });
561
578
  }
@@ -578,13 +595,24 @@ export class SyncChain {
578
595
  * Sends `batch` to the processor. Note: batch may be empty
579
596
  */
580
597
  private async processBatch(batch: Batch): Promise<void> {
581
- const blocks = batch.startProcessing();
598
+ const {blocks, payloadEnvelopes, peers} = batch.startProcessing();
599
+
600
+ const logCtx = {
601
+ id: this.logId,
602
+ ...batch.getMetadata(),
603
+ blockCount: blocks.length,
604
+ blockSlots: prettyPrintIndices(blocks.map((b) => b.slot)),
605
+ ...(payloadEnvelopes ? {envelopeSlots: prettyPrintIndices(Array.from(payloadEnvelopes.keys()))} : {}),
606
+ peers: peers.map(prettyPrintPeerIdStr).join(","),
607
+ };
608
+ this.logger.verbose("Processing batch", logCtx);
582
609
 
583
610
  // wrapError ensures to never call both batch success() and batch error()
584
- const res = await wrapError(this.processChainSegment(blocks, this.syncType));
611
+ const res = await wrapError(this.processChainSegment(blocks, payloadEnvelopes, this.syncType));
585
612
 
586
613
  if (!res.err) {
587
614
  batch.processingSuccess();
615
+ this.logger.verbose("Processed batch", {...logCtx, ...batch.getMetadata()});
588
616
 
589
617
  // If the processed batch is not empty, validate previous AwaitingValidation blocks.
590
618
  if (blocks.length > 0) {
@@ -594,7 +622,7 @@ export class SyncChain {
594
622
  // Potentially process next AwaitingProcessing batch
595
623
  this.triggerBatchProcessor();
596
624
  } else {
597
- this.logger.verbose("Batch process error", {id: this.logId, ...batch.getMetadata()}, res.err);
625
+ this.logger.verbose("Batch process error", logCtx, res.err);
598
626
  batch.processingError(res.err); // Throws after MAX_BATCH_PROCESSING_ATTEMPTS
599
627
 
600
628
  // At least one block was successfully verified and imported, so we can be sure all
@@ -172,7 +172,7 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
172
172
  }
173
173
 
174
174
  /** Convenience method for `SyncChain` */
175
- private processChainSegment: SyncChainFns["processChainSegment"] = async (blocks, syncType) => {
175
+ private processChainSegment: SyncChainFns["processChainSegment"] = async (blocks, payloadEnvelopes, syncType) => {
176
176
  // Not trusted, verify signatures
177
177
  const flags: ImportBlockOpts = {
178
178
  // Only skip importing attestations for finalized sync. For head sync attestation are valuable.
@@ -192,9 +192,15 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
192
192
 
193
193
  if (this.opts?.disableProcessAsChainSegment) {
194
194
  // Should only be used for debugging or testing
195
- for (const block of blocks) await this.chain.processBlock(block, flags);
195
+ for (const block of blocks) {
196
+ await this.chain.processBlock(block, flags);
197
+ const payloadEnvelope = payloadEnvelopes?.get(block.slot);
198
+ if (payloadEnvelope) {
199
+ await this.chain.processExecutionPayload(payloadEnvelope);
200
+ }
201
+ }
196
202
  } else {
197
- await this.chain.processChainSegment(blocks, flags);
203
+ await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
198
204
  }
199
205
  };
200
206
 
@@ -209,13 +215,19 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
209
215
  peerDasMetrics: this.chain.metrics?.peerDas,
210
216
  ...batch.getRequestsForPeer(peer),
211
217
  });
212
- const cached = cacheByRangeResponses({
218
+ const {responses, payloadEnvelopes: downloadedPayloadEnvelopes} = result;
219
+ const {blocks, payloadEnvelopes} = cacheByRangeResponses({
213
220
  cache: this.chain.seenBlockInputCache,
221
+ seenPayloadEnvelopeInputCache: this.chain.seenPayloadEnvelopeInputCache,
214
222
  peerIdStr: peer.peerId,
215
- responses: result,
223
+ responses,
216
224
  batchBlocks,
225
+ downloadedPayloadEnvelopes,
226
+ existingPayloadEnvelopes: batch.getPayloadEnvelopes(),
227
+ custodyConfig: this.chain.custodyConfig,
228
+ seenTimestampSec: Date.now() / 1000,
217
229
  });
218
- return {result: cached, warnings};
230
+ return {result: {blocks, payloadEnvelopes}, warnings};
219
231
  };
220
232
 
221
233
  private pruneBlockInputs: SyncChainFns["pruneBlockInputs"] = (blocks: IBlockInput[]) => {
package/src/sync/types.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import {RootHex, Slot} from "@lodestar/types";
2
+ import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
3
+ import {toRootHex} from "@lodestar/utils";
2
4
  import {IBlockInput} from "../chain/blocks/blockInput/index.js";
5
+ import {PayloadEnvelopeInput} from "../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
3
6
 
4
7
  export enum PendingBlockType {
5
8
  /**
@@ -26,6 +29,14 @@ export enum PendingBlockInputStatus {
26
29
  processing = "processing",
27
30
  }
28
31
 
32
+ export enum PendingPayloadInputStatus {
33
+ pending = "pending",
34
+ fetching = "fetching",
35
+ waitingForBlock = "waiting_for_block",
36
+ downloaded = "downloaded",
37
+ processing = "processing",
38
+ }
39
+
29
40
  export type PendingBlockInput = {
30
41
  status: PendingBlockInputStatus;
31
42
  blockInput: IBlockInput;
@@ -44,10 +55,47 @@ export type PendingRootHex = {
44
55
 
45
56
  export type BlockInputSyncCacheItem = PendingBlockInput | PendingRootHex;
46
57
 
58
+ export type PendingPayloadInput = {
59
+ status:
60
+ | PendingPayloadInputStatus.pending
61
+ | PendingPayloadInputStatus.fetching
62
+ | PendingPayloadInputStatus.downloaded
63
+ | PendingPayloadInputStatus.processing;
64
+ payloadInput: PayloadEnvelopeInput;
65
+ timeAddedSec: number;
66
+ timeSyncedSec?: number;
67
+ peerIdStrings: Set<string>;
68
+ };
69
+
70
+ export type PendingPayloadRootHex = {
71
+ status: PendingPayloadInputStatus.pending | PendingPayloadInputStatus.fetching;
72
+ rootHex: RootHex;
73
+ timeAddedSec: number;
74
+ timeSyncedSec?: number;
75
+ peerIdStrings: Set<string>;
76
+ };
77
+
78
+ export type PendingPayloadEnvelope = {
79
+ status: PendingPayloadInputStatus.waitingForBlock;
80
+ envelope: SignedExecutionPayloadEnvelope;
81
+ timeAddedSec: number;
82
+ peerIdStrings: Set<string>;
83
+ };
84
+
85
+ export type PayloadSyncCacheItem = PendingPayloadInput | PendingPayloadRootHex | PendingPayloadEnvelope;
86
+
47
87
  export function isPendingBlockInput(pending: BlockInputSyncCacheItem): pending is PendingBlockInput {
48
88
  return "blockInput" in pending;
49
89
  }
50
90
 
91
+ export function isPendingPayloadInput(pending: PayloadSyncCacheItem): pending is PendingPayloadInput {
92
+ return "payloadInput" in pending;
93
+ }
94
+
95
+ export function isPendingPayloadEnvelope(pending: PayloadSyncCacheItem): pending is PendingPayloadEnvelope {
96
+ return "envelope" in pending;
97
+ }
98
+
51
99
  export function getBlockInputSyncCacheItemRootHex(block: BlockInputSyncCacheItem): RootHex {
52
100
  return isPendingBlockInput(block) ? block.blockInput.blockRootHex : block.rootHex;
53
101
  }
@@ -55,3 +103,27 @@ export function getBlockInputSyncCacheItemRootHex(block: BlockInputSyncCacheItem
55
103
  export function getBlockInputSyncCacheItemSlot(block: BlockInputSyncCacheItem): Slot | string {
56
104
  return isPendingBlockInput(block) ? block.blockInput.slot : "unknown";
57
105
  }
106
+
107
+ export function getPayloadSyncCacheItemRootHex(payload: PayloadSyncCacheItem): RootHex {
108
+ if (isPendingPayloadInput(payload)) {
109
+ return payload.payloadInput.blockRootHex;
110
+ }
111
+
112
+ if (isPendingPayloadEnvelope(payload)) {
113
+ return toRootHex(payload.envelope.message.beaconBlockRoot);
114
+ }
115
+
116
+ return payload.rootHex;
117
+ }
118
+
119
+ export function getPayloadSyncCacheItemSlot(payload: PayloadSyncCacheItem): Slot | string {
120
+ if (isPendingPayloadInput(payload)) {
121
+ return payload.payloadInput.slot;
122
+ }
123
+
124
+ if (isPendingPayloadEnvelope(payload)) {
125
+ return payload.envelope.message.payload.slotNumber;
126
+ }
127
+
128
+ return "unknown";
129
+ }