@aztec/p2p 0.0.1-commit.b655e406 → 0.0.1-commit.fce3e4f

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 (219) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +1 -1
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/client/factory.d.ts +1 -1
  4. package/dest/client/index.d.ts +1 -1
  5. package/dest/client/interface.d.ts +4 -2
  6. package/dest/client/interface.d.ts.map +1 -1
  7. package/dest/client/p2p_client.d.ts +4 -22
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +10 -5
  10. package/dest/config.d.ts +60 -54
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +11 -1
  13. package/dest/enr/generate-enr.d.ts +1 -1
  14. package/dest/enr/index.d.ts +1 -1
  15. package/dest/errors/attestation-pool.error.d.ts +7 -0
  16. package/dest/errors/attestation-pool.error.d.ts.map +1 -0
  17. package/dest/errors/attestation-pool.error.js +12 -0
  18. package/dest/errors/reqresp.error.d.ts +1 -1
  19. package/dest/errors/reqresp.error.d.ts.map +1 -1
  20. package/dest/index.d.ts +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +28 -6
  22. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  24. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  25. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +33 -32
  26. package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
  27. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +13 -6
  28. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  29. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +40 -17
  30. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +11 -6
  31. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  32. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +30 -8
  33. package/dest/mem_pools/attestation_pool/mocks.d.ts +224 -3
  34. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  35. package/dest/mem_pools/attestation_pool/mocks.js +1 -1
  36. package/dest/mem_pools/index.d.ts +1 -1
  37. package/dest/mem_pools/instrumentation.d.ts +3 -1
  38. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  39. package/dest/mem_pools/instrumentation.js +11 -2
  40. package/dest/mem_pools/interface.d.ts +1 -1
  41. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +1 -36
  42. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool/index.d.ts +1 -1
  44. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +1 -1
  45. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  46. package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
  47. package/dest/mem_pools/tx_pool/tx_pool.d.ts +1 -1
  48. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
  49. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -3
  50. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  51. package/dest/msg_validators/attestation_validator/attestation_validator.js +11 -11
  52. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +20 -0
  53. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
  54. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +67 -0
  55. package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
  56. package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
  57. package/dest/msg_validators/attestation_validator/index.js +1 -0
  58. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +1 -1
  59. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  60. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +21 -9
  61. package/dest/msg_validators/block_proposal_validator/index.d.ts +1 -1
  62. package/dest/msg_validators/index.d.ts +1 -1
  63. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +1 -1
  64. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -1
  65. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  66. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  67. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +1 -1
  68. package/dest/msg_validators/tx_validator/archive_cache.d.ts +1 -1
  69. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
  70. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +1 -1
  71. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  72. package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
  73. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  74. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +1 -1
  75. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  76. package/dest/msg_validators/tx_validator/factory.d.ts +1 -1
  77. package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
  78. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  79. package/dest/msg_validators/tx_validator/index.d.ts +1 -1
  80. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +1 -1
  81. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  82. package/dest/msg_validators/tx_validator/phases_validator.d.ts +1 -1
  83. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  84. package/dest/msg_validators/tx_validator/phases_validator.js +3 -1
  85. package/dest/msg_validators/tx_validator/test_utils.d.ts +1 -1
  86. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +1 -1
  87. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  88. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +1 -1
  89. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
  90. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +1 -1
  91. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  92. package/dest/services/data_store.d.ts +1 -1
  93. package/dest/services/data_store.d.ts.map +1 -1
  94. package/dest/services/discv5/discV5_service.d.ts +1 -1
  95. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  96. package/dest/services/dummy_service.d.ts +1 -1
  97. package/dest/services/dummy_service.d.ts.map +1 -1
  98. package/dest/services/encoding.d.ts +1 -1
  99. package/dest/services/encoding.d.ts.map +1 -1
  100. package/dest/services/gossipsub/scoring.d.ts +1 -1
  101. package/dest/services/index.d.ts +1 -1
  102. package/dest/services/libp2p/instrumentation.d.ts +3 -1
  103. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  104. package/dest/services/libp2p/instrumentation.js +9 -2
  105. package/dest/services/libp2p/libp2p_service.d.ts +11 -67
  106. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  107. package/dest/services/libp2p/libp2p_service.js +224 -54
  108. package/dest/services/peer-manager/interface.d.ts +1 -1
  109. package/dest/services/peer-manager/metrics.d.ts +3 -1
  110. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  111. package/dest/services/peer-manager/metrics.js +11 -0
  112. package/dest/services/peer-manager/peer_manager.d.ts +1 -32
  113. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  114. package/dest/services/peer-manager/peer_manager.js +2 -0
  115. package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
  116. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  117. package/dest/services/peer-manager/peer_scoring.js +40 -2
  118. package/dest/services/reqresp/config.d.ts +1 -1
  119. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +1 -1
  120. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  121. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +1 -4
  122. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  123. package/dest/services/reqresp/index.d.ts +1 -1
  124. package/dest/services/reqresp/interface.d.ts +1 -1
  125. package/dest/services/reqresp/interface.d.ts.map +1 -1
  126. package/dest/services/reqresp/metrics.d.ts +1 -1
  127. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  128. package/dest/services/reqresp/protocols/auth.d.ts +1 -1
  129. package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
  130. package/dest/services/reqresp/protocols/block.d.ts +1 -1
  131. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +1 -1
  132. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  133. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
  134. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +3 -5
  135. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  136. package/dest/services/reqresp/protocols/block_txs/index.d.ts +1 -1
  137. package/dest/services/reqresp/protocols/goodbye.d.ts +1 -1
  138. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  139. package/dest/services/reqresp/protocols/index.d.ts +1 -1
  140. package/dest/services/reqresp/protocols/ping.d.ts +1 -1
  141. package/dest/services/reqresp/protocols/status.d.ts +2 -2
  142. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  143. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  144. package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
  145. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +2 -2
  146. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  147. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  148. package/dest/services/reqresp/reqresp.d.ts +1 -41
  149. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  150. package/dest/services/reqresp/status.d.ts +2 -2
  151. package/dest/services/reqresp/status.d.ts.map +1 -1
  152. package/dest/services/service.d.ts +1 -1
  153. package/dest/services/tx_collection/config.d.ts +1 -1
  154. package/dest/services/tx_collection/fast_tx_collection.d.ts +1 -7
  155. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  156. package/dest/services/tx_collection/index.d.ts +1 -1
  157. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  158. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  159. package/dest/services/tx_collection/slow_tx_collection.d.ts +1 -3
  160. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  161. package/dest/services/tx_collection/slow_tx_collection.js +2 -1
  162. package/dest/services/tx_collection/tx_collection.d.ts +1 -2
  163. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  164. package/dest/services/tx_collection/tx_collection_sink.d.ts +3 -3
  165. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  166. package/dest/services/tx_collection/tx_source.d.ts +1 -1
  167. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  168. package/dest/services/tx_provider.d.ts +1 -1
  169. package/dest/services/tx_provider.d.ts.map +1 -1
  170. package/dest/services/tx_provider_instrumentation.d.ts +1 -1
  171. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
  172. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
  173. package/dest/test-helpers/get-ports.d.ts +1 -1
  174. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  175. package/dest/test-helpers/index.d.ts +1 -1
  176. package/dest/test-helpers/make-enrs.d.ts +1 -1
  177. package/dest/test-helpers/make-test-p2p-clients.d.ts +2 -2
  178. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  179. package/dest/test-helpers/mock-pubsub.d.ts +4 -4
  180. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  181. package/dest/test-helpers/mock-tx-helpers.d.ts +2 -2
  182. package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -1
  183. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  184. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  185. package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
  186. package/dest/testbench/p2p_client_testbench_worker.js +11 -8
  187. package/dest/testbench/parse_log_file.d.ts +1 -1
  188. package/dest/testbench/testbench.d.ts +1 -1
  189. package/dest/testbench/worker_client_manager.d.ts +1 -1
  190. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  191. package/dest/types/index.d.ts +1 -1
  192. package/dest/util.d.ts +2 -1
  193. package/dest/util.d.ts.map +1 -1
  194. package/dest/util.js +11 -2
  195. package/dest/versioning.d.ts +1 -1
  196. package/package.json +19 -18
  197. package/src/client/interface.ts +4 -1
  198. package/src/client/p2p_client.ts +12 -6
  199. package/src/config.ts +18 -1
  200. package/src/errors/attestation-pool.error.ts +13 -0
  201. package/src/mem_pools/attestation_pool/attestation_pool.ts +29 -5
  202. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +45 -32
  203. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +65 -23
  204. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +50 -16
  205. package/src/mem_pools/attestation_pool/mocks.ts +1 -1
  206. package/src/mem_pools/instrumentation.ts +13 -0
  207. package/src/msg_validators/attestation_validator/attestation_validator.ts +13 -15
  208. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +91 -0
  209. package/src/msg_validators/attestation_validator/index.ts +1 -0
  210. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +25 -9
  211. package/src/msg_validators/tx_validator/phases_validator.ts +3 -1
  212. package/src/services/libp2p/instrumentation.ts +10 -1
  213. package/src/services/libp2p/libp2p_service.ts +248 -59
  214. package/src/services/peer-manager/metrics.ts +10 -0
  215. package/src/services/peer-manager/peer_manager.ts +2 -0
  216. package/src/services/peer-manager/peer_scoring.ts +46 -3
  217. package/src/services/tx_collection/slow_tx_collection.ts +3 -2
  218. package/src/testbench/p2p_client_testbench_worker.ts +8 -5
  219. package/src/util.ts +12 -2
