@lodestar/beacon-node 1.47.0-dev.f591cb177e → 1.47.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 (316) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +40 -12
  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 +21 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/utils.d.ts +2 -1
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/events/index.d.ts.map +1 -1
  11. package/lib/api/impl/events/index.js +2 -0
  12. package/lib/api/impl/events/index.js.map +1 -1
  13. package/lib/api/impl/lodestar/index.js +1 -1
  14. package/lib/api/impl/lodestar/index.js.map +1 -1
  15. package/lib/api/impl/validator/index.d.ts.map +1 -1
  16. package/lib/api/impl/validator/index.js +232 -49
  17. package/lib/api/impl/validator/index.js.map +1 -1
  18. package/lib/api/impl/validator/utils.js +1 -1
  19. package/lib/api/impl/validator/utils.js.map +1 -1
  20. package/lib/api/rest/base.d.ts.map +1 -1
  21. package/lib/api/rest/base.js +7 -0
  22. package/lib/api/rest/base.js.map +1 -1
  23. package/lib/chain/archiveStore/archiveStore.js +1 -1
  24. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  25. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  26. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  27. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  28. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  29. package/lib/chain/blocks/index.d.ts.map +1 -1
  30. package/lib/chain/blocks/index.js +14 -6
  31. package/lib/chain/blocks/index.js.map +1 -1
  32. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  33. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  34. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  35. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  37. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  39. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  40. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  41. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
  42. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  43. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  44. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  45. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  46. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  47. package/lib/chain/bls/index.d.ts +1 -1
  48. package/lib/chain/bls/index.d.ts.map +1 -1
  49. package/lib/chain/bls/interface.d.ts +5 -5
  50. package/lib/chain/bls/interface.d.ts.map +1 -1
  51. package/lib/chain/bls/multithread/index.d.ts +5 -11
  52. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  53. package/lib/chain/bls/multithread/index.js +116 -101
  54. package/lib/chain/bls/multithread/index.js.map +1 -1
  55. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  56. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  57. package/lib/chain/bls/multithread/jobItem.js +10 -65
  58. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  59. package/lib/chain/bls/multithread/types.d.ts +29 -8
  60. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  61. package/lib/chain/bls/multithread/types.js +12 -0
  62. package/lib/chain/bls/multithread/types.js.map +1 -1
  63. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  64. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  65. package/lib/chain/bls/multithread/utils.js +10 -0
  66. package/lib/chain/bls/multithread/utils.js.map +1 -1
  67. package/lib/chain/bls/multithread/worker.js +49 -23
  68. package/lib/chain/bls/multithread/worker.js.map +1 -1
  69. package/lib/chain/bls/singleThread.d.ts +4 -10
  70. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  71. package/lib/chain/bls/singleThread.js +8 -45
  72. package/lib/chain/bls/singleThread.js.map +1 -1
  73. package/lib/chain/bls/utils.d.ts +4 -4
  74. package/lib/chain/bls/utils.d.ts.map +1 -1
  75. package/lib/chain/bls/utils.js +18 -19
  76. package/lib/chain/bls/utils.js.map +1 -1
  77. package/lib/chain/chain.d.ts +8 -3
  78. package/lib/chain/chain.d.ts.map +1 -1
  79. package/lib/chain/chain.js +20 -7
  80. package/lib/chain/chain.js.map +1 -1
  81. package/lib/chain/errors/blockError.d.ts +1 -0
  82. package/lib/chain/errors/blockError.d.ts.map +1 -1
  83. package/lib/chain/errors/blockError.js +6 -1
  84. package/lib/chain/errors/blockError.js.map +1 -1
  85. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  86. package/lib/chain/forkChoice/index.js +4 -0
  87. package/lib/chain/forkChoice/index.js.map +1 -1
  88. package/lib/chain/interface.d.ts +6 -2
  89. package/lib/chain/interface.d.ts.map +1 -1
  90. package/lib/chain/interface.js.map +1 -1
  91. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  92. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  93. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  94. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  95. package/lib/chain/opPools/attestationPool.js +1 -1
  96. package/lib/chain/opPools/attestationPool.js.map +1 -1
  97. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
  98. package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
  99. package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
  100. package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
  101. package/lib/chain/opPools/executionPayloadBidPool.d.ts +7 -2
  102. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  103. package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
  104. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  105. package/lib/chain/opPools/index.d.ts +2 -1
  106. package/lib/chain/opPools/index.d.ts.map +1 -1
  107. package/lib/chain/opPools/index.js +1 -0
  108. package/lib/chain/opPools/index.js.map +1 -1
  109. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  110. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  111. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  112. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  113. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  114. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  115. package/lib/chain/opPools/utils.d.ts +1 -1
  116. package/lib/chain/opPools/utils.d.ts.map +1 -1
  117. package/lib/chain/opPools/utils.js +1 -1
  118. package/lib/chain/opPools/utils.js.map +1 -1
  119. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  120. package/lib/chain/prepareNextSlot.js +23 -5
  121. package/lib/chain/prepareNextSlot.js.map +1 -1
  122. package/lib/chain/produceBlock/produceBlockBody.js +1 -1
  123. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  124. package/lib/chain/regen/interface.d.ts +4 -2
  125. package/lib/chain/regen/interface.d.ts.map +1 -1
  126. package/lib/chain/regen/interface.js +2 -0
  127. package/lib/chain/regen/interface.js.map +1 -1
  128. package/lib/chain/regen/queued.d.ts +2 -2
  129. package/lib/chain/regen/queued.d.ts.map +1 -1
  130. package/lib/chain/regen/queued.js +5 -5
  131. package/lib/chain/regen/queued.js.map +1 -1
  132. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  133. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  134. package/lib/chain/seenCache/seenAttesters.js +25 -4
  135. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  136. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  137. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  138. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  139. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  140. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
  141. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  142. package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
  143. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  144. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
  145. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  146. package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
  147. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  148. package/lib/chain/stateCache/types.d.ts +3 -3
  149. package/lib/chain/stateCache/types.d.ts.map +1 -1
  150. package/lib/chain/validation/attestation.d.ts.map +1 -1
  151. package/lib/chain/validation/attestation.js +1 -4
  152. package/lib/chain/validation/attestation.js.map +1 -1
  153. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  154. package/lib/chain/validation/attesterSlashing.js +1 -1
  155. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  156. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  157. package/lib/chain/validation/executionPayloadBid.js +3 -4
  158. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  159. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  160. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  161. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  162. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  163. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  164. package/lib/chain/validation/proposerPreferences.js +2 -2
  165. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  166. package/lib/chain/validation/proposerSlashing.js +1 -1
  167. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  168. package/lib/chain/validation/voluntaryExit.d.ts +9 -1
  169. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  170. package/lib/chain/validation/voluntaryExit.js +46 -1
  171. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  172. package/lib/execution/builder/apiClient.d.ts +64 -0
  173. package/lib/execution/builder/apiClient.d.ts.map +1 -0
  174. package/lib/execution/builder/apiClient.js +184 -0
  175. package/lib/execution/builder/apiClient.js.map +1 -0
  176. package/lib/execution/builder/validateBid.d.ts +22 -0
  177. package/lib/execution/builder/validateBid.d.ts.map +1 -0
  178. package/lib/execution/builder/validateBid.js +111 -0
  179. package/lib/execution/builder/validateBid.js.map +1 -0
  180. package/lib/metrics/metrics/lodestar.d.ts +74 -22
  181. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  182. package/lib/metrics/metrics/lodestar.js +142 -52
  183. package/lib/metrics/metrics/lodestar.js.map +1 -1
  184. package/lib/network/gossip/topic.d.ts +13 -0
  185. package/lib/network/gossip/topic.d.ts.map +1 -1
  186. package/lib/network/gossip/topic.js +37 -0
  187. package/lib/network/gossip/topic.js.map +1 -1
  188. package/lib/network/network.d.ts.map +1 -1
  189. package/lib/network/network.js +22 -43
  190. package/lib/network/network.js.map +1 -1
  191. package/lib/network/processor/constants.d.ts +7 -0
  192. package/lib/network/processor/constants.d.ts.map +1 -0
  193. package/lib/network/processor/constants.js +7 -0
  194. package/lib/network/processor/constants.js.map +1 -0
  195. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  196. package/lib/network/processor/gossipHandlers.js +6 -2
  197. package/lib/network/processor/gossipHandlers.js.map +1 -1
  198. package/lib/network/processor/index.d.ts +3 -0
  199. package/lib/network/processor/index.d.ts.map +1 -1
  200. package/lib/network/processor/index.js +36 -15
  201. package/lib/network/processor/index.js.map +1 -1
  202. package/lib/network/reqresp/score.d.ts.map +1 -1
  203. package/lib/network/reqresp/score.js +5 -11
  204. package/lib/network/reqresp/score.js.map +1 -1
  205. package/lib/network/util.d.ts +0 -1
  206. package/lib/network/util.d.ts.map +1 -1
  207. package/lib/network/util.js +0 -4
  208. package/lib/network/util.js.map +1 -1
  209. package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
  210. package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
  211. package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
  212. package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
  213. package/lib/node/nodejs.d.ts +2 -1
  214. package/lib/node/nodejs.d.ts.map +1 -1
  215. package/lib/node/nodejs.js +7 -0
  216. package/lib/node/nodejs.js.map +1 -1
  217. package/lib/sync/range/batch.d.ts +23 -5
  218. package/lib/sync/range/batch.d.ts.map +1 -1
  219. package/lib/sync/range/batch.js +61 -22
  220. package/lib/sync/range/batch.js.map +1 -1
  221. package/lib/sync/range/chain.d.ts +2 -1
  222. package/lib/sync/range/chain.d.ts.map +1 -1
  223. package/lib/sync/range/chain.js +23 -10
  224. package/lib/sync/range/chain.js.map +1 -1
  225. package/lib/sync/types.d.ts +32 -0
  226. package/lib/sync/types.d.ts.map +1 -1
  227. package/lib/sync/types.js +33 -0
  228. package/lib/sync/types.js.map +1 -1
  229. package/lib/sync/unknownBlock.d.ts +6 -2
  230. package/lib/sync/unknownBlock.d.ts.map +1 -1
  231. package/lib/sync/unknownBlock.js +143 -68
  232. package/lib/sync/unknownBlock.js.map +1 -1
  233. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  234. package/lib/sync/utils/downloadByRange.js +16 -11
  235. package/lib/sync/utils/downloadByRange.js.map +1 -1
  236. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  237. package/lib/sync/utils/downloadByRoot.js +3 -1
  238. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  239. package/package.json +35 -35
  240. package/src/api/impl/beacon/blocks/index.ts +47 -15
  241. package/src/api/impl/beacon/pool/index.ts +22 -2
  242. package/src/api/impl/beacon/state/utils.ts +2 -1
  243. package/src/api/impl/events/index.ts +3 -1
  244. package/src/api/impl/lodestar/index.ts +1 -1
  245. package/src/api/impl/validator/index.ts +285 -49
  246. package/src/api/impl/validator/utils.ts +1 -1
  247. package/src/api/rest/base.ts +7 -0
  248. package/src/chain/archiveStore/archiveStore.ts +1 -1
  249. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  250. package/src/chain/blocks/index.ts +18 -6
  251. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  252. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  253. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  254. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  255. package/src/chain/bls/index.ts +1 -1
  256. package/src/chain/bls/interface.ts +7 -3
  257. package/src/chain/bls/multithread/index.ts +148 -120
  258. package/src/chain/bls/multithread/jobItem.ts +12 -82
  259. package/src/chain/bls/multithread/types.ts +33 -8
  260. package/src/chain/bls/multithread/utils.ts +11 -0
  261. package/src/chain/bls/multithread/worker.ts +85 -27
  262. package/src/chain/bls/singleThread.ts +11 -55
  263. package/src/chain/bls/utils.ts +23 -24
  264. package/src/chain/chain.ts +23 -6
  265. package/src/chain/errors/blockError.ts +8 -2
  266. package/src/chain/forkChoice/index.ts +4 -0
  267. package/src/chain/interface.ts +6 -1
  268. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  269. package/src/chain/opPools/attestationPool.ts +1 -1
  270. package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
  271. package/src/chain/opPools/executionPayloadBidPool.ts +13 -7
  272. package/src/chain/opPools/index.ts +2 -1
  273. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  274. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  275. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  276. package/src/chain/opPools/utils.ts +1 -1
  277. package/src/chain/prepareNextSlot.ts +23 -5
  278. package/src/chain/produceBlock/produceBlockBody.ts +1 -1
  279. package/src/chain/regen/interface.ts +4 -2
  280. package/src/chain/regen/queued.ts +5 -5
  281. package/src/chain/seenCache/seenAttesters.ts +31 -5
  282. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  283. package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
  284. package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
  285. package/src/chain/stateCache/types.ts +3 -3
  286. package/src/chain/validation/attestation.ts +1 -7
  287. package/src/chain/validation/attesterSlashing.ts +1 -8
  288. package/src/chain/validation/executionPayloadBid.ts +3 -4
  289. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  290. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  291. package/src/chain/validation/proposerPreferences.ts +3 -3
  292. package/src/chain/validation/proposerSlashing.ts +1 -1
  293. package/src/chain/validation/voluntaryExit.ts +59 -2
  294. package/src/execution/builder/apiClient.ts +244 -0
  295. package/src/execution/builder/validateBid.ts +166 -0
  296. package/src/metrics/metrics/lodestar.ts +155 -57
  297. package/src/network/gossip/topic.ts +38 -0
  298. package/src/network/network.ts +27 -55
  299. package/src/network/processor/constants.ts +6 -0
  300. package/src/network/processor/gossipHandlers.ts +6 -2
  301. package/src/network/processor/index.ts +42 -19
  302. package/src/network/reqresp/score.ts +5 -11
  303. package/src/network/util.ts +0 -5
  304. package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
  305. package/src/node/nodejs.ts +10 -1
  306. package/src/sync/range/batch.ts +83 -28
  307. package/src/sync/range/chain.ts +26 -11
  308. package/src/sync/types.ts +33 -0
  309. package/src/sync/unknownBlock.ts +167 -73
  310. package/src/sync/utils/downloadByRange.ts +21 -12
  311. package/src/sync/utils/downloadByRoot.ts +3 -1
  312. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  313. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  314. package/lib/chain/bls/maybeBatch.js +0 -35
  315. package/lib/chain/bls/maybeBatch.js.map +0 -1
  316. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -52,12 +52,16 @@ import {
52
52
  import {
53
53
  GWEI_TO_WEI,
54
54
  TimeoutError,
55
+ byteArrayEquals,
55
56
  defer,
56
57
  formatWeiToEth,
57
58
  fromHex,
59
+ prettyGweiToEth,
58
60
  prettyWeiToEth,
59
61
  resolveOrRacePromises,
62
+ sleep,
60
63
  toHex,
64
+ toPrintableUrl,
61
65
  toRootHex,
62
66
  } from "@lodestar/utils";
63
67
  import {MAX_BUILDER_BOOST_FACTOR} from "@lodestar/validator";
@@ -72,6 +76,7 @@ import {
72
76
  SyncCommitteeErrorCode,
73
77
  } from "../../../chain/errors/index.js";
74
78
  import {ChainEvent, CommonBlockBody} from "../../../chain/index.js";
79
+ import {PooledExecutionPayloadBid} from "../../../chain/opPools/index.js";
75
80
  import {PREPARE_NEXT_SLOT_BPS} from "../../../chain/prepareNextSlot.js";
76
81
  import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/produceBlock/index.js";
77
82
  import {RegenCaller} from "../../../chain/regen/index.js";
@@ -80,7 +85,9 @@ import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
80
85
  import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
81
86
  import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
82
87
  import {ZERO_HASH} from "../../../constants/index.js";
88
+ import {BUILDER_BID_DEADLINE_MS, BuilderApiBid, decodeBuilderUrl} from "../../../execution/builder/apiClient.js";
83
89
  import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
90
+ import {getBuilderBidTotalGwei, validateBuilderApiExecutionPayloadBid} from "../../../execution/builder/validateBid.js";
84
91
  import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
85
92
  import {CommitteeSubscription} from "../../../network/subnets/index.js";
86
93
  import {callInNextEventLoop} from "../../../util/eventLoop.js";
@@ -112,6 +119,55 @@ import {
112
119
  const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
113
120
  /** Overall timeout for execution and block production apis */
114
121
  const BLOCK_PRODUCTION_RACE_TIMEOUT_MS = 12_000;
122
+ /** Rejection message of the bid block branch when there is no viable bid to commit to */
123
+ const NO_BID_AVAILABLE = "No builder bid available";
124
+
125
+ type BidCandidate = {
126
+ signedBid: gloas.SignedExecutionPayloadBid;
127
+ /** Total payment in Gwei, counting the execution payment at most at the entry's cap */
128
+ totalGwei: bigint;
129
+ boostFactor: bigint;
130
+ url?: string;
131
+ /** Time in milliseconds from the slot start when the bid was received */
132
+ receivedMs: number;
133
+ };
134
+
135
+ /**
136
+ * Return the best bid by boosted total payment. A candidate with the max boost factor is
137
+ * preferred over any other regardless of value, ties prefer a builder api bid over the
138
+ * p2p bid, and the earlier received bid between builder api bids.
139
+ */
140
+ function selectBestBid(candidates: BidCandidate[]): BidCandidate | null {
141
+ const boostedValue = ({totalGwei, boostFactor}: BidCandidate): bigint => boostFactor * totalGwei;
142
+ let best: BidCandidate | null = null;
143
+ for (const candidate of candidates) {
144
+ if (best === null) {
145
+ best = candidate;
146
+ continue;
147
+ }
148
+ // Preserve max boost preference before comparing bid values
149
+ const candidateIsMaxBoost = candidate.boostFactor === MAX_BUILDER_BOOST_FACTOR;
150
+ const bestIsMaxBoost = best.boostFactor === MAX_BUILDER_BOOST_FACTOR;
151
+ if (candidateIsMaxBoost !== bestIsMaxBoost) {
152
+ if (candidateIsMaxBoost) {
153
+ best = candidate;
154
+ }
155
+ continue;
156
+ }
157
+ const candidateValue = boostedValue(candidate);
158
+ const bestValue = boostedValue(best);
159
+ if (candidateValue > bestValue) {
160
+ best = candidate;
161
+ } else if (
162
+ candidateValue === bestValue &&
163
+ // A tie prefers a builder api bid over the p2p bid, and the earlier received bid otherwise
164
+ (best.url === undefined || (candidate.url !== undefined && candidate.receivedMs < best.receivedMs))
165
+ ) {
166
+ best = candidate;
167
+ }
168
+ }
169
+ return best;
170
+ }
115
171
 
116
172
  type ProduceBlockContentsRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & {
117
173
  data: BlockContents;
@@ -857,7 +913,7 @@ export function getValidatorApi(
857
913
  feeRecipient,
858
914
  strictFeeRecipientCheck,
859
915
  includePayload,
860
- builderBoostFactor,
916
+ builderConfig,
861
917
  }) {
862
918
  const fork = config.getForkName(slot);
863
919
 
@@ -865,7 +921,7 @@ export function getValidatorApi(
865
921
  throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
866
922
  }
867
923
 
868
- builderBoostFactor = builderBoostFactor ?? BigInt(100);
924
+ const builderBoostFactor = builderConfig.builderBoostFactor;
869
925
  if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
870
926
  throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
871
927
  }
@@ -892,32 +948,117 @@ export function getValidatorApi(
892
948
  })
893
949
  );
