@lodestar/beacon-node 1.44.0-dev.552cdce8d0 → 1.44.0-dev.6a52e6a9f6

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 (159) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +30 -0
  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 +1 -1
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts.map +1 -1
  8. package/lib/api/impl/debug/index.js +69 -12
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +101 -42
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
  14. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.js +0 -4
  16. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.js +5 -2
  19. package/lib/chain/blocks/importBlock.js.map +1 -1
  20. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  21. package/lib/chain/blocks/importExecutionPayload.js +5 -3
  22. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  23. package/lib/chain/chain.d.ts +1 -1
  24. package/lib/chain/chain.d.ts.map +1 -1
  25. package/lib/chain/chain.js +2 -1
  26. package/lib/chain/chain.js.map +1 -1
  27. package/lib/chain/errors/executionPayloadBid.d.ts +11 -1
  28. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  29. package/lib/chain/errors/executionPayloadBid.js +2 -0
  30. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  31. package/lib/chain/errors/payloadAttestation.d.ts +6 -0
  32. package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
  33. package/lib/chain/errors/payloadAttestation.js +1 -0
  34. package/lib/chain/errors/payloadAttestation.js.map +1 -1
  35. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  36. package/lib/chain/forkChoice/index.js +14 -4
  37. package/lib/chain/forkChoice/index.js.map +1 -1
  38. package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
  39. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  40. package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
  41. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  42. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  43. package/lib/chain/prepareNextSlot.js +2 -1
  44. package/lib/chain/prepareNextSlot.js.map +1 -1
  45. package/lib/chain/produceBlock/produceBlockBody.d.ts +7 -1
  46. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  47. package/lib/chain/produceBlock/produceBlockBody.js +107 -18
  48. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  49. package/lib/chain/regen/interface.d.ts +2 -1
  50. package/lib/chain/regen/interface.d.ts.map +1 -1
  51. package/lib/chain/regen/interface.js +2 -0
  52. package/lib/chain/regen/interface.js.map +1 -1
  53. package/lib/chain/regen/queued.d.ts +0 -1
  54. package/lib/chain/regen/queued.d.ts.map +1 -1
  55. package/lib/chain/regen/queued.js +0 -4
  56. package/lib/chain/regen/queued.js.map +1 -1
  57. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
  58. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  59. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
  60. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  61. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
  62. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  63. package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
  64. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  65. package/lib/chain/stateCache/types.d.ts +0 -2
  66. package/lib/chain/stateCache/types.d.ts.map +1 -1
  67. package/lib/chain/stateCache/types.js.map +1 -1
  68. package/lib/chain/validation/executionPayloadBid.d.ts +7 -3
  69. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  70. package/lib/chain/validation/executionPayloadBid.js +58 -15
  71. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  72. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  73. package/lib/chain/validation/payloadAttestationMessage.js +24 -4
  74. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  75. package/lib/chain/validatorMonitor.d.ts +1 -0
  76. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  77. package/lib/chain/validatorMonitor.js +16 -0
  78. package/lib/chain/validatorMonitor.js.map +1 -1
  79. package/lib/execution/builder/index.d.ts +1 -2
  80. package/lib/execution/builder/index.d.ts.map +1 -1
  81. package/lib/execution/builder/index.js +0 -1
  82. package/lib/execution/builder/index.js.map +1 -1
  83. package/lib/execution/engine/interface.d.ts +1 -0
  84. package/lib/execution/engine/interface.d.ts.map +1 -1
  85. package/lib/execution/engine/types.d.ts +2 -0
  86. package/lib/execution/engine/types.d.ts.map +1 -1
  87. package/lib/execution/engine/types.js +2 -0
  88. package/lib/execution/engine/types.js.map +1 -1
  89. package/lib/metrics/metrics/lodestar.d.ts +1 -1
  90. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  91. package/lib/metrics/metrics/lodestar.js +4 -3
  92. package/lib/metrics/metrics/lodestar.js.map +1 -1
  93. package/lib/network/gossip/topic.d.ts +1 -1
  94. package/lib/network/interface.d.ts +1 -0
  95. package/lib/network/interface.d.ts.map +1 -1
  96. package/lib/network/network.d.ts +1 -0
  97. package/lib/network/network.d.ts.map +1 -1
  98. package/lib/network/network.js +5 -0
  99. package/lib/network/network.js.map +1 -1
  100. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  101. package/lib/network/processor/gossipHandlers.js +12 -4
  102. package/lib/network/processor/gossipHandlers.js.map +1 -1
  103. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  104. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +5 -0
  105. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  106. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
  107. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  108. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +3 -3
  109. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  110. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
  111. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  112. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
  113. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  114. package/lib/network/reqresp/handlers/index.js +2 -2
  115. package/lib/network/reqresp/handlers/index.js.map +1 -1
  116. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  117. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +22 -3
  118. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  119. package/package.json +14 -16
  120. package/src/api/impl/beacon/blocks/index.ts +36 -0
  121. package/src/api/impl/beacon/pool/index.ts +3 -1
  122. package/src/api/impl/debug/index.ts +73 -12
  123. package/src/api/impl/validator/index.ts +112 -43
  124. package/src/chain/archiveStore/archiveStore.ts +0 -5
  125. package/src/chain/blocks/importBlock.ts +10 -2
  126. package/src/chain/blocks/importExecutionPayload.ts +8 -2
  127. package/src/chain/chain.ts +2 -0
  128. package/src/chain/errors/executionPayloadBid.ts +6 -1
  129. package/src/chain/errors/payloadAttestation.ts +2 -0
  130. package/src/chain/forkChoice/index.ts +14 -4
  131. package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
  132. package/src/chain/prepareNextSlot.ts +2 -1
  133. package/src/chain/produceBlock/produceBlockBody.ts +158 -25
  134. package/src/chain/regen/interface.ts +2 -1
  135. package/src/chain/regen/queued.ts +0 -5
  136. package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
  137. package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
  138. package/src/chain/stateCache/types.ts +0 -2
  139. package/src/chain/validation/executionPayloadBid.ts +66 -19
  140. package/src/chain/validation/payloadAttestationMessage.ts +26 -4
  141. package/src/chain/validatorMonitor.ts +18 -0
  142. package/src/execution/builder/index.ts +1 -4
  143. package/src/execution/engine/interface.ts +1 -0
  144. package/src/execution/engine/types.ts +4 -0
  145. package/src/metrics/metrics/lodestar.ts +4 -3
  146. package/src/network/interface.ts +1 -0
  147. package/src/network/network.ts +11 -0
  148. package/src/network/processor/gossipHandlers.ts +17 -4
  149. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +6 -0
  150. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
  151. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +3 -3
  152. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
  153. package/src/network/reqresp/handlers/index.ts +2 -2
  154. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +21 -3
  155. package/lib/execution/builder/utils.d.ts +0 -5
  156. package/lib/execution/builder/utils.d.ts.map +0 -1
  157. package/lib/execution/builder/utils.js +0 -17
  158. package/lib/execution/builder/utils.js.map +0 -1
  159. package/src/execution/builder/utils.ts +0 -19