@@ -1,14 +1,14 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
3
  import { randomInt } from '@aztec/foundation/crypto';
3
4
  import { Fr } from '@aztec/foundation/fields';
4
5
  import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
5
- import { SerialQueue } from '@aztec/foundation/queue';
6
6
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
7
  import { Timer } from '@aztec/foundation/timer';
8
8
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
9
9
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
10
  import { protocolContractsHash } from '@aztec/protocol-contracts';
11
- import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
11
+ import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
12
12
  import type { ContractDataSource } from '@aztec/stdlib/contract';
13
13
  import { GasFees } from '@aztec/stdlib/gas';
14
14
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
@@ -50,8 +50,13 @@ import { ENR } from '@nethermindeth/enr';
50
50
  import { createLibp2p } from 'libp2p';
51
51
 
52
52
  import type { P2PConfig } from '../../config.js';
53
+ import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
53
54
  import type { MemPools } from '../../mem_pools/interface.js';
54
- import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
55
+ import {
56
+ AttestationValidator,
57
+ BlockProposalValidator,
58
+ FishermanAttestationValidator,
59
+ } from '../../msg_validators/index.js';
55
60
  import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
56
61
  import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
57
62
  import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
@@ -116,7 +121,6 @@ type ReceivedMessageValidationResult<T> =
116
121
  * Lib P2P implementation of the P2PService interface.