894
950
 
895
- // TODO GLOAS: add external builder api support when it is implemented
896
951
  const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
897
952
  const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
953
+ // Post-gloas every proposal parent has an execution payload hash
954
+ if (bidParentBlockHash === null) {
955
+ throw new ApiError(500, `Unknown parent block hash for proposal parent ${parentBlockRootHex}`);
956
+ }
898
957
  const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot, parentBlock);
899
- // Keep a builder bid as fallback unless the circuit breaker is active
900
- const builderBid = circuitBreakerActive
901
- ? null
902
- : chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
903
958
 
904
- const logCtx = {
905
- slot,
906
- parentSlot,
907
- parentBlockRoot: parentBlockRootHex,
908
- parentBlockHash: parentBlock.executionPayloadBlockHash,
909
- fork,
910
- builderBoostFactor,
911
- strictFeeRecipientCheck,
912
- circuitBreakerActive,
913
- ...(builderBid !== null
914
- ? {
915
- bidValue: builderBid.message.value,
916
- builderIndex: builderBid.message.builderIndex,
917
- bidBlockHash: toRootHex(builderBid.message.blockHash),
959
+ // Fire builder API bid requests while the local payload is built, one request per entry.
960
+ // Any entry failure yields no bid and never fails block production.
961
+ let builderApiBidsPromise: Promise<BuilderApiBid[]> = Promise.resolve([]);
962
+ if (builderConfig.builders.length > 0 && !circuitBreakerActive) {
963
+ try {
964
+ const proposerIndex = chain.getHeadState().getBeaconProposer(slot);
965
+ const proposerPubkey = chain.pubkeyCache.getOrThrow(proposerIndex).toBytes();
966
+ builderApiBidsPromise = chain.builderApiClient.getExecutionPayloadBids(
967
+ builderConfig.builders,
968
+ slot,
969
+ fromHex(bidParentBlockHash),
970
+ parentBlockRoot,
971
+ proposerPubkey
972
+ );
973
+ } catch (e) {
974
+ logger.warn("Unable to request builder API bids", {slot}, e as Error);
975
+ }
976
+ }
977
+
978
+ // A builder bid is expected if builders are configured or a p2p bid was already received.
979
+ // Used by the censorship override which may run before the bid deadline
980
+ const builderBidExpected =
981
+ builderConfig.builders.length > 0 ||
982
+ (!circuitBreakerActive &&
983
+ chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex) !== null);
984
+
985
+ // Select the p2p bid once builders had time to bid up, matching the deadline advertised
986
+ // on builder API bid requests, unless the circuit breaker is active
987
+ const p2pBidPromise: Promise<PooledExecutionPayloadBid | null> = circuitBreakerActive
988
+ ? Promise.resolve(null)
989
+ : sleep(Math.max(0, BUILDER_BID_DEADLINE_MS - chain.clock.msFromSlot(slot))).then(() => {
990
+ const p2pBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
991
+ // Discard p2p bids below the proposer's configured floor on the total payment.
992
+ // A p2p bid's total is just its value since gossip validation enforces executionPayment=0.
993
+ if (p2pBid !== null && BigInt(p2pBid.signedBid.message.value) < builderConfig.minBid) {
994
+ logger.info("Best p2p bid below configured minimum", {
995
+ slot,
996
+ bidValue: prettyGweiToEth(p2pBid.signedBid.message.value),
997
+ minBid: prettyGweiToEth(builderConfig.minBid),
998
+ });
999
+ return null;
918
1000
  }
919
- : {}),
920
- };
1001
+ return p2pBid;
1002
+ });
1003
+
1004
+ // Candidates are ranked by their boosted counted total payment, the p2p bid is governed
1005
+ // by the top-level factors and each builder API bid by its own entry. Ties prefer the
1006
+ // earliest received builder API bid, so the signed block can be routed back to its
1007
+ // builder directly.
1008
+ const bestBidPromise: Promise<BidCandidate | null> = (async () => {
1009
+ const [builderApiBids, p2pBid] = await Promise.all([builderApiBidsPromise, p2pBidPromise]);
1010
+
1011
+ const candidates = (
1012
+ await Promise.all(
1013
+ builderApiBids.map(async ({url, entry, signedBid, receivedMs}): Promise<BidCandidate | null> => {
1014
+ try {
1015
+ await validateBuilderApiExecutionPayloadBid(chain, signedBid, {
1016
+ slot,
1017
+ parentBlock,
1018
+ parentBlockHash: bidParentBlockHash,
1019
+ parentBlockRoot: parentBlockRootHex,
1020
+ entry,
1021
+ });
1022
+ return {
1023
+ signedBid,
1024
+ totalGwei: getBuilderBidTotalGwei(signedBid.message, entry.maxExecutionPayment),
1025
+ boostFactor: entry.builderBoostFactor,
1026
+ url,
1027
+ receivedMs,
1028
+ };
1029
+ } catch (e) {
1030
+ metrics?.builderApi.bidsDiscarded.inc();
1031
+ logger.warn("Ignoring invalid builder API bid", {slot, builder: toPrintableUrl(url)}, e as Error);
1032
+ return null;
1033
+ }
1034
+ })
1035
+ )
1036
+ ).filter((candidate): candidate is BidCandidate => candidate !== null);
1037
+
1038
+ if (p2pBid !== null) {
1039
+ candidates.push({
1040
+ signedBid: p2pBid.signedBid,
1041
+ totalGwei: BigInt(p2pBid.signedBid.message.value),
1042
+ boostFactor: builderConfig.builderBoostFactor,
1043
+ receivedMs: p2pBid.receivedMs,
1044
+ });
1045
+ }
1046
+
1047
+ const best = selectBestBid(candidates);
1048
+ if (candidates.length > 0) {
1049
+ logger.debug("Ranked builder bid candidates", {
1050
+ slot,
1051
+ candidates: candidates
1052
+ .map(
1053
+ (candidate) =>
1054
+ `${candidate.url ?? "p2p"}:total=${prettyGweiToEth(candidate.totalGwei)}:boost=${candidate.boostFactor}:received=${candidate.receivedMs}ms`
1055
+ )
1056
+ .join(","),
1057
+ bidSource: best?.url ?? "p2p",
1058
+ });
1059
+ }
1060
+ return best;
1061
+ })();
921
1062
 
