@lodestar/beacon-node 1.41.0-dev.36a9b46d5f → 1.41.0-dev.3b98c59c64

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 (222) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +121 -3
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/index.js +8 -8
  5. package/lib/api/impl/beacon/state/index.js.map +1 -1
  6. package/lib/api/impl/beacon/state/utils.d.ts +3 -4
  7. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/state/utils.js +5 -24
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/debug/index.d.ts.map +1 -1
  11. package/lib/api/impl/debug/index.js +5 -2
  12. package/lib/api/impl/debug/index.js.map +1 -1
  13. package/lib/api/impl/validator/index.d.ts.map +1 -1
  14. package/lib/api/impl/validator/index.js +104 -6
  15. package/lib/api/impl/validator/index.js.map +1 -1
  16. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  17. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/archiveStore.js +9 -0
  19. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  20. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +5 -6
  21. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +9 -10
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  24. package/lib/chain/archiveStore/historicalState/worker.js +3 -3
  25. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  26. package/lib/chain/archiveStore/utils/archivePayloads.d.ts +7 -0
  27. package/lib/chain/archiveStore/utils/archivePayloads.d.ts.map +1 -0
  28. package/lib/chain/archiveStore/utils/archivePayloads.js +10 -0
  29. package/lib/chain/archiveStore/utils/archivePayloads.js.map +1 -0
  30. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  31. package/lib/chain/blocks/importBlock.js +0 -2
  32. package/lib/chain/blocks/importBlock.js.map +1 -1
  33. package/lib/chain/blocks/index.d.ts.map +1 -1
  34. package/lib/chain/blocks/index.js +2 -1
  35. package/lib/chain/blocks/index.js.map +1 -1
  36. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  37. package/lib/chain/blocks/writeBlockInputToDb.js +3 -0
  38. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  39. package/lib/chain/bls/multithread/index.d.ts +3 -3
  40. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  41. package/lib/chain/bls/multithread/index.js +5 -5
  42. package/lib/chain/bls/multithread/index.js.map +1 -1
  43. package/lib/chain/bls/multithread/jobItem.d.ts +2 -2
  44. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  45. package/lib/chain/bls/multithread/jobItem.js +2 -2
  46. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  47. package/lib/chain/bls/singleThread.d.ts +4 -4
  48. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  49. package/lib/chain/bls/singleThread.js +4 -4
  50. package/lib/chain/bls/singleThread.js.map +1 -1
  51. package/lib/chain/bls/utils.d.ts +2 -2
  52. package/lib/chain/bls/utils.d.ts.map +1 -1
  53. package/lib/chain/bls/utils.js +7 -4
  54. package/lib/chain/bls/utils.js.map +1 -1
  55. package/lib/chain/chain.d.ts +6 -9
  56. package/lib/chain/chain.d.ts.map +1 -1
  57. package/lib/chain/chain.js +32 -16
  58. package/lib/chain/chain.js.map +1 -1
  59. package/lib/chain/emitter.d.ts +2 -2
  60. package/lib/chain/emitter.d.ts.map +1 -1
  61. package/lib/chain/interface.d.ts +4 -6
  62. package/lib/chain/interface.d.ts.map +1 -1
  63. package/lib/chain/interface.js.map +1 -1
  64. package/lib/chain/options.d.ts.map +1 -1
  65. package/lib/chain/options.js.map +1 -1
  66. package/lib/chain/prepareNextSlot.js +3 -3
  67. package/lib/chain/prepareNextSlot.js.map +1 -1
  68. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +10 -2
  69. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  70. package/lib/chain/produceBlock/computeNewStateRoot.js +24 -2
  71. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  72. package/lib/chain/produceBlock/produceBlockBody.d.ts +22 -7
  73. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  74. package/lib/chain/produceBlock/produceBlockBody.js +110 -10
  75. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  76. package/lib/chain/validation/attestation.d.ts.map +1 -1
  77. package/lib/chain/validation/attestation.js +4 -1
  78. package/lib/chain/validation/attestation.js.map +1 -1
  79. package/lib/chain/validation/attesterSlashing.js +1 -1
  80. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  81. package/lib/chain/validation/dataColumnSidecar.d.ts +2 -2
  82. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  83. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  84. package/lib/chain/validation/payloadAttestationMessage.js +8 -1
  85. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  86. package/lib/chain/validation/proposerSlashing.js +1 -1
  87. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  88. package/lib/chain/validation/syncCommitteeContributionAndProof.js +1 -1
  89. package/lib/db/beacon.d.ts +3 -1
  90. package/lib/db/beacon.d.ts.map +1 -1
  91. package/lib/db/beacon.js +5 -1
  92. package/lib/db/beacon.js.map +1 -1
  93. package/lib/db/buckets.d.ts +3 -1
  94. package/lib/db/buckets.d.ts.map +1 -1
  95. package/lib/db/buckets.js +2 -0
  96. package/lib/db/buckets.js.map +1 -1
  97. package/lib/db/interface.d.ts +3 -1
  98. package/lib/db/interface.d.ts.map +1 -1
  99. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  100. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  101. package/lib/db/repositories/dataColumnSidecar.d.ts +5 -3
  102. package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
  103. package/lib/db/repositories/dataColumnSidecar.js +14 -1
  104. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  105. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +5 -3
  106. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  107. package/lib/db/repositories/dataColumnSidecarArchive.js +14 -1
  108. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  109. package/lib/db/repositories/executionPayloadEnvelope.d.ts +19 -0
  110. package/lib/db/repositories/executionPayloadEnvelope.d.ts.map +1 -0
  111. package/lib/db/repositories/executionPayloadEnvelope.js +22 -0
  112. package/lib/db/repositories/executionPayloadEnvelope.js.map +1 -0
  113. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts +18 -0
  114. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts.map +1 -0
  115. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +28 -0
  116. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -0
  117. package/lib/db/repositories/index.d.ts +2 -0
  118. package/lib/db/repositories/index.d.ts.map +1 -1
  119. package/lib/db/repositories/index.js +2 -0
  120. package/lib/db/repositories/index.js.map +1 -1
  121. package/lib/metrics/metrics/beacon.d.ts +1 -0
  122. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  123. package/lib/metrics/metrics/beacon.js +5 -0
  124. package/lib/metrics/metrics/beacon.js.map +1 -1
  125. package/lib/metrics/metrics/lodestar.d.ts +5 -0
  126. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  127. package/lib/metrics/metrics/lodestar.js +9 -0
  128. package/lib/metrics/metrics/lodestar.js.map +1 -1
  129. package/lib/monitoring/service.d.ts +2 -2
  130. package/lib/monitoring/service.d.ts.map +1 -1
  131. package/lib/monitoring/service.js +3 -2
  132. package/lib/monitoring/service.js.map +1 -1
  133. package/lib/network/gossip/interface.d.ts +3 -3
  134. package/lib/network/gossip/interface.d.ts.map +1 -1
  135. package/lib/network/gossip/topic.d.ts +113 -63
  136. package/lib/network/gossip/topic.d.ts.map +1 -1
  137. package/lib/network/gossip/topic.js +2 -2
  138. package/lib/network/gossip/topic.js.map +1 -1
  139. package/lib/network/interface.d.ts +3 -2
  140. package/lib/network/interface.d.ts.map +1 -1
  141. package/lib/network/network.d.ts +3 -2
  142. package/lib/network/network.d.ts.map +1 -1
  143. package/lib/network/network.js +10 -1
  144. package/lib/network/network.js.map +1 -1
  145. package/lib/network/peers/peerManager.d.ts +2 -0
  146. package/lib/network/peers/peerManager.d.ts.map +1 -1
  147. package/lib/network/peers/peerManager.js +54 -26
  148. package/lib/network/peers/peerManager.js.map +1 -1
  149. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  150. package/lib/network/processor/gossipHandlers.js +5 -1
  151. package/lib/network/processor/gossipHandlers.js.map +1 -1
  152. package/lib/node/nodejs.d.ts +3 -5
  153. package/lib/node/nodejs.d.ts.map +1 -1
  154. package/lib/node/nodejs.js +6 -4
  155. package/lib/node/nodejs.js.map +1 -1
  156. package/lib/util/blobs.d.ts +2 -2
  157. package/lib/util/blobs.d.ts.map +1 -1
  158. package/lib/util/blobs.js.map +1 -1
  159. package/lib/util/dataColumns.d.ts +11 -3
  160. package/lib/util/dataColumns.d.ts.map +1 -1
  161. package/lib/util/dataColumns.js +27 -0
  162. package/lib/util/dataColumns.js.map +1 -1
  163. package/lib/util/multifork.d.ts +8 -0
  164. package/lib/util/multifork.d.ts.map +1 -1
  165. package/lib/util/multifork.js +37 -0
  166. package/lib/util/multifork.js.map +1 -1
  167. package/lib/util/serializedCache.d.ts +5 -0
  168. package/lib/util/serializedCache.d.ts.map +1 -1
  169. package/lib/util/serializedCache.js +5 -0
  170. package/lib/util/serializedCache.js.map +1 -1
  171. package/package.json +15 -15
  172. package/src/api/impl/beacon/blocks/index.ts +145 -2
  173. package/src/api/impl/beacon/state/index.ts +8 -8
  174. package/src/api/impl/beacon/state/utils.ts +15 -29
  175. package/src/api/impl/debug/index.ts +8 -5
  176. package/src/api/impl/validator/index.ts +127 -5
  177. package/src/chain/archiveStore/archiveStore.ts +10 -0
  178. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +10 -11
  179. package/src/chain/archiveStore/historicalState/worker.ts +3 -3
  180. package/src/chain/archiveStore/utils/archivePayloads.ts +15 -0
  181. package/src/chain/blocks/importBlock.ts +0 -3
  182. package/src/chain/blocks/index.ts +2 -1
  183. package/src/chain/blocks/writeBlockInputToDb.ts +3 -0
  184. package/src/chain/bls/multithread/index.ts +7 -7
  185. package/src/chain/bls/multithread/jobItem.ts +3 -3
  186. package/src/chain/bls/singleThread.ts +5 -5
  187. package/src/chain/bls/utils.ts +8 -5
  188. package/src/chain/chain.ts +51 -26
  189. package/src/chain/emitter.ts +2 -2
  190. package/src/chain/interface.ts +4 -11
  191. package/src/chain/options.ts +1 -0
  192. package/src/chain/prepareNextSlot.ts +5 -5
  193. package/src/chain/produceBlock/computeNewStateRoot.ts +35 -3
  194. package/src/chain/produceBlock/produceBlockBody.ts +163 -13
  195. package/src/chain/validation/attestation.ts +4 -1
  196. package/src/chain/validation/attesterSlashing.ts +1 -1
  197. package/src/chain/validation/dataColumnSidecar.ts +2 -5
  198. package/src/chain/validation/payloadAttestationMessage.ts +9 -1
  199. package/src/chain/validation/proposerSlashing.ts +1 -1
  200. package/src/chain/validation/syncCommitteeContributionAndProof.ts +1 -1
  201. package/src/db/beacon.ts +8 -0
  202. package/src/db/buckets.ts +3 -0
  203. package/src/db/interface.ts +5 -0
  204. package/src/db/repositories/dataColumnSidecar.ts +18 -3
  205. package/src/db/repositories/dataColumnSidecarArchive.ts +18 -3
  206. package/src/db/repositories/executionPayloadEnvelope.ts +26 -0
  207. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +32 -0
  208. package/src/db/repositories/index.ts +2 -0
  209. package/src/metrics/metrics/beacon.ts +5 -0
  210. package/src/metrics/metrics/lodestar.ts +9 -0
  211. package/src/monitoring/service.ts +3 -2
  212. package/src/network/gossip/interface.ts +3 -3
  213. package/src/network/gossip/topic.ts +2 -1
  214. package/src/network/interface.ts +4 -1
  215. package/src/network/network.ts +21 -3
  216. package/src/network/peers/peerManager.ts +65 -27
  217. package/src/network/processor/gossipHandlers.ts +7 -1
  218. package/src/node/nodejs.ts +8 -9
  219. package/src/util/blobs.ts +3 -3
  220. package/src/util/dataColumns.ts +37 -1
  221. package/src/util/multifork.ts +45 -0
  222. package/src/util/serializedCache.ts +5 -0