117
122
  */
118
123
  export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
119
- private jobQueue: SerialQueue = new SerialQueue();
120
124
  private discoveryRunningPromise?: RunningPromise;
121
125
  private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
122
126
 
@@ -140,6 +144,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
140
144
 
141
145
  private instrumentation: P2PInstrumentation;
142
146
 
147
+ protected logger: Logger;
148
+
143
149
  constructor(
144
150
  private clientType: T,
145
151
  private config: P2PConfig,
@@ -153,10 +159,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
153
159
  private proofVerifier: ClientProtocolCircuitVerifier,
154
160
  private worldStateSynchronizer: WorldStateSynchronizer,
155
161
  telemetry: TelemetryClient,
156
- protected logger = createLogger('p2p:libp2p_service'),
162
+ logger: Logger = createLogger('p2p:libp2p_service'),
157
163
  ) {
158
164
  super(telemetry, 'LibP2PService');
159
165
 
166
+ // Create child logger with fisherman prefix if in fisherman mode
167
+ this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
168
+
160
169
  this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
161
170
 
162
171
  this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
@@ -174,15 +183,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
174
183
  this.protocolVersion,
175
184
  );
176
185
 
177
- this.attestationValidator = new AttestationValidator(epochCache);
186
+ // Use FishermanAttestationValidator in fisherman mode to validate attestation payloads against proposals
187
+ this.attestationValidator = config.fishermanMode
188
+ ? new FishermanAttestationValidator(epochCache, mempools.attestationPool!, telemetry)
189
+ : new AttestationValidator(epochCache);
178
190
  this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
179
191
 