922
1063
  const commonBlockBodyPromise = chain.produceCommonBlockBody({
923
1064
  slot,
@@ -937,9 +1078,6 @@ export function getValidatorApi(
937
1078
  };
938
1079
 
939
1080
  metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
940
- if (builderBid !== null) {
941
- metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
942
- }
943
1081
 
944
1082
  const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
945
1083
  const t = metrics?.blockProductionTime.startTimer();
@@ -956,19 +1094,25 @@ export function getValidatorApi(
956
1094
  const enginePromise: ReturnType<typeof chain.produceBlock> = timed(ProducedBlockSource.engine, () =>
957
1095
  chain.produceBlock(baseAttrs)
958
1096
  ).then((engineBlock) => {
959
- // No need to wait for the bid block if the engine block will always be selected due to
960
- // suspected builder censorship or a builder boost factor of 0
961
- if (engineBlock.shouldOverrideBuilder || builderBoostFactor === BigInt(0)) {
1097
+ // No need to wait for the bid block if the engine block will always be selected, either
1098
+ // due to suspected builder censorship, or because no builders are configured and the
1099
+ // boost factor of 0 always prefers the local block over p2p bids
1100
+ if (engineBlock.shouldOverrideBuilder || (builderConfig.builders.length === 0 && builderBoostFactor === 0n)) {
962
1101
  controller.abort();
963
1102
  }
964
1103
  return engineBlock;
965
1104
  });
966
- const bidPromise: ReturnType<typeof chain.produceBlock> =
967
- builderBid !== null
968
- ? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
969
- : Promise.reject(new Error("No builder bid available"));
1105
+ const bidBlockPromise: ReturnType<typeof chain.produceBlock> = bestBidPromise.then((candidate) => {
1106
+ if (candidate === null) {
1107
+ throw new Error(NO_BID_AVAILABLE);
1108
+ }
1109
+ metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
1110
+ return timed(ProducedBlockSource.builder, () =>
1111
+ chain.produceBlock({...baseAttrs, builderBid: candidate.signedBid})
1112
+ );
1113
+ });
970
1114
 
971
- const [engineResult, bidResult] = await resolveOrRacePromises([enginePromise, bidPromise], {
1115
+ const [engineResult, bidBlockResult] = await resolveOrRacePromises([enginePromise, bidBlockPromise], {
972
1116
  resolveTimeoutMs: cutoffMs,
973
1117
  raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
974
1118
  signal: controller.signal,
@@ -977,29 +1121,74 @@ export function getValidatorApi(
977
1121
  let bestResult: typeof engineResult | null = null;
978
1122
  let source: ProducedBlockSource = ProducedBlockSource.engine;
979
1123
 
1124
+ // Resolved instantly whenever the bid branch produced a block
1125
+ const bestBid = bidBlockResult.status === "fulfilled" ? await bestBidPromise : null;
1126
+
1127
+ const logCtx = {
1128
+ slot,
1129
+ parentSlot,
1130
+ parentBlockRoot: parentBlockRootHex,
1131
+ parentBlockHash: parentBlock.executionPayloadBlockHash,
1132
+ fork,
1133
+ builderBoostFactor,
1134
+ strictFeeRecipientCheck,
1135
+ circuitBreakerActive,
1136
+ builderEntries: builderConfig.builders.length,
1137
+ ...(bestBid !== null
1138
+ ? {
1139
+ bidSource: bestBid.url !== undefined ? toPrintableUrl(bestBid.url) : "p2p",
1140
+ bidValue: prettyGweiToEth(bestBid.signedBid.message.value),
1141
+ bidExecutionPayment: prettyGweiToEth(bestBid.signedBid.message.executionPayment),
1142
+ // The full bid total and the counted total used during bid selection
1143
+ bidTotal: prettyGweiToEth(
1144
+ BigInt(bestBid.signedBid.message.value) + bestBid.signedBid.message.executionPayment
1145
+ ),
1146
+ bidCountedTotal: prettyGweiToEth(bestBid.totalGwei),
1147
+ bidBoostFactor: bestBid.boostFactor,
1148
+ builderIndex: bestBid.signedBid.message.builderIndex,
1149
+ bidBlockHash: toRootHex(bestBid.signedBid.message.blockHash),
1150
+ bidReceivedMs: bestBid.receivedMs,
1151
+ }
1152
+ : {}),
1153
+ };
1154
+
980
1155
  // handle shouldOverrideBuilder separately
981
- if (engineResult.status === "fulfilled" && engineResult.value.shouldOverrideBuilder && builderBid !== null) {
1156
+ if (
1157
+ engineResult.status === "fulfilled" &&
1158
+ engineResult.value.shouldOverrideBuilder &&
1159
+ (builderBidExpected || bidBlockResult.status === "fulfilled")
1160
+ ) {
982
1161
  source = ProducedBlockSource.engine;
983
1162
  bestResult = engineResult;
984
1163
  metrics?.blockProductionSelectionResults.inc({
985
1164
  source: ProducedBlockSource.engine,
986
1165
  reason: EngineBlockSelectionReason.BuilderCensorship,
987
1166
  });
988
- logger.warn("Selected local block: censorship suspected in builder bid", logCtx);
989
- } else if (engineResult.status === "fulfilled" && bidResult.status === "fulfilled") {
1167
+ logger.warn("Selected local block: censorship suspected in builder bid", {
1168
+ ...logCtx,
1169
+ durationMs: engineResult.durationMs,
1170
+ ...getBlockValueLogInfo(engineResult.value),
1171
+ });
1172
+ } else if (engineResult.status === "fulfilled" && bidBlockResult.status === "fulfilled") {
990
1173
  const result = selectBlockProductionSourceByBoostFactor({
991
- builderBoostFactor,
1174
+ builderBoostFactor: bestBid?.boostFactor ?? builderBoostFactor,
992
1175
  engineExecutionPayloadValue: engineResult.value.executionPayloadValue,
993
- // The bid value is the payment to the proposer, in Gwei
994
- builderExecutionPayloadValue: BigInt(builderBid?.message.value ?? 0) * GWEI_TO_WEI,
1176
+ // The bid total payment is its value plus its counted executionPayment, in Gwei
1177
+ builderExecutionPayloadValue: (bestBid?.totalGwei ?? 0n) * GWEI_TO_WEI,
995
1178
  });
996
1179
  source = result.source;
997
1180
  metrics?.blockProductionSelectionResults.inc(result);
998
- logger.info(`Selected ${source} block`, {reason: result.reason, ...logCtx});
999
- bestResult = source === ProducedBlockSource.builder ? bidResult : engineResult;
1000
- } else if (bidResult.status === "fulfilled") {
1181
+ logger.info(`Selected ${source} block`, {
1182
+ reason: result.reason,
1183
+ ...logCtx,
1184
+ engineDurationMs: engineResult.durationMs,
1185
+ ...getBlockValueLogInfo(engineResult.value, ProducedBlockSource.engine),
1186
+ builderDurationMs: bidBlockResult.durationMs,
1187
+ });
1188
+ bestResult = source === ProducedBlockSource.builder ? bidBlockResult : engineResult;
1189
+ } else if (bidBlockResult.status === "fulfilled") {
1001
1190
  source = ProducedBlockSource.builder;
1002
- bestResult = bidResult;
1191
+ bestResult = bidBlockResult;
1003
1192
  const reason =
1004
1193
  engineResult.status === "pending"
1005
1194
  ? BuilderBlockSelectionReason.EnginePending
@@ -1008,28 +1197,32 @@ export function getValidatorApi(
1008
1197
  logger.info("Selected builder bid block: no local block produced", {
1009
1198
  reason,
1010
1199
  ...logCtx,
1200
+ durationMs: bidBlockResult.durationMs,
1201
+ ...getBlockValueLogInfo(bidBlockResult.value),
1011
1202
  error: engineResult.status === "rejected" ? (engineResult.reason as Error).message : undefined,
1012
1203
  });
1013
1204
  } else if (engineResult.status === "fulfilled") {
1014
1205
  source = ProducedBlockSource.engine;
1015
1206
  bestResult = engineResult;
1016
1207
  const reason =
1017
- builderBid === null
1208
+ bidBlockResult.status === "rejected" && (bidBlockResult.reason as Error).message === NO_BID_AVAILABLE
1018
1209
  ? EngineBlockSelectionReason.BuilderNoBid
1019
- : bidResult.status === "pending"
1210
+ : bidBlockResult.status === "pending"
1020
1211
  ? EngineBlockSelectionReason.BuilderPending
1021
1212
  : EngineBlockSelectionReason.BuilderError;
1022
1213
  metrics?.blockProductionSelectionResults.inc({source: ProducedBlockSource.engine, reason});
1023
1214
  logger.info("Selected local block: no builder bid block produced", {
1024
1215
  reason,
1025
1216
  ...logCtx,
1026
- error: bidResult.status === "rejected" ? (bidResult.reason as Error).message : undefined,
1217
+ durationMs: engineResult.durationMs,
1218
+ ...getBlockValueLogInfo(engineResult.value),
1219
+ error: bidBlockResult.status === "rejected" ? (bidBlockResult.reason as Error).message : undefined,
1027
1220
  });
1028
1221
  }
1029
1222
 
1030
1223
  if (bestResult === null || bestResult.status !== "fulfilled") {
1031
1224
  const engineReason = engineResult.status === "rejected" ? engineResult.reason : engineResult.status;
1032
- const bidReason = bidResult.status === "rejected" ? bidResult.reason : bidResult.status;
1225
+ const bidReason = bidBlockResult.status === "rejected" ? bidBlockResult.reason : bidBlockResult.status;
1033
1226
  logger.error("Block production failed", {
1034
1227
  ...logCtx,
1035
1228
  engineReason: String(engineReason),
@@ -1093,7 +1286,13 @@ export function getValidatorApi(
1093
1286
 
1094
1287
  return {
1095
1288
  data: block as gloas.BeaconBlock,
1096
- meta: {version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: false},
1289
+ meta: {
1290
+ version: fork,
1291
+ consensusBlockValue,
1292
+ executionPayloadValue,
1293
+ executionPayloadIncluded: false,
1294
+ builderUrl: source === ProducedBlockSource.builder ? bestBid?.url : undefined,
1295
+ },
1097
1296
  };
1098
1297
  },
1099
1298
 
@@ -1877,6 +2076,43 @@ export function getValidatorApi(
1877
2076
  }
1878
2077
  },
1879
2078
 
2079
+ async submitBuilderPreferences({builderPreferences}) {
2080
+ const failures: FailureList = [];
2081
+
2082
+ await Promise.all(
2083
+ builderPreferences.map(async (entry, i) => {
2084
+ let builder = Buffer.from(entry.url).toString("utf8");
2085
+ try {
2086
+ const url = decodeBuilderUrl(entry.url);
2087
+ builder = toPrintableUrl(url);
2088
+ const proposerIndex = chain.getHeadState().getBeaconProposer(entry.auth.message.slot);
2089
+ const expectedProposerPubkey = chain.pubkeyCache.getOrThrow(proposerIndex).toBytes();
2090
+ if (!byteArrayEquals(entry.proposerPubkey, expectedProposerPubkey)) {
2091
+ throw new ApiError(
2092
+ 400,
2093
+ `Invalid proposer pubkey for builder preferences slot=${entry.auth.message.slot}`
2094
+ );
2095
+ }
2096
+ await chain.builderApiClient.submitBuilderPreferences(url, entry.proposerPubkey, {
2097
+ preferences: {maxExecutionPayment: entry.maxExecutionPayment},
2098
+ auth: entry.auth,
2099
+ });
2100
+ } catch (e) {
2101
+ failures.push({index: i, message: (e as Error).message});
2102
+ logger.verbose(
2103
+ `Error on submitBuilderPreferences [${i}]`,
2104
+ {slot: entry.auth.message.slot, builder},
2105
+ e as Error
2106
+ );
2107
+ }
2108
+ })
2109
+ );
2110
+
2111
+ if (failures.length > 0) {
2112
+ throw new IndexedError("Error submitting builder preferences", failures);
2113
+ }
2114
+ },
2115
+
1880
2116
  async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
1881
2117
  const fork = config.getForkName(slot);
1882
2118
 
@@ -89,7 +89,7 @@ export function selectBlockProductionSourceByBoostFactor({
89
89
  return {source: ProducedBlockSource.builder, reason: BuilderBlockSelectionReason.BuilderPreferred};
90
90
  }
91
91
 
92
- if (engineExecutionPayloadValue >= (builderExecutionPayloadValue * builderBoostFactor) / BigInt(100)) {
92
+ if (engineExecutionPayloadValue * 100n >= builderExecutionPayloadValue * builderBoostFactor) {
93
93
  return {source: ProducedBlockSource.engine, reason: EngineBlockSelectionReason.BlockValue};
94
94
  }
95
95
 
@@ -104,6 +104,13 @@ export class RestApiServer {
104
104
 
105
105
  // To parse our ApiError -> statusCode
106
106
  server.setErrorHandler<FastifyError | Error>((err, _req, res) => {
107
+ if (res.raw.headersSent) {
108
+ // A status code can no longer be written, e.g. if a long-lived SSE stream failed mid-flight.
109
+ // End the response instead, else the socket is left open and the client never notices.
110
+ if (!res.raw.writableEnded && !res.raw.destroyed) res.raw.end();
111
+ return;
112
+ }
113
+
107
114
  const stacktraces = opts.stacktraces ? err.stack?.split("\n") : undefined;
108
115
  if ("validation" in err && err.validation) {
109
116
  const {instancePath = "unknown", message} = err.validation?.[0] ?? {};
@@ -192,7 +192,7 @@ export class ArchiveStore {
192
192
 
193
193
  private onCheckpoint = (): void => {
194
194
  const headStateRoot = this.chain.forkChoice.getHead().stateRoot;
195
- this.chain.regen.pruneOnCheckpoint(
195
+ this.chain.regen.onCheckpoint(
196
196
  this.chain.forkChoice.getFinalizedCheckpoint().epoch,
197
197
  this.chain.forkChoice.getJustifiedCheckpoint().epoch,
198
198
  headStateRoot
@@ -58,11 +58,12 @@ export type PayloadErrorType =
58
58
  };
59
59
 
60
60
  export class PayloadError extends Error {
61
- type: PayloadErrorType;
62
-
63
- constructor(type: PayloadErrorType, message?: string) {
61
+ constructor(
62
+ readonly payloadInput: PayloadEnvelopeInput,
63
+ readonly type: PayloadErrorType,
64
+ message?: string
65
+ ) {
64
66
  super(message ?? type.code);
65
- this.type = type;
66
67
  }
67
68
  }
68
69
 
@@ -125,7 +126,7 @@ export async function importExecutionPayload(
125
126
  // 2. Get ProtoBlock for parent root lookup
126
127
  const protoBlock = this.forkChoice.getBlockHexDefaultStatus(blockRootHex);
127
128
  if (!protoBlock) {
128
- throw new PayloadError({
129
+ throw new PayloadError(payloadInput, {
129
130
  code: PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE,
130
131
  blockRootHex,
131
132
  });
@@ -140,13 +141,13 @@ export async function importExecutionPayload(
140
141
  );
141
142
 
142
143
  if (blockState == null) {
143
- throw new PayloadError({
144
+ throw new PayloadError(payloadInput, {
144
145
  code: PayloadErrorCode.MISS_BLOCK_STATE,
145
146
  blockRootHex: protoBlock.blockRoot,
146
147
  });
147
148
  }
148
149
  if (!isStatePostGloas(blockState)) {
149
- throw new PayloadError({
150
+ throw new PayloadError(payloadInput, {
150
151
  code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
151
152
  message: `Expected gloas+ state for payload import, got fork=${blockState.forkName}`,
152
153
  });
@@ -161,6 +162,7 @@ export async function importExecutionPayload(
161
162
  });
162
163
  } catch (e) {
163
164
  throw new PayloadError(
165
+ payloadInput,
164
166
  {
165
167
  code: PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR,
166
168
  message: (e as Error).message,
@@ -184,7 +186,6 @@ export async function importExecutionPayload(
184
186
  : verifyExecutionPayloadEnvelopeSignature(
185
187
  this.config,
186
188
  blockState,
187
- this.pubkeyCache,
188
189
  signedEnvelope,
189
190
  payloadInput.proposerIndex,
190
191
  this.bls
@@ -193,7 +194,7 @@ export async function importExecutionPayload(
193
194
 
194
195
  // 4b. Check signature verification result
195
196
  if (!signatureValid) {
196
- throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
197
+ throw new PayloadError(payloadInput, {code: PayloadErrorCode.INVALID_SIGNATURE});
197
198
  }
198
199
 
199
200
  // 4c. Handle EL response
@@ -202,7 +203,7 @@ export async function importExecutionPayload(
202
203
  break;
203
204
 
204
205
  case ExecutionPayloadStatus.INVALID:
205
- throw new PayloadError({
206
+ throw new PayloadError(payloadInput, {
206
207
  code: PayloadErrorCode.EXECUTION_ENGINE_INVALID,
207
208
  execStatus: execResult.status,
208
209
  errorMessage: execResult.validationError ?? "",
@@ -215,7 +216,7 @@ export async function importExecutionPayload(
215
216
  case ExecutionPayloadStatus.INVALID_BLOCK_HASH:
216
217
  case ExecutionPayloadStatus.ELERROR:
217
218
  case ExecutionPayloadStatus.UNAVAILABLE:
218
- throw new PayloadError({
219
+ throw new PayloadError(payloadInput, {
219
220
  code: PayloadErrorCode.EXECUTION_ENGINE_ERROR,
220
221
  execStatus: execResult.status,
221
222
  errorMessage: execResult.validationError ?? "",
@@ -8,7 +8,7 @@ import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.j
8
8
  import {BlockProcessOpts} from "../options.js";
9
9
  import {IBlockInput} from "./blockInput/types.js";
10
10
  import {importBlock} from "./importBlock.js";
11
- import {importExecutionPayload} from "./importExecutionPayload.js";
11
+ import {PayloadError, importExecutionPayload} from "./importExecutionPayload.js";
12
12
  import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
13
13
  import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
14
14
  import {assertLinearChainSegment} from "./utils/chainSegment.js";
@@ -164,13 +164,21 @@ export async function processBlocks(
164
164
  return; // Ignore
165
165
  }
166
166
 
167
- // above functions should only throw BlockError
168
- const err = getBlockError(e, blocks[0].getBlock());
167
+ // above functions should only throw BlockError, or PayloadError from the gloas payload import
168
+ const err = getBlockOrPayloadError(e, blocks[0].getBlock());
169
169
 
170
170
  // TODO: De-duplicate with logic above
171
171
  // ChainEvent.errorBlock
172
- if (!(err instanceof BlockError)) {
173
- this.logger.debug("Non BlockError received", {}, err);
172
+ if (!(err instanceof BlockError) && !(err instanceof PayloadError)) {
173
+ this.logger.debug("Neither BlockError nor PayloadError received", {}, err);
174
+ } else if (err instanceof PayloadError) {
175
+ if (!opts.disableOnBlockError) {
176
+ this.logger.debug(
177
+ "Payload error",
178
+ {slot: err.payloadInput.slot, blockRoot: err.payloadInput.blockRootHex},
179
+ err
180
+ );
181
+ }
174
182
  } else if (!opts.disableOnBlockError) {
175
183
  this.logger.debug("Block error", {slot: err.signedBlock.message.slot}, err);
176
184
 
@@ -201,11 +209,15 @@ export async function processBlocks(
201
209
  }
202
210
  }
203
211
 
204
- function getBlockError(e: unknown, block: SignedBeaconBlock): BlockError {
212
+ function getBlockOrPayloadError(e: unknown, block: SignedBeaconBlock): BlockError | PayloadError {
205
213
  if (e instanceof BlockError) {
206
214
  return e;
207
215
  }
208
216
 
217
+ if (e instanceof PayloadError) {
218
+ return e;
219
+ }
220
+
209
221
  if (e instanceof Error) {
210
222
  const blockError = new BlockError(block, {code: BlockErrorCode.BEACON_CHAIN_ERROR, error: e});
211
223
  blockError.stack = e.stack;