@@ -1,3 +1,4 @@
1
+ import { ForkSeq } from "@lodestar/params";
1
2
  import { RespStatus, ResponseError } from "@lodestar/reqresp";
2
3
  import { prettyBytes, prettyPrintIndices, toRootHex } from "@lodestar/utils";
3
4
  import { getBlobKzgCommitmentsCountFromSignedBeaconBlockSerialized } from "../../../util/sszBytes.js";
@@ -12,6 +13,13 @@ export async function handleColumnSidecarUnavailability({ chain, db, metrics, un
12
13
  logData.blockRoot = prettyBytes(blockRoot);
13
14
  }
14
15
  chain.logger.debug("dataColumnSidecar requested unavailable", logData);
16
+ // Post-gloas, columns exist only for FULL blocks; a finalized block is FULL if its envelope was
17
+ // archived. Bid blobsCount is unreliable here since an EMPTY block's bid may still commit to blobs
18
+ if (blockRoot === undefined && chain.config.getForkSeq(slot) >= ForkSeq.gloas) {
19
+ const envelopeBytes = await db.executionPayloadEnvelopeArchive.getBinary(slot);
20
+ if (!envelopeBytes)
21
+ return;
22
+ }
15
23
  const blockBytes = blockRoot ? await db.block.getBinary(blockRoot) : await db.blockArchive.getBinary(slot);
16
24
  if (!blockBytes) {
17
25
  chain.logger.verbose(`Expected ${blockRoot ? "unfinalized" : "finalized"} block not found while handling unavailable dataColumnSidecar`, {
@@ -38,9 +46,20 @@ export function validateRequestedDataColumns(chain, requestedColumns) {
38
46
  if (requestedColumns.length === 0) {
39
47
  throw new ResponseError(RespStatus.INVALID_REQUEST, "dataColumnSidecar requested without column indices");
40
48
  }
41
- const custodyColumns = chain.custodyConfig.custodyColumns;
42
- const availableColumns = requestedColumns.filter((c) => custodyColumns.includes(c));
43
- const missingColumns = requestedColumns.filter((c) => !custodyColumns.includes(c));
49
+ const { custodyColumns, custodyColumnsIndex } = chain.custodyConfig;
50
+ const availableColumns = [];
51
+ const missingColumns = [];
52
+ for (const c of requestedColumns) {
53
+ // `c` is peer-controlled and SSZ-deserialized as `uint64`, so it may exceed
54
+ // `NUMBER_OF_COLUMNS - 1`; `Uint8Array` returns `undefined` for OOB reads,
55
+ // and `undefined !== 0` would silently classify OOB indices as custodied.
56
+ if ((custodyColumnsIndex[c] ?? 0) !== 0) {
57
+ availableColumns.push(c);
58
+ }
59
+ else {
60
+ missingColumns.push(c);
61
+ }
62
+ }
44
63
  if (missingColumns.length > 0) {
45
64
  chain.logger.verbose("Requested dataColumnSidecar for non-custody columns", {
46
65
  requestedColumns: prettyPrintIndices(requestedColumns),
@@ -1 +1 @@
1
- {"version":3,"file":"dataColumnResponseValidation.js","sourceRoot":"","sources":["../../../../src/network/reqresp/utils/dataColumnResponseValidation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataColumnResponseValidation.js","sourceRoot":"","sources":["../../../../src/network/reqresp/utils/dataColumnResponseValidation.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAI3E,OAAO,EAAC,yDAAyD,EAAC,MAAM,2BAA2B,CAAC;AAEpG,MAAM,CAAC,KAAK,UAAU,iCAAiC,CAAC,EACtD,KAAK,EACL,EAAE,EACF,OAAO,EACP,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,IAAI,EACJ,SAAS,GAUV,EAAiB;IAChB,MAAM,OAAO,GAAY;QACvB,IAAI;QACJ,wBAAwB,EAAE,kBAAkB,CAAC,wBAAwB,CAAC;QACtE,gBAAgB,EAAE,kBAAkB,CAAC,gBAAgB,CAAC;QACtD,gBAAgB,EAAE,kBAAkB,CAAC,gBAAgB,CAAC;KACvD,CAAC;IACF,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,OAAO,CAAC,CAAC;IAEvE,gGAAgG;IAChG,mGAAmG;IACnG,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAC9E,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,+BAA+B,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC/E,IAAI,CAAC,aAAa;YAAE,OAAO;IAC7B,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3G,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,OAAO,CAClB,YAAY,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,+DAA+D,EAClH;YACE,IAAI;YACJ,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;YACvD,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;SACnD,CACF,CAAC;QACF,OAAO;IACT,CAAC;IAED,uCAAuC;IACvC,MAAM,UAAU,GAAG,yDAAyD,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAEvG,wFAAwF;IACxF,IAAI,UAAU,KAAK,CAAC;QAAE,OAAO;IAE7B,wEAAwE;IACxE,mEAAmE;IACnE,OAAO,EAAE,WAAW,CAAC,qBAAqB,CAAC,GAAG,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAChF,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,kEAAkE,EAAE;QACvF,wBAAwB,EAAE,kBAAkB,CAAC,wBAAwB,CAAC;QACtE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;KAC1D,CAAC,CAAC;AAAA,CACJ;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAmB,EAAE,gBAA+B,EAAiB;IAChH,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,aAAa,CAAC,UAAU,CAAC,eAAe,EAAE,oDAAoD,CAAC,CAAC;IAC5G,CAAC;IAED,MAAM,EAAC,cAAc,EAAE,mBAAmB,EAAC,GAAG,KAAK,CAAC,aAAa,CAAC;IAClE,MAAM,gBAAgB,GAAkB,EAAE,CAAC;IAC3C,MAAM,cAAc,GAAkB,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;QACjC,4EAA4E;QAC5E,2EAA2E;QAC3E,0EAA0E;QAC1E,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,qDAAqD,EAAE;YAC1E,gBAAgB,EAAE,kBAAkB,CAAC,gBAAgB,CAAC;YACtD,cAAc,EAAE,kBAAkB,CAAC,cAAc,CAAC;YAClD,gBAAgB,EAAE,kBAAkB,CAAC,gBAAgB,CAAC;YACtD,cAAc,EAAE,kBAAkB,CAAC,cAAc,CAAC;SACnD,CAAC,CAAC;QAEH,iEAAiE;QACjE,oFAAoF;QACpF,8GAA8G;IAChH,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,4CAA4C,EAAE;YACjE,gBAAgB,EAAE,kBAAkB,CAAC,gBAAgB,CAAC;YACtD,cAAc,EAAE,kBAAkB,CAAC,cAAc,CAAC;SACnD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,gBAAgB,CAAC;AAAA,CACzB"}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/ChainSafe/lodestar/issues"
13
13
  },
14
- "version": "1.44.0-dev.552cdce8d0",
14
+ "version": "1.44.0-dev.6a52e6a9f6",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -116,7 +116,6 @@
116
116
  "@chainsafe/libp2p-quic": "^2.0.1",
117
117
  "@chainsafe/persistent-merkle-tree": "^1.2.5",
118
118
  "@chainsafe/prometheus-gc-stats": "^1.0.0",
119
- "@chainsafe/pubkey-index-map": "^3.0.0",
120
119
  "@chainsafe/snappy-wasm": "^0.5.0",
121
120
  "@chainsafe/ssz": "^1.4.0",
122
121
  "@chainsafe/threads": "^1.11.3",
@@ -135,17 +134,17 @@
135
134
  "@libp2p/peer-id": "^6.0.4",
136
135
  "@libp2p/prometheus-metrics": "^5.0.14",
137
136
  "@libp2p/tcp": "^11.0.13",
138
- "@lodestar/api": "^1.44.0-dev.552cdce8d0",
139
- "@lodestar/config": "^1.44.0-dev.552cdce8d0",
140
- "@lodestar/db": "^1.44.0-dev.552cdce8d0",
141
- "@lodestar/fork-choice": "^1.44.0-dev.552cdce8d0",
142
- "@lodestar/logger": "^1.44.0-dev.552cdce8d0",
143
- "@lodestar/params": "^1.44.0-dev.552cdce8d0",
144
- "@lodestar/reqresp": "^1.44.0-dev.552cdce8d0",
145
- "@lodestar/state-transition": "^1.44.0-dev.552cdce8d0",
146
- "@lodestar/types": "^1.44.0-dev.552cdce8d0",
147
- "@lodestar/utils": "^1.44.0-dev.552cdce8d0",
148
- "@lodestar/validator": "^1.44.0-dev.552cdce8d0",
137
+ "@lodestar/api": "^1.44.0-dev.6a52e6a9f6",
138
+ "@lodestar/config": "^1.44.0-dev.6a52e6a9f6",
139
+ "@lodestar/db": "^1.44.0-dev.6a52e6a9f6",
140
+ "@lodestar/fork-choice": "^1.44.0-dev.6a52e6a9f6",
141
+ "@lodestar/logger": "^1.44.0-dev.6a52e6a9f6",
142
+ "@lodestar/params": "^1.44.0-dev.6a52e6a9f6",
143
+ "@lodestar/reqresp": "^1.44.0-dev.6a52e6a9f6",
144
+ "@lodestar/state-transition": "^1.44.0-dev.6a52e6a9f6",
145
+ "@lodestar/types": "^1.44.0-dev.6a52e6a9f6",
146
+ "@lodestar/utils": "^1.44.0-dev.6a52e6a9f6",
147
+ "@lodestar/validator": "^1.44.0-dev.6a52e6a9f6",
149
148
  "@multiformats/multiaddr": "^13.0.1",
150
149
  "datastore-core": "^11.0.2",
151
150
  "datastore-fs": "^11.0.2",
@@ -168,13 +167,12 @@
168
167
  "@libp2p/interface-internal": "^3.0.13",
169
168
  "@libp2p/logger": "^6.2.2",
170
169
  "@libp2p/utils": "^7.0.13",
171
- "@lodestar/spec-test-util": "^1.44.0-dev.552cdce8d0",
170
+ "@lodestar/spec-test-util": "^1.44.0-dev.6a52e6a9f6",
172
171
  "@types/js-yaml": "^4.0.5",
173
172
  "@types/qs": "^6.9.7",
174
173
  "@types/tmp": "^0.2.3",
175
174
  "dotenv": "^16.4.5",
176
175
  "js-yaml": "^4.1.0",
177
- "rewiremock": "^3.14.5",
178
176
  "rimraf": "^4.4.1",
179
177
  "snappy": "^7.2.2",
180
178
  "snappyjs": "^0.7.0",
@@ -186,5 +184,5 @@
186
184
  "beacon",
187
185
  "blockchain"
188
186
  ],
189
- "gitHead": "fed08b6217ae9e76ec2ce7b28016620c41faba4f"
187
+ "gitHead": "81de9c8ef90c65bd31cc81b2f90fc01981705073"
190
188
  }
@@ -50,6 +50,7 @@ import {
50
50
  ProduceFullGloas,
51
51
  } from "../../../../chain/produceBlock/index.js";
52
52
  import {validateGossipBlock} from "../../../../chain/validation/block.js";
53
+ import {validateApiExecutionPayloadBid} from "../../../../chain/validation/executionPayloadBid.js";
53
54
  import {validateApiExecutionPayloadEnvelope} from "../../../../chain/validation/executionPayloadEnvelope.js";
54
55
  import {OpSource} from "../../../../chain/validatorMonitor.js";
55
56
  import {
@@ -825,6 +826,41 @@ export function getBeaconBlockApi({
825
826
  });
826
827
  },
827
828
 
829
+ async publishExecutionPayloadBid({signedExecutionPayloadBid}) {
830
+ const bid = signedExecutionPayloadBid.message;
831
+ const slot = bid.slot;
832
+ const fork = config.getForkName(slot);
833
+
834
+ if (!isForkPostGloas(fork)) {
835
+ throw new ApiError(400, `publishExecutionPayloadBid not supported for pre-gloas fork=${fork}`);
836
+ }
837
+
838
+ await validateApiExecutionPayloadBid(chain, signedExecutionPayloadBid);
839
+
840
+ try {
841
+ const insertOutcome = chain.executionPayloadBidPool.add(signedExecutionPayloadBid);
842
+ metrics?.opPool.executionPayloadBidPool.apiInsertOutcome.inc({insertOutcome});
843
+ } catch (e) {
844
+ chain.logger.error("Error adding to executionPayloadBid pool", {}, e as Error);
845
+ }
846
+
847
+ const sentPeers = await network.publishSignedExecutionPayloadBid(signedExecutionPayloadBid);
848
+
849
+ chain.emitter.emit(routes.events.EventType.executionPayloadBid, {
850
+ version: fork,
851
+ data: signedExecutionPayloadBid,
852
+ });
853
+
854
+ chain.logger.info("Published execution payload bid", {
855
+ slot,
856
+ builderIndex: bid.builderIndex,
857
+ blockHash: toRootHex(bid.blockHash),
858
+ parentBlockHash: toRootHex(bid.parentBlockHash),
859
+ value: bid.value,
860
+ sentPeers,
861
+ });
862
+ },
863
+
828
864
  async getSignedExecutionPayloadEnvelope({blockId}, context) {
829
865
  const {block, executionOptimistic, finalized} = await getBlockResponse(chain, blockId);
830
866
  const slot = block.message.slot;
@@ -328,8 +328,10 @@ export function getBeaconPoolApi({
328
328
 
329
329
  chain.forkChoice.notifyPtcMessages(
330
330
  toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
331
+ payloadAttestationMessage.data.slot,
331
332
  validatorCommitteeIndices,
332
- payloadAttestationMessage.data.payloadPresent
333
+ payloadAttestationMessage.data.payloadPresent,
334
+ payloadAttestationMessage.data.blobDataAvailable
333
335
  );
334
336
 
335
337
  await network.publishPayloadAttestationMessage(payloadAttestationMessage);
@@ -1,7 +1,8 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ApplicationMethods} from "@lodestar/api/server";
3
- import {ExecutionStatus} from "@lodestar/fork-choice";
3
+ import {ExecutionStatus, PayloadStatus} from "@lodestar/fork-choice";
4
4
  import {ForkPostDeneb, ZERO_HASH_HEX, isForkPostDeneb, isForkPostFulu} from "@lodestar/params";
5
+ import {computeTimeAtSlot} from "@lodestar/state-transition";
5
6
  import {BeaconState, DataColumnSidecar, DataColumnSidecars, type SignedBeaconBlock, sszTypesFor} from "@lodestar/types";
6
7
  import {toRootHex} from "@lodestar/utils";
7
8
  import {getBlobKzgCommitments} from "../../../util/dataColumns.js";
@@ -12,6 +13,29 @@ import {getStateResponseWithRegen} from "../beacon/state/utils.js";
12
13
  import {ApiModules} from "../types.js";
13
14
  import {assertUniqueItems} from "../utils.js";
14
15
 
16
+ function toForkChoiceValidity(status: ExecutionStatus): "valid" | "invalid" | "optimistic" {
17
+ switch (status) {
18
+ case ExecutionStatus.Valid:
19
+ return "valid";
20
+ case ExecutionStatus.Invalid:
21
+ return "invalid";
22
+ case ExecutionStatus.Syncing:
23
+ case ExecutionStatus.PreMerge:
24
+ return "optimistic";
25
+ }
26
+ }
27
+
28
+ function toPayloadStatusName(status: PayloadStatus): "pending" | "empty" | "full" {
29
+ switch (status) {
30
+ case PayloadStatus.PENDING:
31
+ return "pending";
32
+ case PayloadStatus.EMPTY:
33
+ return "empty";
34
+ case PayloadStatus.FULL:
35
+ return "full";
36
+ }
37
+ }
38
+
15
39
  export function getDebugApi({
16
40
  chain,
17
41
  config,
@@ -40,23 +64,60 @@ export function getDebugApi({
40
64
  justifiedEpoch: node.justifiedEpoch,
41
65
  finalizedEpoch: node.finalizedEpoch,
42
66
  weight: node.weight,
43
- validity: (() => {
44
- switch (node.executionStatus) {
45
- case ExecutionStatus.Valid:
46
- return "valid";
47
- case ExecutionStatus.Invalid:
48
- return "invalid";
49
- case ExecutionStatus.Syncing:
50
- case ExecutionStatus.PreMerge:
51
- return "optimistic";
52
- }
53
- })(),
67
+ validity: toForkChoiceValidity(node.executionStatus),
54
68
  executionBlockHash: node.executionPayloadBlockHash ?? ZERO_HASH_HEX,
55
69
  })),
56
70
  },
57
71
  };
58
72
  },
59
73
 
74
+ async getDebugForkChoiceV2() {
75
+ const {forkChoice} = chain;
76
+ return {
77
+ data: {
78
+ justifiedCheckpoint: forkChoice.getJustifiedCheckpoint(),
79
+ finalizedCheckpoint: forkChoice.getFinalizedCheckpoint(),
80
+ forkChoiceNodes: forkChoice.getAllNodes().map((node) => {
81
+ // Payload-specific fields apply only to a revealed Gloas payload = the FULL variant of a
82
+ // Gloas block
83
+ const ptc = node.payloadStatus === PayloadStatus.FULL ? forkChoice.getPTCVoteCounts(node.blockRoot) : null;
84
+ return {
85
+ payloadStatus: toPayloadStatusName(node.payloadStatus),
86
+ slot: node.slot,
87
+ blockRoot: node.blockRoot,
88
+ parentRoot: node.parentRoot,
89
+ weight: node.weight,
90
+ validity: toForkChoiceValidity(node.executionStatus),
91
+ executionBlockHash: node.executionPayloadBlockHash ?? ZERO_HASH_HEX,
92
+ extraData: {
93
+ executionOptimistic: isOptimisticBlock(node),
94
+ timestamp: computeTimeAtSlot(config, node.slot, chain.genesisTime),
95
+ target: node.targetRoot,
96
+ justifiedEpoch: node.justifiedEpoch,
97
+ finalizedEpoch: node.finalizedEpoch,
98
+ unrealizedJustifiedEpoch: node.unrealizedJustifiedEpoch,
99
+ unrealizedFinalizedEpoch: node.unrealizedFinalizedEpoch,
100
+ payloadAttesterCount: ptc?.attesterCount ?? null,
101
+ payloadAvailabilityYesCount: ptc?.payloadPresentCount ?? null,
102
+ payloadDataAvailabilityYesCount: ptc?.dataAvailableCount ?? null,
103
+ gasLimit:
104
+ node.payloadStatus === PayloadStatus.FULL && "executionPayloadGasLimit" in node
105
+ ? node.executionPayloadGasLimit
106
+ : null,
107
+ },
108
+ };
109
+ }),
110
+ extraData: {
111
+ unrealizedJustifiedCheckpoint: forkChoice.getUnrealizedJustifiedCheckpoint(),
112
+ unrealizedFinalizedCheckpoint: forkChoice.getUnrealizedFinalizedCheckpoint(),
113
+ proposerBoostRoot: forkChoice.getProposerBoostRoot(),
114
+ previousProposerBoostRoot: forkChoice.getPreviousProposerBoostRoot(),
115
+ headRoot: forkChoice.getHeadRoot(),
116
+ },
117
+ },
118
+ };
119
+ },
120
+
60
121
  async getProtoArrayNodes() {
61
122
  const nodes = chain.forkChoice.getAllNodes().map((node) => ({
62
123
  // if node has executionPayloadNumber, it will overwrite the below default
@@ -914,20 +914,38 @@ export function getValidatorApi(
914
914
  notWhileSyncing();
915
915
  await waitForSlot(slot);
916
916
 
917
- // TODO GLOAS: support producing blocks from builder bids
918
- const source = ProducedBlockSource.engine;
919
-
920
- // TODO GLOAS: needs to be updated after fork choice changes are merged
921
917
  const parentBlock = chain.getProposerHead(slot);
922
918
  const {blockRoot: parentBlockRootHex, slot: parentSlot} = parentBlock;
923
919
  const parentBlockRoot = fromHex(parentBlockRootHex);
924
920
  notOnOutOfRangeData(parentBlockRoot);
925
921
  metrics?.blockProductionSlotDelta.set(slot - parentSlot);
926
- metrics?.blockProductionRequests.inc({source});
927
922
 
928
923
  const graffitiBytes = toGraffitiBytes(
929
924
  graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
930
925
  );
926
+
927
+ // TODO GLOAS: respect builderSelection (MaxProfit, BuilderAlways, ExecutionAlways, etc.) to let
928
+ // the user control bid source preferences and value comparison. Also add external builder api
929
+ // support when it is implemented.
930
+ const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
931
+ const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
932
+ const builderBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
933
+
934
+ const logCtx = {
935
+ slot,
936
+ parentSlot,
937
+ parentBlockRoot: parentBlockRootHex,
938
+ parentBlockHash: parentBlock.executionPayloadBlockHash,
939
+ fork,
940
+ ...(builderBid !== null
941
+ ? {
942
+ bidValue: builderBid.message.value,
943
+ builderIndex: builderBid.message.builderIndex,
944
+ bidBlockHash: toRootHex(builderBid.message.blockHash),
945
+ }
946
+ : {}),
947
+ };
948
+
931
949
  const commonBlockBodyPromise = chain.produceCommonBlockBody({
932
950
  slot,
933
951
  parentBlock,
@@ -935,44 +953,76 @@ export function getValidatorApi(
935
953
  graffiti: graffitiBytes,
936
954
  });
937
955
 
938
- let timer: undefined | ((opts: {source: ProducedBlockSource}) => number);
939
- try {
940
- timer = metrics?.blockProductionTime.startTimer();
941
- const {block, executionPayloadValue, consensusBlockValue} = await chain.produceBlock({
942
- slot,
943
- parentBlock,
944
- randaoReveal,
945
- graffiti: graffitiBytes,
946
- feeRecipient,
947
- commonBlockBodyPromise,
948
- });
956
+ const baseAttrs = {
957
+ slot,
958
+ parentBlock,
959
+ randaoReveal,
960
+ graffiti: graffitiBytes,
961
+ feeRecipient,
962
+ commonBlockBodyPromise,
963
+ };
949
964
 
950
- metrics?.blockProductionSuccess.inc({source});
951
- metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
952
- metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
953
- metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
965
+ metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
966
+ if (builderBid !== null) {
967
+ metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
968
+ }
954
969
 
955
- const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
956
- logger.verbose("Produced block", {
957
- slot,
958
- executionPayloadValue,
959
- consensusBlockValue,
960
- root: blockRoot,
961
- });
962
- if (chain.opts.persistProducedBlocks) {
963
- void chain.persistBlock(block, "produced_engine_block");
970
+ const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
971
+ const t = metrics?.blockProductionTime.startTimer();
972
+ return fn().finally(() => t?.({source}));
973
+ };
974
+
975
+ // Always build local block. If builder bid available, also build with it in parallel and prefer it.
976
+ const [engineResult, bidResult] = await Promise.allSettled([
977
+ timed(ProducedBlockSource.engine, () => chain.produceBlock(baseAttrs)),
978
+ builderBid !== null
979
+ ? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
980
+ : Promise.reject(),
981
+ ]);
982
+
983
+ let bestResult: typeof engineResult | null = null;
984
+ let source: ProducedBlockSource = ProducedBlockSource.engine;
985
+ if (builderBid !== null && bidResult.status === "fulfilled") {
986
+ source = ProducedBlockSource.builder;
987
+ bestResult = bidResult;
988
+ logger.info("Selected builder bid block", logCtx);
989
+ } else if (engineResult.status === "fulfilled") {
990
+ source = ProducedBlockSource.engine;
991
+ bestResult = engineResult;
992
+ if (builderBid !== null) {
993
+ logger.warn("Builder bid block production failed, using local block", logCtx);
964
994
  }
995
+ }
965
996
 
966
- return {
967
- data: block as gloas.BeaconBlock,
968
- meta: {
969
- version: fork,
970
- consensusBlockValue,
971
- },
972
- };
973
- } finally {
974
- timer?.({source});
997
+ if (bestResult === null || bestResult.status !== "fulfilled") {
998
+ const engineReason = engineResult.status === "rejected" ? engineResult.reason : undefined;
999
+ const bidReason = builderBid !== null && bidResult.status === "rejected" ? bidResult.reason : undefined;
1000
+ logger.error("Block production failed", {...logCtx, engineReason, bidReason});
1001
+ throw Error(`Block production failed: engine=${engineReason ?? "n/a"} builder=${bidReason ?? "n/a"}`);
1002
+ }
1003
+
1004
+ const {block, executionPayloadValue, consensusBlockValue} = bestResult.value;
1005
+
1006
+ metrics?.blockProductionSuccess.inc({source});
1007
+ metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
1008
+ metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
1009
+ metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
1010
+
1011
+ const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
1012
+ logger.verbose("Produced block", {
1013
+ ...logCtx,
1014
+ executionPayloadValue,
1015
+ consensusBlockValue,
1016
+ root: blockRoot,
1017
+ });
1018
+ if (chain.opts.persistProducedBlocks) {
1019
+ void chain.persistBlock(block, "produced_engine_block");
975
1020
  }
1021
+
1022
+ return {
1023
+ data: block as gloas.BeaconBlock,
1024
+ meta: {version: fork, consensusBlockValue},
1025
+ };
976
1026
  },
977
1027
 
978
1028
  async produceAttestationData({committeeIndex, slot}) {
@@ -1060,15 +1110,34 @@ export function getValidatorApi(
1060
1110
  notWhileSyncing();
1061
1111
  await waitForSlot(slot);
1062
1112
 
1063
- const block = chain.forkChoice.getCanonicalBlockClosestLteSlot(slot);
1113
+ const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
1064
1114
  if (!block) {
1065
- throw new ApiError(404, `No canonical block found at or before slot=${slot}`);
1115
+ // No block is seen at slot. Return 404 so vc can skip casting payload attestation.
1116
+ throw new ApiError(404, `No canonical block found at slot=${slot}`);
1066
1117
  }
1067
1118
 
1068
- const blockIsForSlot = block.slot === slot;
1069
1119
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
1070
- const payloadPresent = blockIsForSlot && (payloadInput?.hasPayloadEnvelope() ?? false);
1071
- const blobDataAvailable = blockIsForSlot && (payloadInput?.hasAllData() ?? false);
1120
+ // Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
1121
+ // into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
1122
+ // the input's creation time.
1123
+ const payloadDueSec = config.getPayloadDueMs() / 1000;
1124
+ const payloadSeenSec =
1125
+ payloadInput?.hasPayloadEnvelope() === true
1126
+ ? chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec)
1127
+ : null;
1128
+ const payloadPresent = payloadSeenSec !== null && payloadSeenSec < payloadDueSec;
1129
+ const blobDataAvailable = payloadInput?.hasAllData() === true;
1130
+
1131
+ logger.debug("Produced payload attestation data", {
1132
+ slot,
1133
+ blockRoot: block.blockRoot,
1134
+ blockSlot: block.slot,
1135
+ payloadPresent,
1136
+ blobDataAvailable,
1137
+ hasPayloadInput: payloadInput !== undefined,
1138
+ payloadSeenSec,
1139
+ payloadDueSec,
1140
+ });
1072
1141
 
1073
1142
  return {
1074
1143
  data: {
@@ -30,7 +30,6 @@ export enum ArchiveStoreTask {
30
30
  PruneHistory = "prune_history",
31
31
  OnFinalizedCheckpoint = "on_finalized_checkpoint",
32
32
  MaybeArchiveState = "maybe_archive_state",
33
- RegenPruneOnFinalized = "regen_prune_on_finalized",
34
33
  ForkchoicePrune = "forkchoice_prune",
35
34
  UpdateBackfillRange = "update_backfill_range",
36
35
  }
@@ -229,10 +228,6 @@ export class ArchiveStore {
229
228
  await this.statesArchiverStrategy.maybeArchiveState(finalized, this.metrics);
230
229
  timer?.({source: ArchiveStoreTask.MaybeArchiveState});
231
230
 
232
- timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
233
- this.chain.regen.pruneOnFinalized(finalizedEpoch);
234
- timer?.({source: ArchiveStoreTask.RegenPruneOnFinalized});
235
-
236
231
  // tasks rely on extended fork choice
237
232
  timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
238
233
  const prunedBlocks = this.chain.forkChoice.prune(finalized.rootHex);
@@ -116,13 +116,19 @@ export async function importBlock(
116
116
  }
117
117
  executionStatus = parentBlock.executionStatus;
118
118
  }
119
+
120
+ // getBeaconProposerOrNull will return null if head state is more than one epoch away
121
+ // from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
122
+ const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
123
+
119
124
  const blockSummary = this.forkChoice.onBlock(
120
125
  block.message,
121
126
  postState,
122
127
  blockDelaySec,
123
128
  currentSlot,
124
129
  executionStatus,
125
- dataAvailabilityStatus
130
+ dataAvailabilityStatus,
131
+ expectedProposerIndex
126
132
  );
127
133
 
128
134
  // This adds the state necessary to process the next block
@@ -257,8 +263,10 @@ export async function importBlock(
257
263
  if (ptcIndices.length > 0) {
258
264
  this.forkChoice.notifyPtcMessages(
259
265
  toRootHex(payloadAttestation.data.beaconBlockRoot),
266
+ payloadAttestation.data.slot,
260
267
  ptcIndices,
261
- payloadAttestation.data.payloadPresent
268
+ payloadAttestation.data.payloadPresent,
269
+ payloadAttestation.data.blobDataAvailable
262
270
  );
263
271
  }
264
272
  } catch (e) {
@@ -129,7 +129,7 @@ export async function importExecutionPayload(
129
129
 
130
130
  // 3. Regenerate state for envelope verification
131
131
  const blockState = await this.regen
132
- .getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.processBlock)
132
+ .getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.importExecutionPayload)
133
133
  .catch(() =>
134
134
  // only happen at the 1st batch of skipped slot checkpoint sync
135
135
  this.regen.getClosestHeadState(protoBlock)
@@ -237,6 +237,7 @@ export async function importExecutionPayload(
237
237
  blockRootHex,
238
238
  blockHashHex,
239
239
  envelope.payload.blockNumber,
240
+ envelope.payload.gasLimit,
240
241
  execStatus,
241
242
  dataAvailabilityStatus
242
243
  );
@@ -254,7 +255,11 @@ export async function importExecutionPayload(
254
255
  }
255
256
 
256
257
  // 8. Record metrics for payload envelope and column sources
257
- this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
258
+ const delaySec = this.clock.secFromSlot(slot);
259
+ this.metrics?.importPayload.elapsedTimeTillImported.observe(
260
+ {source: payloadInput.getPayloadEnvelopeSource().source},
261
+ delaySec
262
+ );
258
263
  for (const {source} of payloadInput.getSampledColumnsWithSource()) {
259
264
  this.metrics?.importPayload.columnsBySource.inc({source});
260
265
  }
@@ -275,6 +280,7 @@ export async function importExecutionPayload(
275
280
  builderIndex: envelope.builderIndex,
276
281
  blockRoot: blockRootHex,
277
282
  blockHash: blockHashHex,
283
+ delaySec,
278
284
  });
279
285
  }
280
286
 
@@ -1049,6 +1049,7 @@ export class BeaconChain implements IBeaconChain {
1049
1049
  feeRecipient,
1050
1050
  commonBlockBodyPromise,
1051
1051
  parentBlock,
1052
+ builderBid,
1052
1053
  }: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
1053
1054
  ): Promise<{
1054
1055
  block: AssembledBlockType<T>;
@@ -1078,6 +1079,7 @@ export class BeaconChain implements IBeaconChain {
1078
1079
  proposerIndex,
1079
1080
  proposerPubKey,
1080
1081
  commonBlockBodyPromise,
1082
+ builderBid,
1081
1083
  }
1082
1084
  );
1083
1085
 
@@ -9,7 +9,9 @@ export enum ExecutionPayloadBidErrorCode {
9
9
  BID_TOO_HIGH = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_HIGH",
10
10
  TOO_MANY_KZG_COMMITMENTS = "EXECUTION_PAYLOAD_BID_ERROR_TOO_MANY_KZG_COMMITMENTS",
11
11
  UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
12
+ UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
12
13
  INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
14
+ NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
13
15
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
14
16
  NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
15
17
  PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
@@ -38,7 +40,9 @@ export type ExecutionPayloadBidErrorType =
38
40
  commitmentLimit: number;
39
41
  }
40
42
  | {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
43
+ | {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
41
44
  | {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
45
+ | {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
42
46
  | {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
43
47
  | {
44
48
  code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
@@ -56,7 +60,8 @@ export type ExecutionPayloadBidErrorType =
56
60
  code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_GAS_LIMIT_MISMATCH;
57
61
  builderIndex: BuilderIndex;
58
62
  bidGasLimit: number;
59
- expectedGasLimit: number;
63
+ parentGasLimit: number;
64
+ targetGasLimit: number;
60
65
  };
61
66
 
62
67
  export class ExecutionPayloadBidError extends GossipActionError<ExecutionPayloadBidErrorType> {}
@@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode {
5
5
  NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT",
6
6
  PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN",
7
7
  UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT",
8
+ INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT",
8
9
  INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK",
9
10
  INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER",
10
11
  INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE",
@@ -18,6 +19,7 @@ export type PayloadAttestationErrorType =
18
19
  blockRoot: RootHex;
19
20
  }
20
21
  | {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex}
22
+ | {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot}
21
23
  | {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex}
22
24
  | {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex}
23
25
  | {code: PayloadAttestationErrorCode.INVALID_SIGNATURE};