180
192
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
181
193
 
182
194
  this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
183
195
  this.logger.debug(
184
- `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`,
185
- { p2pMessageIdentifier: await block.p2pMessageIdentifier() },
196
+ `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber} from peer.`,
197
+ { p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
186
198
  );
187
199
  return undefined;
188
200
  };
@@ -395,7 +407,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
395
407
  logger: createLibp2pComponentLogger(logger.module),
396
408
  });
397
409
 
398
- const peerScoring = new PeerScoring(config);
410
+ const peerScoring = new PeerScoring(config, telemetry);
399
411
  const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
400
412
 
401
413
  const peerManager = new PeerManager(
@@ -450,9 +462,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
450
462
  }
451
463
  const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
452
464
 
453
- // Start job queue, peer discovery service and libp2p node
454
- this.jobQueue.start();
455
-
456
465
  await this.peerManager.initializePeers();
457
466
  if (!this.config.p2pDiscoveryDisabled) {
458
467
  await this.peerDiscoveryService.start();
@@ -490,9 +499,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
490
499
  // add GossipSub listener
491
500
  this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
492
501
 
493
- // Start running promise for peer discovery
502
+ // Start running promise for peer discovery and metrics collection
494
503
  this.discoveryRunningPromise = new RunningPromise(
495
- () => this.peerManager.heartbeat(),
504
+ async () => {
505
+ await this.peerManager.heartbeat();
506
+ },
496
507
  this.logger,
497
508
  this.config.peerCheckIntervalMS,
498
509
  );
@@ -501,9 +512,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
501
512
  // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
502
513
  const reqrespSubProtocolValidators = {
503
514
  ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
504
- // TODO(#11336): A request validator for blocks
505
515
  [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
506
516
  [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
517
+ [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
507
518
  };
508
519
  await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
509
520
  this.logger.info(`Started P2P service`, {
@@ -525,9 +536,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
525
536
  // Stop peer manager
526
537
  this.logger.debug('Stopping peer manager...');
527
538
  await this.peerManager.stop();
528
-
529
- this.logger.debug('Stopping job queue...');
530
- await this.jobQueue.end();
531
539
  this.logger.debug('Stopping running promise...');
532
540
  await this.discoveryRunningPromise?.stop();
533
541
  this.logger.debug('Stopping peer discovery service...');
@@ -615,7 +623,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
615
623
  if (!this.node.services.pubsub) {
616
624
  throw new Error('Pubsub service not available.');
617
625
  }
618
- const p2pMessage = P2PMessage.fromGossipable(message);
626
+ const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
619
627
  const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
620
628
  return result.recipients.length;
621
629
  }
@@ -660,13 +668,39 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
660
668
  return { result: true, topicType };
661
669
  }
662
670
 
671
+ /**
672
+ * Safely deserializes a P2PMessage from raw message data.
673
+ * @param msgId - The message ID.
674
+ * @param source - The peer ID of the message source.
675
+ * @param data - The raw message data.
676
+ * @returns The deserialized P2PMessage or undefined if deserialization fails.
677
+ */
678
+ private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
679
+ try {
680
+ return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
681
+ } catch (err) {
682
+ this.logger.error(`Error deserializing P2PMessage`, err, {
683
+ msgId,
684
+ source: source.toString(),
685
+ });
686
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
687
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
688
+ return undefined;
689
+ }
690
+ }
691
+
663
692
  /**
664
693
  * Handles a new gossip message that was received by the client.
665
694
  * @param topic - The message's topic.
666
695
  * @param data - The message data
667
696
  */
668
697
  protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
669
- const p2pMessage = P2PMessage.fromMessageData(Buffer.from(msg.data));
698
+ const msgReceivedTime = Date.now();
699
+ let topicType: TopicType | undefined;
700
+ const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
701
+ if (!p2pMessage) {
702
+ return;
703
+ }
670
704
 
671
705
  const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
672
706
 
@@ -675,15 +709,25 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
675
709
  }
676
710
 
677
711
  if (msg.topic === this.topicStrings[TopicType.tx]) {
712
+ topicType = TopicType.tx;
678
713
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
679
714
  }
680
- if (msg.topic === this.topicStrings[TopicType.block_attestation] && this.clientType === P2PClientType.Full) {
681
- await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
715
+ if (msg.topic === this.topicStrings[TopicType.block_attestation]) {
716
+ topicType = TopicType.block_attestation;
717
+ if (this.clientType === P2PClientType.Full) {
718
+ await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
719
+ }
682
720
  }
683
721
  if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
722
+ topicType = TopicType.block_proposal;
684
723
  await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
685
724
  }
686
725
 
726
+ if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
727
+ const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
728
+ this.instrumentation.recordMessageLatency(topicType, latency);
729
+ }
730
+
687
731
  return;
688
732
  }