@@ -10,7 +10,7 @@ import {
10
10
  getBlockHeaderProposerSignatureSetByHeaderSlot,
11
11
  getBlockHeaderProposerSignatureSetByParentStateSlot,
12
12
  } from "@lodestar/state-transition";
13
- import {Root, Slot, SubnetID, fulu, ssz} from "@lodestar/types";
13
+ import {DataColumnSidecar, Root, Slot, SubnetID, fulu, ssz} from "@lodestar/types";
14
14
  import {byteArrayEquals, toRootHex, verifyMerkleBranch} from "@lodestar/utils";
15
15
  import {Metrics} from "../../metrics/metrics.js";
16
16
  import {kzg} from "../../util/kzg.js";
@@ -457,9 +457,6 @@ export async function validateBlockDataColumnSidecars(
457
457
  * SPEC FUNCTION
458
458
  * https://github.com/ethereum/consensus-specs/blob/v1.6.0-alpha.4/specs/fulu/p2p-interface.md#compute_subnet_for_data_column_sidecar
459
459
  */
460
- export function computeSubnetForDataColumnSidecar(
461
- config: ChainConfig,
462
- columnSidecar: fulu.DataColumnSidecar
463
- ): SubnetID {
460
+ export function computeSubnetForDataColumnSidecar(config: ChainConfig, columnSidecar: DataColumnSidecar): SubnetID {
464
461
  return columnSidecar.index % config.DATA_COLUMN_SIDECAR_SUBNET_COUNT;
465
462
  }
@@ -87,8 +87,16 @@ async function validatePayloadAttestationMessage(
87
87
  }
88
88
 
89
89
  // [REJECT] `payload_attestation_message.signature` is valid with respect to the validator's public key.
90
+ const validatorPubkey = chain.pubkeyCache.get(validatorIndex);
91
+ if (!validatorPubkey) {
92
+ throw new PayloadAttestationError(GossipAction.REJECT, {
93
+ code: PayloadAttestationErrorCode.INVALID_ATTESTER,
94
+ attesterIndex: validatorIndex,
95
+ });
96
+ }
97
+
90
98
  const signatureSet = createSingleSignatureSetFromComponents(
91
- chain.index2pubkey[validatorIndex],
99
+ validatorPubkey,
92
100
  getPayloadAttestationDataSigningRoot(chain.config, data),
93
101
  payloadAttestationMessage.signature
94
102
  );
@@ -37,7 +37,7 @@ async function validateProposerSlashing(
37
37
  try {
38
38
  const proposer = state.validators.getReadonly(proposerSlashing.signedHeader1.message.proposerIndex);
39
39
  // verifySignature = false, verified in batch below
40
- assertValidProposerSlashing(chain.config, chain.index2pubkey, state.slot, proposerSlashing, proposer, false);
40
+ assertValidProposerSlashing(chain.config, chain.pubkeyCache, state.slot, proposerSlashing, proposer, false);
41
41
  } catch (e) {
42
42
  throw new ProposerSlashingError(GossipAction.REJECT, {
43
43
  code: ProposerSlashingErrorCode.INVALID,
@@ -106,7 +106,7 @@ export async function validateSyncCommitteeGossipContributionAndProof(
106
106
  /**
107
107
  * Retrieve pubkeys in contribution aggregate using epochCtx:
108
108
  * - currSyncCommitteeIndexes cache
109
- * - index2pubkey cache
109
+ * - pubkeyCache
110
110
  */
111
111
  function getContributionIndices(
112
112
  state: CachedBeaconStateAltair,
package/src/db/beacon.ts CHANGED
@@ -15,6 +15,8 @@ import {
15
15
  CheckpointHeaderRepository,
16
16
  DataColumnSidecarArchiveRepository,
17
17
  DataColumnSidecarRepository,
18
+ ExecutionPayloadEnvelopeArchiveRepository,
19
+ ExecutionPayloadEnvelopeRepository,
18
20
  ProposerSlashingRepository,
19
21
  StateArchiveRepository,
20
22
  SyncCommitteeRepository,
@@ -36,6 +38,9 @@ export class BeaconDb implements IBeaconDb {
36
38
  dataColumnSidecar: DataColumnSidecarRepository;
37
39
  dataColumnSidecarArchive: DataColumnSidecarArchiveRepository;
38
40
 
41
+ executionPayloadEnvelope: ExecutionPayloadEnvelopeRepository;
42
+ executionPayloadEnvelopeArchive: ExecutionPayloadEnvelopeArchiveRepository;
43
+
39
44
  stateArchive: StateArchiveRepository;
40
45
  checkpointState: CheckpointStateRepository;
41
46
 
@@ -65,6 +70,9 @@ export class BeaconDb implements IBeaconDb {
65
70
  this.dataColumnSidecar = new DataColumnSidecarRepository(config, db);
66
71
  this.dataColumnSidecarArchive = new DataColumnSidecarArchiveRepository(config, db);
67
72
 
73
+ this.executionPayloadEnvelope = new ExecutionPayloadEnvelopeRepository(config, db);
74
+ this.executionPayloadEnvelopeArchive = new ExecutionPayloadEnvelopeArchiveRepository(config, db);
75
+
68
76
  this.stateArchive = new StateArchiveRepository(config, db);
69
77
  this.checkpointState = new CheckpointStateRepository(config, db);
70
78
  this.voluntaryExit = new VoluntaryExitRepository(config, db);
package/src/db/buckets.ts CHANGED
@@ -69,6 +69,9 @@ export enum Bucket {
69
69
 
70
70
  fulu_dataColumnSidecars = 57, // FULU BeaconBlockRoot -> DataColumnSidecars
71
71
  fulu_dataColumnSidecarsArchive = 58, // FULU BeaconBlockSlot -> DataColumnSidecars
72
+
73
+ gloas_executionPayloadEnvelope = 59, // GLOAS BeaconBlockRoot -> SignedExecutionPayloadEnvelope
74
+ gloas_executionPayloadEnvelopeArchive = 60, // GLOAS Slot -> SignedExecutionPayloadEnvelope
72
75
  }
73
76
 
74
77
  export function getBucketNameByValue<T extends Bucket>(enumValue: T): keyof typeof Bucket {
@@ -12,6 +12,8 @@ import {
12
12
  CheckpointHeaderRepository,
13
13
  DataColumnSidecarArchiveRepository,
14
14
  DataColumnSidecarRepository,
15
+ ExecutionPayloadEnvelopeArchiveRepository,
16
+ ExecutionPayloadEnvelopeRepository,
15
17
  ProposerSlashingRepository,
16
18
  StateArchiveRepository,
17
19
  SyncCommitteeRepository,
@@ -35,6 +37,9 @@ export interface IBeaconDb {
35
37
  dataColumnSidecar: DataColumnSidecarRepository;
36
38
  dataColumnSidecarArchive: DataColumnSidecarArchiveRepository;
37
39
 
40
+ executionPayloadEnvelope: ExecutionPayloadEnvelopeRepository;
41
+ executionPayloadEnvelopeArchive: ExecutionPayloadEnvelopeArchiveRepository;
42
+
38
43
  // finalized states
39
44
  stateArchive: StateArchiveRepository;
40
45
  // checkpoint states
@@ -1,7 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Db, PrefixedRepository, decodeNumberForDbKey, encodeNumberForDbKey} from "@lodestar/db";
3
3
  import {NUMBER_OF_COLUMNS} from "@lodestar/params";
4
- import {ColumnIndex, Root, fulu, ssz} from "@lodestar/types";
4
+ import {ColumnIndex, DataColumnSidecar, Root, isGloasDataColumnSidecar, ssz} from "@lodestar/types";
5
+ import {isGloasDataColumnSidecarBytes} from "../../util/multifork.js";
5
6
  import {Bucket, getBucketNameByValue} from "../buckets.js";
6
7
 
7
8
  const COLUMN_INDEX_BYTE_SIZE = 2;
@@ -15,7 +16,7 @@ type BlockRoot = Root;
15
16
  *
16
17
  * Indexed data by `blockRoot` + `columnIndex`
17
18
  */
18
- export class DataColumnSidecarRepository extends PrefixedRepository<BlockRoot, ColumnIndex, fulu.DataColumnSidecar> {
19
+ export class DataColumnSidecarRepository extends PrefixedRepository<BlockRoot, ColumnIndex, DataColumnSidecar> {
19
20
  constructor(config: ChainForkConfig, db: Db) {
20
21
  const bucket = Bucket.fulu_dataColumnSidecars;
21
22
  super(config, db, bucket, ssz.fulu.DataColumnSidecar, getBucketNameByValue(bucket));
@@ -24,10 +25,24 @@ export class DataColumnSidecarRepository extends PrefixedRepository<BlockRoot, C
24
25
  /**
25
26
  * Id is hashTreeRoot of unsigned BeaconBlock
26
27
  */
27
- getId(value: fulu.DataColumnSidecar): ColumnIndex {
28
+ getId(value: DataColumnSidecar): ColumnIndex {
28
29
  return value.index;
29
30
  }
30
31
 
32
+ encodeValue(value: DataColumnSidecar): Uint8Array {
33
+ if (isGloasDataColumnSidecar(value)) {
34
+ return ssz.gloas.DataColumnSidecar.serialize(value);
35
+ }
36
+ return ssz.fulu.DataColumnSidecar.serialize(value);
37
+ }
38
+
39
+ decodeValue(data: Uint8Array): DataColumnSidecar {
40
+ if (isGloasDataColumnSidecarBytes(data)) {
41
+ return ssz.gloas.DataColumnSidecar.deserialize(data);
42
+ }
43
+ return ssz.fulu.DataColumnSidecar.deserialize(data);
44
+ }
45
+
31
46
  encodeKeyRaw(prefix: BlockRoot, id: ColumnIndex): Uint8Array {
32
47
  return Buffer.concat([prefix, encodeNumberForDbKey(id, COLUMN_INDEX_BYTE_SIZE)]);
33
48
  }
@@ -1,7 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Db, PrefixedRepository, decodeNumberForDbKey, encodeNumberForDbKey} from "@lodestar/db";
3
3
  import {NUMBER_OF_COLUMNS} from "@lodestar/params";
4
- import {ColumnIndex, Slot, fulu, ssz} from "@lodestar/types";
4
+ import {ColumnIndex, DataColumnSidecar, Slot, isGloasDataColumnSidecar, ssz} from "@lodestar/types";
5
+ import {isGloasDataColumnSidecarBytes} from "../../util/multifork.js";
5
6
  import {Bucket, getBucketNameByValue} from "../buckets.js";
6
7
 
7
8
  const COLUMN_INDEX_BYTE_SIZE = 2;
@@ -13,7 +14,7 @@ const SLOT_BYTE_SIZE = 8;
13
14
  *
14
15
  * Indexed data by `slot` + `columnIndex`
15
16
  */
16
- export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot, ColumnIndex, fulu.DataColumnSidecar> {
17
+ export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot, ColumnIndex, DataColumnSidecar> {
17
18
  constructor(config: ChainForkConfig, db: Db) {
18
19
  const bucket = Bucket.fulu_dataColumnSidecarsArchive;
19
20
  super(config, db, bucket, ssz.fulu.DataColumnSidecar, getBucketNameByValue(bucket));
@@ -22,10 +23,24 @@ export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot,
22
23
  /**
23
24
  * Id is hashTreeRoot of unsigned BeaconBlock
24
25
  */
25
- getId(value: fulu.DataColumnSidecar): ColumnIndex {
26
+ getId(value: DataColumnSidecar): ColumnIndex {
26
27
  return value.index;
27
28
  }
28
29
 
30
+ encodeValue(value: DataColumnSidecar): Uint8Array {
31
+ if (isGloasDataColumnSidecar(value)) {
32
+ return ssz.gloas.DataColumnSidecar.serialize(value);
33
+ }
34
+ return ssz.fulu.DataColumnSidecar.serialize(value);
35
+ }
36
+
37
+ decodeValue(data: Uint8Array): DataColumnSidecar {
38
+ if (isGloasDataColumnSidecarBytes(data)) {
39
+ return ssz.gloas.DataColumnSidecar.deserialize(data);
40
+ }
41
+ return ssz.fulu.DataColumnSidecar.deserialize(data);
42
+ }
43
+
29
44
  encodeKeyRaw(prefix: Slot, id: ColumnIndex): Uint8Array {
30
45
  return Buffer.concat([
31
46
  encodeNumberForDbKey(prefix, SLOT_BYTE_SIZE),
@@ -0,0 +1,26 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {Db, Repository} from "@lodestar/db";
3
+ import {Root, gloas, ssz} from "@lodestar/types";
4
+ import {Bucket, getBucketNameByValue} from "../buckets.js";
5
+
6
+ type BlockRoot = Root;
7
+
8
+ /**
9
+ * Used to store unfinalized `SignedExecutionPayloadEnvelope`
10
+ *
11
+ * Indexed by beacon block root (root of the beacon block that contains the bid)
12
+ */
13
+ export class ExecutionPayloadEnvelopeRepository extends Repository<BlockRoot, gloas.SignedExecutionPayloadEnvelope> {
14
+ constructor(config: ChainForkConfig, db: Db) {
15
+ const bucket = Bucket.gloas_executionPayloadEnvelope;
16
+ super(config, db, bucket, ssz.gloas.SignedExecutionPayloadEnvelope, getBucketNameByValue(bucket));
17
+ }
18
+
19
+ /**
20
+ * Id is the beacon block root (not execution payload hash)
21
+ * This allows correlation with the block that contains the bid
22
+ */
23
+ getId(value: gloas.SignedExecutionPayloadEnvelope): BlockRoot {
24
+ return value.message.beaconBlockRoot;
25
+ }
26
+ }
@@ -0,0 +1,32 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {BUCKET_LENGTH, Db, Repository, encodeKey as encodeDbKey} from "@lodestar/db";
3
+ import {Slot, gloas, ssz} from "@lodestar/types";
4
+ import {bytesToInt} from "@lodestar/utils";
5
+ import {Bucket, getBucketNameByValue} from "../buckets.js";
6
+
7
+ /**
8
+ * Used to store finalized `SignedExecutionPayloadEnvelope`
9
+ *
10
+ * Indexed by slot for chronological archival
11
+ */
12
+ export class ExecutionPayloadEnvelopeArchiveRepository extends Repository<Slot, gloas.SignedExecutionPayloadEnvelope> {
13
+ constructor(config: ChainForkConfig, db: Db) {
14
+ const bucket = Bucket.gloas_executionPayloadEnvelopeArchive;
15
+ super(config, db, bucket, ssz.gloas.SignedExecutionPayloadEnvelope, getBucketNameByValue(bucket));
16
+ }
17
+
18
+ /**
19
+ * Id is the slot from the envelope
20
+ */
21
+ getId(value: gloas.SignedExecutionPayloadEnvelope): Slot {
22
+ return value.message.slot;
23
+ }
24
+
25
+ encodeKey(id: Slot): Uint8Array {
26
+ return encodeDbKey(this.bucket, id);
27
+ }
28
+
29
+ decodeKey(data: Uint8Array): number {
30
+ return bytesToInt(data.subarray(BUCKET_LENGTH), "be");
31
+ }
32
+ }
@@ -8,6 +8,8 @@ export {BlockArchiveRepository} from "./blockArchive.js";
8
8
  export {BLSToExecutionChangeRepository} from "./blsToExecutionChange.js";
9
9
  export {DataColumnSidecarRepository} from "./dataColumnSidecar.js";
10
10
  export {DataColumnSidecarArchiveRepository} from "./dataColumnSidecarArchive.js";
11
+ export {ExecutionPayloadEnvelopeRepository} from "./executionPayloadEnvelope.js";
12
+ export {ExecutionPayloadEnvelopeArchiveRepository} from "./executionPayloadEnvelopeArchive.js";
11
13
  export {BestLightClientUpdateRepository} from "./lightclientBestUpdate.js";
12
14
  export {CheckpointHeaderRepository} from "./lightclientCheckpointHeader.js";
13
15
  export {SyncCommitteeRepository} from "./lightclientSyncCommittee.js";
@@ -144,6 +144,11 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
144
144
  help: "Time for preparing payload in advance",
145
145
  buckets: [0.1, 1, 3, 5, 10],
146
146
  }),
147
+ executionPayloadEnvelopeProcessingTime: register.histogram({
148
+ name: "beacon_block_payload_envelope_processing_seconds",
149
+ help: "Time to process execution payload envelope during block production",
150
+ buckets: [0.005, 0.01, 0.05, 0.1, 0.2, 0.5, 1],
151
+ }),
147
152
  payloadFetchedTime: register.histogram<{prepType: PayloadPreparationType}>({
148
153
  name: "beacon_block_payload_fetched_time",
149
154
  help: "Time to fetch the payload from EL",
@@ -827,6 +827,15 @@ export function createLodestarMetrics(
827
827
  help: "Total number of blobs retrieved from execution engine and published to gossip",
828
828
  }),
829
829
  },
830
+ // Gossip execution payload envelope
831
+ gossipExecutionPayloadEnvelope: {
832
+ elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
833
+ name: "lodestar_gossip_execution_payload_envelope_elapsed_time_till_received",
834
+ help: "Time elapsed between slot time and the time execution payload envelope received",
835
+ labelNames: ["source"],
836
+ buckets: [0.5, 1, 2, 4, 6, 12],
837
+ }),
838
+ },
830
839
  recoverDataColumnSidecars: {
831
840
  recoverTime: register.histogram({
832
841
  name: "lodestar_recover_data_column_sidecar_recover_time_seconds",
@@ -89,9 +89,9 @@ export class MonitoringService {
89
89
  }
90
90
 
91
91
  /**
92
- * Stop sending client stats
92
+ * Stop sending client stats and wait for any pending request to complete
93
93
  */
94
- close(): void {
94
+ async close(): Promise<void> {
95
95
  if (this.status === Status.Closed) return;
96
96
  this.status = Status.Closed;
97
97
 
@@ -103,6 +103,7 @@ export class MonitoringService {
103
103
  }
104
104
  if (this.pendingRequest) {
105
105
  this.fetchAbortController?.abort(FetchAbortReason.Close);
106
+ await this.pendingRequest;
106
107
  }
107
108
  }
108
109
 
@@ -4,6 +4,7 @@ import {PeerIdStr} from "@chainsafe/libp2p-gossipsub/types";
4
4
  import {BeaconConfig, ForkBoundary} from "@lodestar/config";
5
5
  import {
6
6
  AttesterSlashing,
7
+ DataColumnSidecar,
7
8
  LightClientFinalityUpdate,
8
9
  LightClientOptimisticUpdate,
9
10
  SignedAggregateAndProof,
@@ -14,7 +15,6 @@ import {
14
15
  altair,
15
16
  capella,
16
17
  deneb,
17
- fulu,
18
18
  gloas,
19
19
  phase0,
20
20
  } from "@lodestar/types";
@@ -98,7 +98,7 @@ export type GossipTypeMap = {
98
98
  [GossipType.blob_sidecar]: deneb.BlobSidecar;
99
99
  [GossipType.beacon_aggregate_and_proof]: SignedAggregateAndProof;
100
100
  [GossipType.beacon_attestation]: SingleAttestation;
101
- [GossipType.data_column_sidecar]: fulu.DataColumnSidecar;
101
+ [GossipType.data_column_sidecar]: DataColumnSidecar;
102
102
  [GossipType.voluntary_exit]: phase0.SignedVoluntaryExit;
103
103
  [GossipType.proposer_slashing]: phase0.ProposerSlashing;
104
104
  [GossipType.attester_slashing]: AttesterSlashing;
@@ -117,7 +117,7 @@ export type GossipFnByType = {
117
117
  [GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise<void> | void;
118
118
  [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: SignedAggregateAndProof) => Promise<void> | void;
119
119
  [GossipType.beacon_attestation]: (attestation: SingleAttestation) => Promise<void> | void;
120
- [GossipType.data_column_sidecar]: (dataColumnSidecar: fulu.DataColumnSidecar) => Promise<void> | void;
120
+ [GossipType.data_column_sidecar]: (dataColumnSidecar: DataColumnSidecar) => Promise<void> | void;
121
121
  [GossipType.voluntary_exit]: (voluntaryExit: phase0.SignedVoluntaryExit) => Promise<void> | void;
122
122
  [GossipType.proposer_slashing]: (proposerSlashing: phase0.ProposerSlashing) => Promise<void> | void;
123
123
  [GossipType.attester_slashing]: (attesterSlashing: AttesterSlashing) => Promise<void> | void;
@@ -6,6 +6,7 @@ import {
6
6
  SYNC_COMMITTEE_SUBNET_COUNT,
7
7
  isForkPostAltair,
8
8
  isForkPostElectra,
9
+ isForkPostFulu,
9
10
  } from "@lodestar/params";
10
11
  import {Attestation, SingleAttestation, ssz, sszTypesFor} from "@lodestar/types";
11
12
  import {GossipAction, GossipActionError, GossipErrorCode} from "../../chain/errors/gossipValidation.js";
@@ -92,7 +93,7 @@ export function getGossipSSZType(topic: GossipTopic) {
92
93
  case GossipType.blob_sidecar:
93
94
  return ssz.deneb.BlobSidecar;
94
95
  case GossipType.data_column_sidecar:
95
- return ssz.fulu.DataColumnSidecar;
96
+ return isForkPostFulu(fork) ? sszTypesFor(fork).DataColumnSidecar : ssz.fulu.DataColumnSidecar;
96
97
  case GossipType.beacon_aggregate_and_proof:
97
98
  return sszTypesFor(fork).SignedAggregateAndProof;
98
99
  case GossipType.beacon_attestation:
@@ -19,6 +19,7 @@ import type {Datastore} from "interface-datastore";
19
19
  import {Libp2p as ILibp2p} from "libp2p";
20
20
  import {
21
21
  AttesterSlashing,
22
+ DataColumnSidecar,
22
23
  LightClientFinalityUpdate,
23
24
  LightClientOptimisticUpdate,
24
25
  SignedAggregateAndProof,
@@ -31,6 +32,7 @@ import {
31
32
  capella,
32
33
  deneb,
33
34
  fulu,
35
+ gloas,
34
36
  phase0,
35
37
  } from "@lodestar/types";
36
38
  import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
@@ -86,7 +88,7 @@ export interface INetwork extends INetworkCorePublic {
86
88
  publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number>;
87
89
  publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number>;
88
90
  publishBeaconAttestation(attestation: SingleAttestation, subnet: SubnetID): Promise<number>;
89
- publishDataColumnSidecar(dataColumnSideCar: fulu.DataColumnSidecar): Promise<number>;
91
+ publishDataColumnSidecar(dataColumnSideCar: DataColumnSidecar): Promise<number>;
90
92
  publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise<number>;
91
93
  publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number>;
92
94
  publishProposerSlashing(proposerSlashing: phase0.ProposerSlashing): Promise<number>;
@@ -95,6 +97,7 @@ export interface INetwork extends INetworkCorePublic {
95
97
  publishContributionAndProof(contributionAndProof: altair.SignedContributionAndProof): Promise<number>;
96
98
  publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
97
99
  publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
100
+ publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
98
101
 
99
102
  // Debug
100
103
  dumpGossipQueue(gossipType: GossipType): Promise<PendingGossipsubMessage[]>;
@@ -10,6 +10,8 @@ import {ResponseIncoming} from "@lodestar/reqresp";
10
10
  import {computeEpochAtSlot} from "@lodestar/state-transition";
11
11
  import {
12
12
  AttesterSlashing,
13
+ DataColumnSidecar,
14
+ DataColumnSidecars,
13
15
  LightClientBootstrap,
14
16
  LightClientFinalityUpdate,
15
17
  LightClientOptimisticUpdate,
@@ -24,6 +26,8 @@ import {
24
26
  capella,
25
27
  deneb,
26
28
  fulu,
29
+ gloas,
30
+ isGloasDataColumnSidecar,
27
31
  phase0,
28
32
  } from "@lodestar/types";
29
33
  import {prettyPrintIndices, sleep} from "@lodestar/utils";
@@ -354,8 +358,11 @@ export class Network implements INetwork {
354
358
  });
355
359
  }
356
360
 
357
- async publishDataColumnSidecar(dataColumnSidecar: fulu.DataColumnSidecar): Promise<number> {
358
- const epoch = computeEpochAtSlot(dataColumnSidecar.signedBlockHeader.message.slot);
361
+ async publishDataColumnSidecar(dataColumnSidecar: DataColumnSidecar): Promise<number> {
362
+ const slot = isGloasDataColumnSidecar(dataColumnSidecar)
363
+ ? dataColumnSidecar.slot
364
+ : dataColumnSidecar.signedBlockHeader.message.slot;
365
+ const epoch = computeEpochAtSlot(slot);
359
366
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
360
367
 
361
368
  const subnet = computeSubnetForDataColumnSidecar(this.config, dataColumnSidecar);
@@ -489,6 +496,17 @@ export class Network implements INetwork {
489
496
  );
490
497
  }
491
498
 
499
+ async publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number> {
500
+ const epoch = computeEpochAtSlot(signedEnvelope.message.slot);
501
+ const boundary = this.config.getForkBoundaryAtEpoch(epoch);
502
+
503
+ return this.publishGossip<GossipType.execution_payload>(
504
+ {type: GossipType.execution_payload, boundary},
505
+ signedEnvelope,
506
+ {ignoreDuplicatePublishError: true}
507
+ );
508
+ }
509
+
492
510
  private async publishGossip<K extends GossipType>(
493
511
  topic: GossipTopicMap[K],
494
512
  object: GossipTypeMap[K],
@@ -765,7 +783,7 @@ export class Network implements INetwork {
765
783
  this.core.setTargetGroupCount(count);
766
784
  };
767
785
 
768
- private onPublishDataColumns = (sidecars: fulu.DataColumnSidecar[]): Promise<number[]> => {
786
+ private onPublishDataColumns = (sidecars: DataColumnSidecars): Promise<number[]> => {
769
787
  return promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishDataColumnSidecar(sidecar)));
770
788
  };
771
789
 
@@ -9,6 +9,7 @@ import {prettyPrintIndices, toHex, withTimeout} from "@lodestar/utils";
9
9
  import {GOODBYE_KNOWN_CODES, GoodByeReasonCode, Libp2pEvent} from "../../constants/index.js";
10
10
  import {IClock} from "../../util/clock.js";
11
11
  import {computeColumnsForCustodyGroup, getCustodyGroups} from "../../util/dataColumns.js";
12
+ import {callInNextEventLoop} from "../../util/eventLoop.js";
12
13
  import {NetworkCoreMetrics} from "../core/metrics.js";
13
14
  import {LodestarDiscv5Opts} from "../discv5/types.js";
14
15
  import {INetworkEventBus, NetworkEvent, NetworkEventData} from "../events.js";
@@ -196,6 +197,13 @@ export class PeerManager {
196
197
 
197
198
  this.lastStatus = this.statusCache.get();
198
199
 
200
+ // A connection may already be open before listeners are attached.
201
+ // Seed those peers so they are tracked in connectedPeers immediately.
202
+ this.bootstrapAlreadyOpenConnections();
203
+ // Defer status/ping to the next event loop tick so the heartbeat interval and
204
+ // event listeners are fully registered before we begin handshakes.
205
+ callInNextEventLoop(() => this.pingAndStatusTimeouts());
206
+
199
207
  // On start-up will connected to existing peers in libp2p.peerStore, same as autoDial behaviour
200
208
  this.heartbeat();
201
209
  this.intervals = [
@@ -692,36 +700,39 @@ export class PeerManager {
692
700
  }
693
701
  }
694
702
 
695
- /**
696
- * The libp2p Upgrader has successfully upgraded a peer connection on a particular multiaddress
697
- * This event is routed through the connectionManager
698
- *
699
- * Registers a peer as connected. The `direction` parameter determines if the peer is being
700
- * dialed or connecting to us.
701
- */
702
- private onLibp2pPeerConnect = async (evt: CustomEvent<Connection>): Promise<void> => {
703
- const {direction, status, remotePeer} = evt.detail;
703
+ private bootstrapAlreadyOpenConnections(): void {
704
+ let bootstrapped = 0;
705
+
706
+ for (const {value: connections} of getConnectionsMap(this.libp2p).values()) {
707
+ for (const connection of connections) {
708
+ // trackLibp2pConnection handles deduplication via overwriteExisting: false
709
+ if (this.trackLibp2pConnection(connection, {overwriteExisting: false, triggerHandshakeNow: false})) {
710
+ bootstrapped++;
711
+ }
712
+ }
713
+ }
714
+
715
+ if (bootstrapped > 0) {
716
+ this.logger.verbose("Bootstrapped already-open libp2p peers", {bootstrapped});
717
+ }
718
+ }
719
+
720
+ private trackLibp2pConnection(
721
+ connection: Connection,
722
+ opts: {overwriteExisting: boolean; triggerHandshakeNow: boolean}
723
+ ): boolean {
724
+ const {direction, status, remotePeer} = connection;
704
725
  const remotePeerStr = remotePeer.toString();
705
726
  const remotePeerPrettyStr = prettyPrintPeerId(remotePeer);
706
- this.logger.verbose("peer connected", {peer: remotePeerPrettyStr, direction, status});
707
- // NOTE: The peerConnect event is not emitted here here, but after asserting peer relevance
708
- this.metrics?.peerConnectedEvent.inc({direction, status});
709
727
 
710
- if (evt.detail.status !== "open") {
728
+ if (status !== "open") {
711
729
  this.logger.debug("Peer disconnected before identify protocol initiated", {
712
730
  peerId: remotePeerPrettyStr,
713
- status: evt.detail.status,
731
+ status,
714
732
  });
715
- return;
733
+ return false;
716
734
  }
717
735
 
718
- // On connection:
719
- // - Outbound connections: send a STATUS and PING request
720
- // - Inbound connections: expect to be STATUS'd, schedule STATUS and PING for latter
721
- // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
722
- // If that happens, it's okay. Only the "outbound" connection triggers immediate action
723
- const now = Date.now();
724
-
725
736
  // Ethereum uses secp256k1 for node IDs, reject peers with other key types
726
737
  if (remotePeer.type !== "secp256k1") {
727
738
  this.logger.debug("Peer does not have secp256k1 key, disconnecting", {
@@ -729,9 +740,19 @@ export class PeerManager {
729
740
  type: remotePeer.type,
730
741
  });
731
742
  void this.goodbyeAndDisconnect(remotePeer, GoodByeReasonCode.IRRELEVANT_NETWORK);
732
- return;
743
+ return false;
733
744
  }
734
745
 
746
+ if (!opts.overwriteExisting && this.connectedPeers.has(remotePeerStr)) {
747
+ return false;
748
+ }
749
+
750
+ // On connection:
751
+ // - Outbound connections: send a STATUS and PING request
752
+ // - Inbound connections: expect to be STATUS'd, schedule STATUS and PING for later
753
+ // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
754
+ // If that happens, it's okay. Only the "outbound" connection triggers immediate action
755
+ const now = Date.now();
735
756
  const nodeId = computeNodeId(remotePeer);
736
757
  const peerData: PeerData = {
737
758
  lastReceivedMsgUnixTsMs: direction === "outbound" ? 0 : now,
@@ -750,14 +771,13 @@ export class PeerManager {
750
771
  };
751
772
  this.connectedPeers.set(remotePeerStr, peerData);
752
773
 
753
- if (direction === "outbound") {
754
- // this.pingAndStatusTimeouts();
774
+ if (direction === "outbound" && opts.triggerHandshakeNow) {
755
775
  void this.requestPing(remotePeer);
756
776
  void this.requestStatus(remotePeer, this.statusCache.get());
757
777
  }
758
778
 
759
779
  this.libp2p.services.identify
760
- .identify(evt.detail)
780
+ .identify(connection)
761
781
  .then((result) => {
762
782
  const agentVersion = result.agentVersion;
763
783
  if (agentVersion) {
@@ -766,7 +786,7 @@ export class PeerManager {
766
786
  }
767
787
  })
768
788
  .catch((err) => {
769
- if (evt.detail.status !== "open") {
789
+ if (connection.status !== "open") {
770
790
  this.logger.debug("Peer disconnected during identify protocol", {
771
791
  peerId: remotePeerPrettyStr,
772
792
  error: (err as Error).message,
@@ -775,6 +795,24 @@ export class PeerManager {
775
795
  this.logger.debug("Error setting agentVersion for the peer", {peerId: remotePeerPrettyStr}, err);
776
796
  }
777
797
  });
798
+
799
+ return true;
800
+ }
801
+
802
+ /**
803
+ * The libp2p Upgrader has successfully upgraded a peer connection on a particular multiaddress
804
+ * This event is routed through the connectionManager
805
+ *
806
+ * Registers a peer as connected. The `direction` parameter determines if the peer is being
807
+ * dialed or connecting to us.
808
+ */
809
+ private onLibp2pPeerConnect = (evt: CustomEvent<Connection>): void => {
810
+ const {direction, status, remotePeer} = evt.detail;
811
+ this.logger.verbose("peer connected", {peer: prettyPrintPeerId(remotePeer), direction, status});
812
+ // NOTE: The peerConnect event is not emitted here here, but after asserting peer relevance
813
+ this.metrics?.peerConnectedEvent.inc({direction, status});
814
+
815
+ this.trackLibp2pConnection(evt.detail, {overwriteExisting: true, triggerHandshakeNow: true});
778
816
  };
779
817
 
780
818
  /**
@@ -548,7 +548,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
548
548
  seenTimestampSec,
549
549
  }: GossipHandlerParamGeneric<GossipType.data_column_sidecar>) => {
550
550
  const {serializedData} = gossipData;
551
- const dataColumnSidecar = sszDeserialize(topic, serializedData);
551
+ // TODO GLOAS: handle gloas.DataColumnSidecar
552
+ const dataColumnSidecar = sszDeserialize(topic, serializedData) as fulu.DataColumnSidecar;
552
553
  const dataColumnSlot = dataColumnSidecar.signedBlockHeader.message.slot;
553
554
  const index = dataColumnSidecar.index;
554
555
 
@@ -821,11 +822,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
821
822
  [GossipType.execution_payload]: async ({
822
823
  gossipData,
823
824
  topic,
825
+ seenTimestampSec,
824
826
  }: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
825
827
  const {serializedData} = gossipData;
826
828
  const executionPayloadEnvelope = sszDeserialize(topic, serializedData);
827
829
  await validateGossipExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
828
830
 
831
+ const slot = executionPayloadEnvelope.message.slot;
832
+ const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
833
+ metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
834
+
829
835
  // TODO GLOAS: Handle valid envelope. Need an import flow that calls `processExecutionPayloadEnvelope` and fork choice
830
836
  },
831
837
  [GossipType.payload_attestation_message]: async ({