689
733
 
@@ -698,6 +742,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
698
742
  try {
699
743
  resultAndObj = await validationFunc();
700
744
  } catch (err) {
745
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
701
746
  this.logger.error(`Error deserializing and validating gossipsub message`, err, {
702
747
  msgId,
703
748
  source: source.toString(),
@@ -751,6 +796,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
751
796
  return;
752
797
  }
753
798
 
799
+ this.instrumentation.incrementTxReceived(1);
754
800
  await this.mempools.txPool.addTxs([tx]);
755
801
  }
756
802
 
@@ -763,12 +809,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
763
809
  private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
764
810
  const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
765
811
  const attestation = BlockAttestation.fromBuffer(payloadData);
812
+ const pool = this.mempools.attestationPool!;
766
813
  const isValid = await this.validateAttestation(source, attestation);
767
- const exists = isValid && (await this.mempools.attestationPool!.hasAttestation(attestation));
814
+ const exists = isValid && (await pool.hasAttestation(attestation));
815
+
816
+ let canAdd = true;
817
+ if (isValid && !exists) {
818
+ const slot = attestation.payload.header.slotNumber;
819
+ const { committee } = await this.epochCache.getCommittee(slot);
820
+ const committeeSize = committee?.length ?? 0;
821
+ canAdd = await pool.canAddAttestation(attestation, committeeSize);
822
+ }
768
823
 
769
824
  this.logger.trace(`Validate propagated block attestation`, {
770
825
  isValid,
771
826
  exists,
827
+ canAdd,
772
828
  [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
773
829
  [Attributes.P2P_ID]: source.toString(),
774
830
  });
@@ -777,6 +833,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
777
833
  return { result: TopicValidatorResult.Reject };
778
834
  } else if (exists) {
779
835
  return { result: TopicValidatorResult.Ignore, obj: attestation };
836
+ } else if (!canAdd) {
837
+ this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
838
+ slot: attestation.payload.header.slotNumber.toString(),
839
+ archive: attestation.archive.toString(),
840
+ source: source.toString(),
841
+ });
842
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
780
843
  } else {
781
844
  return { result: TopicValidatorResult.Accept, obj: attestation };
782
845
  }
@@ -794,10 +857,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
794
857
  }
795
858
 
796
859
  this.logger.debug(
797
- `Received attestation for slot ${attestation.slotNumber.toNumber()} from external peer ${source.toString()}`,
860
+ `Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
798
861
  {
799
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
800
- slot: attestation.slotNumber.toNumber(),
862
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
863
+ slot: attestation.slotNumber,
801
864
  archive: attestation.archive.toString(),
802
865
  source: source.toString(),
803
866
  },
@@ -810,14 +873,17 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
810
873
  const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
811
874
  const block = BlockProposal.fromBuffer(payloadData);
812
875
  const isValid = await this.validateBlockProposal(source, block);
876
+ const pool = this.mempools.attestationPool;
813
877
 
814
878
  // Note that we dont have an attestation pool if we're a prover node, but we still
815
879
  // subscribe to block proposal topics in order to prevent their txs from being cleared.
816
- const exists = isValid && (await this.mempools.attestationPool?.hasBlockProposal(block));
880
+ const exists = isValid && (await pool?.hasBlockProposal(block));
881
+ const canAdd = isValid && (await pool?.canAddProposal(block));
817
882
 
818
883
  this.logger.trace(`Validate propagated block proposal`, {
819
884
  isValid,
820
885
  exists,
886
+ canAdd,
821
887
  [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
822
888
  [Attributes.P2P_ID]: source.toString(),
823
889
  });
@@ -826,6 +892,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
826
892
  return { result: TopicValidatorResult.Reject };
827
893
  } else if (exists) {
828
894
  return { result: TopicValidatorResult.Ignore, obj: block };
895
+ } else if (!canAdd) {
896
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
897
+ this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
898
+ slot: block.slotNumber.toString(),
899
+ archive: block.archive.toString(),
900
+ source: source.toString(),
901
+ });
902
+ return { result: TopicValidatorResult.Reject };
829
903
  } else {
830
904
  return { result: TopicValidatorResult.Accept, obj: block };
831
905
  }
@@ -847,16 +921,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
847
921
 
848
922
  // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
849
923
  @trackSpan('Libp2pService.processValidBlockProposal', async block => ({
850
- [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
924
+ [Attributes.SLOT_NUMBER]: block.slotNumber,
851
925
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
852
- [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then(i => i.toString()),
926
+ [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
853
927
  }))
854
928
  private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
855
- const slot = block.slotNumber.toBigInt();
856
- const previousSlot = slot - 1n;
929
+ const slot = block.slotNumber;
930
+ const previousSlot = SlotNumber(slot - 1);
857
931
  this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
858
- p2pMessageIdentifier: await block.p2pMessageIdentifier(),
859
- slot: block.slotNumber.toNumber(),
932
+ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
933
+ slot: block.slotNumber,
860
934
  archive: block.archive.toString(),
861
935
  source: sender.toString(),
862
936
  });
@@ -865,18 +939,31 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
865
939
  this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
866
940
  }
867
941
 
868
- // Mark the txs in this proposal as non-evictable
942
+ // Attempt to add proposal, then mark the txs in this proposal as non-evictable
943
+ try {
944
+ await this.mempools.attestationPool?.addBlockProposal(block);
945
+ } catch (err: unknown) {
946
+ // Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
947
+ if (err instanceof ProposalSlotCapExceededError) {
948
+ this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
949
+ slot: String(slot),
950
+ archive: block.archive.toString(),
951
+ error: (err as Error).message,
952
+ });
953
+ return;
954
+ }
955
+ throw err;
956
+ }
869
957
  await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
870
- await this.mempools.attestationPool?.addBlockProposal(block);
871
958
  const attestations = await this.blockReceivedCallback(block, sender);
872
959
 
873
960
  // TODO: fix up this pattern - the abstraction is not nice
874
- // The attestation can be undefined if no handler is registered / the validator deems the block invalid
961
+ // The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
875
962
  if (attestations?.length) {
876
963
  for (const attestation of attestations) {
877
- this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber.toNumber()}`, {
878
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
879
- slot: attestation.slotNumber.toNumber(),
964
+ this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
965
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
966
+ slot: attestation.slotNumber,
880
967
  archive: attestation.archive.toString(),
881
968
  });
882
969
  await this.broadcastAttestation(attestation);
@@ -889,9 +976,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
889
976
  * @param attestation - The attestation to broadcast.
890
977
  */
891
978
  @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
892
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
979
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
893
980
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
894
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
981
+ [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
895
982
  }))
896
983
  private async broadcastAttestation(attestation: BlockAttestation) {
897
984
  await this.propagate(attestation);
@@ -902,19 +989,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
902
989
  * @param message - The message to propagate.
903
990
  */
904
991
  public async propagate<T extends Gossipable>(message: T) {
905
- const p2pMessageIdentifier = await message.p2pMessageIdentifier();
992
+ const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
906
993
  this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
907
- void this.jobQueue
908
- .put(async () => {
909
- await this.sendToPeers(message);
910
- })
911
- .catch(error => {
912
- this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
913
- });
994
+ void this.sendToPeers(message).catch(error => {
995
+ this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
996
+ });
914
997
  }
915
998
 
916
999
  /**
917
- * Validate the requested block transactions.
1000
+ * Validate the requested block transactions. Allow partial returns.
918
1001
  * @param request - The block transactions request.
919
1002
  * @param response - The block transactions response.
920
1003
  * @param peerId - The ID of the peer that made the request.
@@ -924,14 +1007,71 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
924
1007
  [Attributes.BLOCK_HASH]: request.blockHash.toString(),
925
1008
  }))
926
1009
  private async validateRequestedBlockTxs(
927
- _request: BlockTxsRequest,
1010
+ request: BlockTxsRequest,
928
1011
  response: BlockTxsResponse,
929
1012
  peerId: PeerId,
930
1013
  ): Promise<boolean> {
931
1014
  const requestedTxValidator = this.createRequestedTxValidator();
932
1015
 
933
1016
  try {
934
- // TODO(palla/txs): Validate that this tx belongs to the block hash being requested
1017
+ if (!response.blockHash.equals(request.blockHash)) {
1018
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1019
+ throw new ValidationError(
1020
+ `Received block txs for unexpected block: expected ${request.blockHash.toString()}, got ${response.blockHash.toString()}`,
1021
+ );
1022
+ }
1023
+
1024
+ if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1025
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1026
+ throw new ValidationError(
1027
+ `Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
1028
+ );
1029
+ }
1030
+
1031
+ // Check no duplicates and not exceeding returnable count
1032
+ const requestedIndices = new Set(request.txIndices.getTrueIndices());
1033
+ const availableIndices = new Set(response.txIndices.getTrueIndices());
1034
+ const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
1035
+
1036
+ const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
1037
+ const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1038
+ if (uniqueReturned.size !== returnedHashes.length) {
1039
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1040
+ throw new ValidationError(`Received duplicate txs in block txs response`);
1041
+ }
1042
+ if (response.txs.length > maxReturnable) {
1043
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1044
+ throw new ValidationError(
1045
+ `Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
1046
+ );
1047
+ }
1048
+
1049
+ // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1050
+ const proposal = await this.mempools.attestationPool?.getBlockProposal(request.blockHash.toString());
1051
+ if (proposal) {
1052
+ // Build intersected indices
1053
+ const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1054
+
1055
+ // Enforce subset membership and preserve increasing order by index.
1056
+ const hashToIndexInProposal = new Map<string, number>(
1057
+ proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
1058
+ );
1059
+ const allowedIndexSet = new Set(intersectIdx);
1060
+ const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
1061
+ const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
1062
+ const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
1063
+ if (!allAllowed || !strictlyIncreasing) {
1064
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1065
+ throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1066
+ }
1067
+ } else {
1068
+ // No local proposal, cannot check the membership/order of the returned txs
1069
+ this.logger.warn(
1070
+ `Block proposal not found for block hash ${request.blockHash.toString()}; cannot validate membership/order of returned txs`,
1071
+ );
1072
+ return false;
1073
+ }
1074
+
935
1075
  await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
936
1076
  return true;
937
1077
  } catch (e: any) {
@@ -955,7 +1095,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
955
1095
  * ReqRespSubProtocol.TX subprotocol validation.
956
1096
  *
957
1097
  * @param requestedTxHash - The collection of the txs that was requested.
958
- * @param responseTx - The collectin of txs that was received as a response to the request.
1098
+ * @param responseTx - The collection of txs that was received as a response to the request.
959
1099
  * @param peerId - The peer ID of the peer that sent the tx.
960
1100
  * @returns True if the whole collection of txs is valid, false otherwise.
961
1101
  */
@@ -982,6 +1122,53 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
982
1122
  }
983
1123
  }
984
1124
 
1125
+ /**
1126
+ * Validates a BLOCK response.
1127
+ *
1128
+ * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1129
+ * Penalizes on block number mismatch or hash mismatch.
1130
+ *
1131
+ * @param requestedBlockNumber - The requested block number.
1132
+ * @param responseBlock - The block returned by the peer.
1133
+ * @param peerId - The peer that returned the block.
1134
+ * @returns True if the response is valid, false otherwise.
1135
+ */
1136
+ @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1137
+ [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1138
+ }))
1139
+ private async validateRequestedBlock(
1140
+ requestedBlockNumber: Fr,
1141
+ responseBlock: L2Block,
1142
+ peerId: PeerId,
1143
+ ): Promise<boolean> {
1144
+ try {
1145
+ const reqNum = Number(requestedBlockNumber.toString());
1146
+ if (responseBlock.number !== reqNum) {
1147
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1148
+ return false;
1149
+ }
1150
+
1151
+ const local = await this.archiver.getBlock(reqNum);
1152
+ if (!local) {
1153
+ // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1154
+ // TODO: Consider extending this validator to accept an expected hash or
1155
+ // performing quorum-based checks when using P2P syncing prior to L1 sync.
1156
+ this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1157
+ return false;
1158
+ }
1159
+ const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1160
+ if (!localHash.equals(respHash)) {
1161
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1162
+ return false;
1163
+ }
1164
+
1165
+ return true;
1166
+ } catch (e) {
1167
+ this.logger.warn(`Error validating requested block`, e);
1168
+ return false;
1169
+ }
1170
+ }
1171
+
985
1172
  private createRequestedTxValidator(): TxValidator {
986
1173
  return new AggregateTxValidator(
987
1174
  new DataTxValidator(),
@@ -996,19 +1183,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
996
1183
  }
997
1184
 
998
1185
  private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
1186
+ const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1187
+
999
1188
  if (!(await tx.validateTxHash())) {
1000
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1189
+ penalize(PeerErrorSeverity.MidToleranceError);
1001
1190
  throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
1002
1191
  }
1003
1192
 
1004
1193
  if (requested && !requested.has(tx.getTxHash().toString())) {
1005
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1194
+ penalize(PeerErrorSeverity.MidToleranceError);
1006
1195
  throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1007
1196
  }
1008
1197
 
1009
1198
  const { result } = await txValidator.validateTx(tx);
1010
1199
  if (result === 'invalid') {
1011
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1200
+ penalize(PeerErrorSeverity.LowToleranceError);
1012
1201
  throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1013
1202
  }
1014
1203
  }
@@ -1183,9 +1372,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1183
1372
  * @returns True if the attestation is valid, false otherwise.
1184
1373
  */
1185
1374
  @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
1186
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
1375
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
1187
1376
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
1188
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
1377
+ [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
1189
1378
  }))
1190
1379
  public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
1191
1380
  const severity = await this.attestationValidator.validate(attestation);
@@ -1228,7 +1417,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1228
1417
  private async sendToPeers<T extends Gossipable>(message: T) {
1229
1418
  const parent = message.constructor as typeof Gossipable;
1230
1419
 
1231
- const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
1420
+ const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
1232
1421
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
1233
1422
 
1234
1423
  const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
@@ -15,6 +15,7 @@ export class PeerManagerMetrics {
15
15
  private sentGoodbyes: UpDownCounter;
16
16
  private receivedGoodbyes: UpDownCounter;
17
17
  private peerCount: Gauge;
18
+ private lowScoreDisconnects: UpDownCounter;
18
19
 
19
20
  public readonly tracer: Tracer;
20
21
 
@@ -40,6 +41,11 @@ export class PeerManagerMetrics {
40
41
  unit: 'peers',
41
42
  valueType: ValueType.INT,
42
43
  });
44
+ this.lowScoreDisconnects = meter.createUpDownCounter(Metrics.PEER_MANAGER_LOW_SCORE_DISCONNECTS, {
45
+ description: 'Number of peers disconnected due to low score',
46
+ unit: 'peers',
47
+ valueType: ValueType.INT,
48
+ });
43
49
  }
44
50
 
45
51
  public recordGoodbyeSent(reason: GoodByeReason) {
@@ -53,4 +59,8 @@ export class PeerManagerMetrics {
53
59
  public recordPeerCount(count: number) {
54
60
  this.peerCount.record(count);
55
61
  }
62
+
63
+ public recordLowScoreDisconnect(scoreState: 'Banned' | 'Disconnect') {
64
+ this.lowScoreDisconnects.add(1, { [Attributes.P2P_PEER_SCORE_STATE]: scoreState });
65
+ }
56
66
  }
@@ -577,9 +577,11 @@ export class PeerManager implements PeerManagerInterface {
577
577
  const score = this.peerScoring.getScoreState(peer.remotePeer.toString());
578
578
  switch (score) {
579
579
  case PeerScoreState.Banned:
580
+ this.metrics.recordLowScoreDisconnect('Banned');
580
581
  void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.BANNED);
581
582
  break;
582
583
  case PeerScoreState.Disconnect:
584
+ this.metrics.recordLowScoreDisconnect('Disconnect');
583
585
  void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.LOW_SCORE);
584
586
  break;
585
587
  case PeerScoreState.Healthy: