@aztec/p2p 0.86.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/client/factory.d.ts +4 -1
  3. package/dest/client/factory.d.ts.map +1 -1
  4. package/dest/client/factory.js +16 -14
  5. package/dest/client/index.d.ts +1 -0
  6. package/dest/client/index.d.ts.map +1 -1
  7. package/dest/client/index.js +1 -0
  8. package/dest/client/interface.d.ts +155 -0
  9. package/dest/client/interface.d.ts.map +1 -0
  10. package/dest/client/interface.js +9 -0
  11. package/dest/client/p2p_client.d.ts +26 -164
  12. package/dest/client/p2p_client.d.ts.map +1 -1
  13. package/dest/client/p2p_client.js +185 -114
  14. package/dest/config.d.ts +5 -6
  15. package/dest/config.d.ts.map +1 -1
  16. package/dest/config.js +6 -11
  17. package/dest/enr/generate-enr.d.ts +9 -1
  18. package/dest/enr/generate-enr.d.ts.map +1 -1
  19. package/dest/enr/generate-enr.js +24 -2
  20. package/dest/index.d.ts +1 -0
  21. package/dest/index.d.ts.map +1 -1
  22. package/dest/index.js +1 -0
  23. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +2 -0
  24. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  25. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +4 -4
  26. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +1 -0
  27. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  28. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +8 -2
  29. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +1 -0
  30. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  31. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +5 -2
  32. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/mocks.js +2 -2
  34. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +4 -0
  35. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +50 -14
  37. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +3 -0
  38. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  39. package/dest/mem_pools/tx_pool/memory_tx_pool.js +9 -0
  40. package/dest/mem_pools/tx_pool/tx_pool.d.ts +9 -0
  41. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  42. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +13 -5
  44. package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
  45. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +1 -1
  46. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  47. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  48. package/dest/msg_validators/tx_validator/data_validator.js +15 -14
  49. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +0 -2
  50. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  51. package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
  52. package/dest/msg_validators/tx_validator/factory.d.ts +14 -0
  53. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
  54. package/dest/msg_validators/tx_validator/factory.js +62 -0
  55. package/dest/msg_validators/tx_validator/gas_validator.js +3 -3
  56. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +8 -4
  57. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  58. package/dest/msg_validators/tx_validator/metadata_validator.js +35 -17
  59. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  60. package/dest/msg_validators/tx_validator/phases_validator.js +1 -1
  61. package/dest/msg_validators/tx_validator/tx_proof_validator.js +1 -1
  62. package/dest/services/discv5/discV5_service.d.ts +2 -2
  63. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  64. package/dest/services/discv5/discV5_service.js +9 -13
  65. package/dest/services/dummy_service.d.ts +3 -3
  66. package/dest/services/dummy_service.d.ts.map +1 -1
  67. package/dest/services/dummy_service.js +6 -1
  68. package/dest/services/encoding.d.ts +1 -3
  69. package/dest/services/encoding.d.ts.map +1 -1
  70. package/dest/services/libp2p/libp2p_service.d.ts +4 -2
  71. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  72. package/dest/services/libp2p/libp2p_service.js +94 -88
  73. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  74. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  75. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  76. package/dest/services/peer-manager/peer_manager.js +11 -2
  77. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  78. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +2 -2
  79. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  80. package/dest/services/reqresp/connection-sampler/connection_sampler.js +41 -21
  81. package/dest/services/reqresp/interface.d.ts +1 -3
  82. package/dest/services/reqresp/interface.d.ts.map +1 -1
  83. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  84. package/dest/services/reqresp/protocols/goodbye.d.ts +0 -2
  85. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  86. package/dest/services/reqresp/protocols/goodbye.js +1 -1
  87. package/dest/services/reqresp/protocols/ping.d.ts +0 -2
  88. package/dest/services/reqresp/protocols/ping.d.ts.map +1 -1
  89. package/dest/services/reqresp/protocols/status.d.ts +0 -2
  90. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  91. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  92. package/dest/services/reqresp/reqresp.d.ts +1 -3
  93. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  94. package/dest/services/reqresp/reqresp.js +13 -10
  95. package/dest/services/service.d.ts +4 -3
  96. package/dest/services/service.d.ts.map +1 -1
  97. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  98. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  99. package/dest/test-helpers/make-test-p2p-clients.js +2 -2
  100. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  101. package/dest/test-helpers/reqresp-nodes.js +1 -1
  102. package/dest/testbench/p2p_client_testbench_worker.js +11 -6
  103. package/dest/testbench/testbench.js +1 -1
  104. package/dest/testbench/worker_client_manager.d.ts +0 -1
  105. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  106. package/dest/testbench/worker_client_manager.js +2 -2
  107. package/dest/types/index.d.ts +1 -0
  108. package/dest/types/index.d.ts.map +1 -1
  109. package/dest/types/index.js +1 -0
  110. package/dest/versioning.d.ts +2 -2
  111. package/dest/versioning.d.ts.map +1 -1
  112. package/dest/versioning.js +6 -1
  113. package/package.json +15 -15
  114. package/src/bootstrap/bootstrap.ts +1 -1
  115. package/src/client/factory.ts +38 -33
  116. package/src/client/index.ts +1 -0
  117. package/src/client/interface.ts +186 -0
  118. package/src/client/p2p_client.ts +226 -287
  119. package/src/config.ts +11 -18
  120. package/src/enr/generate-enr.ts +35 -3
  121. package/src/index.ts +1 -0
  122. package/src/mem_pools/attestation_pool/attestation_pool.ts +3 -0
  123. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +4 -4
  124. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +11 -4
  125. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +10 -3
  126. package/src/mem_pools/attestation_pool/mocks.ts +2 -2
  127. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +79 -34
  128. package/src/mem_pools/tx_pool/memory_tx_pool.ts +16 -1
  129. package/src/mem_pools/tx_pool/tx_pool.ts +12 -0
  130. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +9 -3
  131. package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
  132. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +1 -1
  133. package/src/msg_validators/tx_validator/block_header_validator.ts +1 -1
  134. package/src/msg_validators/tx_validator/data_validator.ts +24 -18
  135. package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
  136. package/src/msg_validators/tx_validator/factory.ts +94 -0
  137. package/src/msg_validators/tx_validator/gas_validator.ts +3 -3
  138. package/src/msg_validators/tx_validator/metadata_validator.ts +50 -14
  139. package/src/msg_validators/tx_validator/phases_validator.ts +6 -2
  140. package/src/msg_validators/tx_validator/tx_proof_validator.ts +1 -1
  141. package/src/services/discv5/discV5_service.ts +14 -12
  142. package/src/services/dummy_service.ts +8 -2
  143. package/src/services/libp2p/libp2p_service.ts +102 -111
  144. package/src/services/peer-manager/metrics.ts +4 -1
  145. package/src/services/peer-manager/peer_manager.ts +18 -1
  146. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +5 -1
  147. package/src/services/reqresp/connection-sampler/connection_sampler.ts +42 -19
  148. package/src/services/reqresp/metrics.ts +4 -1
  149. package/src/services/reqresp/protocols/goodbye.ts +1 -1
  150. package/src/services/reqresp/rate-limiter/rate_limiter.ts +4 -1
  151. package/src/services/reqresp/reqresp.ts +12 -12
  152. package/src/services/service.ts +7 -1
  153. package/src/test-helpers/make-test-p2p-clients.ts +2 -1
  154. package/src/test-helpers/reqresp-nodes.ts +1 -1
  155. package/src/testbench/p2p_client_testbench_worker.ts +10 -4
  156. package/src/testbench/testbench.ts +1 -1
  157. package/src/testbench/worker_client_manager.ts +2 -2
  158. package/src/types/index.ts +1 -0
  159. package/src/versioning.ts +8 -1
@@ -1,10 +1,9 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
- import { Fr } from '@aztec/foundation/fields';
3
2
  import { createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
4
3
  import { SerialQueue } from '@aztec/foundation/queue';
5
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
6
5
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
7
- import { ProtocolContractAddress } from '@aztec/protocol-contracts';
6
+ import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
8
7
  import type { L2BlockSource } from '@aztec/stdlib/block';
9
8
  import type { ContractDataSource } from '@aztec/stdlib/contract';
10
9
  import { GasFees } from '@aztec/stdlib/gas';
@@ -14,13 +13,14 @@ import {
14
13
  BlockProposal,
15
14
  type Gossipable,
16
15
  P2PClientType,
16
+ P2PMessage,
17
17
  PeerErrorSeverity,
18
18
  TopicType,
19
19
  createTopicString,
20
20
  getTopicTypeForClientType,
21
21
  metricsTopicStrToLabels,
22
22
  } from '@aztec/stdlib/p2p';
23
- import { DatabasePublicStateSource, MerkleTreeId } from '@aztec/stdlib/trees';
23
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
24
24
  import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx';
25
25
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
26
26
  import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
@@ -49,14 +49,8 @@ import type { P2PConfig } from '../../config.js';
49
49
  import type { MemPools } from '../../mem_pools/interface.js';
50
50
  import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
51
51
  import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
52
- import {
53
- DataTxValidator,
54
- DoubleSpendTxValidator,
55
- GasTxValidator,
56
- MetadataTxValidator,
57
- PhasesTxValidator,
58
- TxProofValidator,
59
- } from '../../msg_validators/tx_validator/index.js';
52
+ import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
53
+ import { DoubleSpendTxValidator, TxProofValidator } from '../../msg_validators/tx_validator/index.js';
60
54
  import { GossipSubEvent } from '../../types/index.js';
61
55
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
62
56
  import { getVersions } from '../../versioning.js';
@@ -71,13 +65,6 @@ import { pingHandler, reqRespBlockHandler, reqRespTxHandler, statusHandler } fro
71
65
  import { ReqResp } from '../reqresp/reqresp.js';
72
66
  import type { P2PService, PeerDiscoveryService } from '../service.js';
73
67
 
74
- interface MessageValidator {
75
- validator: {
76
- validateTx(tx: Tx): Promise<TxValidationResult>;
77
- };
78
- severity: PeerErrorSeverity;
79
- }
80
-
81
68
  interface ValidationResult {
82
69
  name: string;
83
70
  isValid: TxValidationResult;
@@ -233,7 +220,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
233
220
  tcp({
234
221
  maxConnections: config.maxPeerCount,
235
222
  // socket option: the maximum length of the queue of pending connections
236
- // https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
223
+ // https://nodejs.org/dist/latest-v22.x/docs/api/net.html#serverlisten
237
224
  // it's not safe if we increase this number
238
225
  backlog: 5,
239
226
  closeServerOnMaxConnections: {
@@ -270,6 +257,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
270
257
  heartbeatInterval: config.gossipsubInterval,
271
258
  mcacheLength: config.gossipsubMcacheLength,
272
259
  mcacheGossip: config.gossipsubMcacheGossip,
260
+ seenTTL: config.gossipsubSeenTTL,
273
261
  msgIdFn: getMsgIdFn,
274
262
  msgIdToStrFn: msgIdToStrFn,
275
263
  fastMsgIdFn: fastMsgIdFn,
@@ -489,14 +477,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
489
477
  /**
490
478
  * Publishes data to a topic.
491
479
  * @param topic - The topic to publish to.
492
- * @param data - The data to publish.
480
+ * @param data - The message to publish.
493
481
  * @returns The number of recipients the data was sent to.
494
482
  */
495
- private async publishToTopic(topic: string, data: Uint8Array) {
483
+ private async publishToTopic(topic: string, message: Gossipable) {
496
484
  if (!this.node.services.pubsub) {
497
485
  throw new Error('Pubsub service not available.');
498
486
  }
499
- const result = await this.node.services.pubsub.publish(topic, data);
487
+ const p2pMessage = await P2PMessage.fromGossipable(message);
488
+ this.logger.debug(`Publishing message`, {
489
+ topic,
490
+ messageId: p2pMessage.id,
491
+ });
492
+ const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
500
493
 
501
494
  return result.recipients.length;
502
495
  }
@@ -507,14 +500,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
507
500
  * @param data - The message data
508
501
  */
509
502
  protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
503
+ const p2pMessage = P2PMessage.fromMessageData(Buffer.from(msg.data));
504
+ const currentTime = new Date();
505
+ const messageLatency = currentTime.getTime() - p2pMessage.publishTime.getTime();
506
+ this.logger.debug(`Received message`, {
507
+ topic: msg.topic,
508
+ messageId: p2pMessage.id,
509
+ messageLatency,
510
+ });
510
511
  if (msg.topic === this.topicStrings[TopicType.tx]) {
511
- await this.handleGossipedTx(msg, msgId, source);
512
+ await this.handleGossipedTx(p2pMessage.payload, msgId, source);
512
513
  }
513
514
  if (msg.topic === this.topicStrings[TopicType.block_attestation] && this.clientType === P2PClientType.Full) {
514
- await this.processAttestationFromPeer(msg, msgId, source);
515
+ await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
515
516
  }
516
517
  if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
517
- await this.processBlockFromPeer(msg, msgId, source);
518
+ await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
518
519
  }
519
520
 
520
521
  return;
@@ -540,9 +541,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
540
541
  return resultAndObj;
541
542
  }
542
543
 
543
- protected async handleGossipedTx(msg: Message, msgId: string, source: PeerId) {
544
+ protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
544
545
  const validationFunc = async () => {
545
- const tx = Tx.fromBuffer(Buffer.from(msg.data));
546
+ const tx = Tx.fromBuffer(payloadData);
546
547
  const result = await this.validatePropagatedTx(tx, source);
547
548
  return { result, obj: tx };
548
549
  };
@@ -563,12 +564,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
563
564
  *
564
565
  * @param attestation - The attestation to process.
565
566
  */
566
- private async processAttestationFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
567
+ private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
567
568
  const validationFunc = async () => {
568
- const attestation = BlockAttestation.fromBuffer(Buffer.from(msg.data));
569
+ const attestation = BlockAttestation.fromBuffer(payloadData);
569
570
  const result = await this.validateAttestation(source, attestation);
570
571
  this.logger.trace(`validatePropagatedAttestation: ${result}`, {
571
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString(),
572
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
572
573
  [Attributes.P2P_ID]: source.toString(),
573
574
  });
574
575
  return { result, obj: attestation };
@@ -594,12 +595,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
594
595
  await this.mempools.attestationPool!.addAttestations([attestation]);
595
596
  }
596
597
 
597
- private async processBlockFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
598
+ private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
598
599
  const validationFunc = async () => {
599
- const block = BlockProposal.fromBuffer(Buffer.from(msg.data));
600
+ const block = BlockProposal.fromBuffer(payloadData);
600
601
  const result = await this.validateBlockProposal(source, block);
601
602
  this.logger.trace(`validatePropagatedBlock: ${result}`, {
602
- [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
603
+ [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
603
604
  [Attributes.P2P_ID]: source.toString(),
604
605
  });
605
606
  return { result, obj: block };
@@ -629,6 +630,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
629
630
  block: block.blockNumber.toNumber(),
630
631
  },
631
632
  );
633
+ // Mark the txs in this proposal as non-evictable
634
+ await this.mempools.txPool.markTxsAsNonEvictable(block.payload.txHashes);
632
635
  const attestation = await this.blockReceivedCallback(block);
633
636
 
634
637
  // TODO: fix up this pattern - the abstraction is not nice
@@ -652,8 +655,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
652
655
  * @param attestation - The attestation to broadcast.
653
656
  */
654
657
  @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
655
- [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
656
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
658
+ [Attributes.BLOCK_NUMBER]: attestation.blockNumber.toNumber(),
659
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
657
660
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
658
661
  [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
659
662
  }))
@@ -720,21 +723,25 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
720
723
  private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
721
724
  const blockNumber = (await this.archiver.getBlockNumber()) + 1;
722
725
  const messageValidators = await this.createMessageValidators(blockNumber);
723
- const outcome = await this.runValidations(tx, messageValidators);
724
726
 
725
- if (outcome.allPassed) {
726
- return true;
727
- }
728
- const { name } = outcome.failure;
729
- let { severity } = outcome.failure;
727
+ for (const validator of messageValidators) {
728
+ const outcome = await this.runValidations(tx, validator);
730
729
 
731
- // Double spend validator has a special case handler
732
- if (name === 'doubleSpendValidator') {
733
- severity = await this.handleDoubleSpendFailure(tx, blockNumber);
734
- }
730
+ if (outcome.allPassed) {
731
+ continue;
732
+ }
733
+ const { name } = outcome.failure;
734
+ let { severity } = outcome.failure;
735
+
736
+ // Double spend validator has a special case handler
737
+ if (name === 'doubleSpendValidator') {
738
+ severity = await this.handleDoubleSpendFailure(tx, blockNumber);
739
+ }
735
740
 
736
- this.peerManager.penalizePeer(peerId, severity);
737
- return false;
741
+ this.peerManager.penalizePeer(peerId, severity);
742
+ return false;
743
+ }
744
+ return true;
738
745
  }
739
746
 
740
747
  private async getGasFees(blockNumber: number): Promise<GasFees> {
@@ -748,6 +755,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
748
755
  return gasFees;
749
756
  }
750
757
 
758
+ public async validate(txs: Tx[]): Promise<void> {
759
+ const blockNumber = (await this.archiver.getBlockNumber()) + 1;
760
+ const messageValidators = await this.createMessageValidators(blockNumber);
761
+
762
+ await Promise.all(
763
+ txs.map(async tx => {
764
+ for (const validator of messageValidators) {
765
+ const outcome = await this.runValidations(tx, validator);
766
+ if (!outcome.allPassed) {
767
+ throw new Error('Invalid tx detected', { cause: { outcome } });
768
+ }
769
+ }
770
+ }),
771
+ );
772
+ }
773
+
751
774
  /**
752
775
  * Create message validators for the given block number.
753
776
  *
@@ -757,51 +780,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
757
780
  * @param blockNumber - The block number to create validators for.
758
781
  * @returns The message validators.
759
782
  */
760
- private async createMessageValidators(blockNumber: number): Promise<Record<string, MessageValidator>> {
761
- const merkleTree = this.worldStateSynchronizer.getCommitted();
783
+ private async createMessageValidators(blockNumber: number): Promise<Record<string, MessageValidator>[]> {
762
784
  const gasFees = await this.getGasFees(blockNumber - 1);
763
785
  const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
764
786
 
765
- return {
766
- dataValidator: {
767
- validator: new DataTxValidator(),
768
- severity: PeerErrorSeverity.HighToleranceError,
769
- },
770
- metadataValidator: {
771
- validator: new MetadataTxValidator(
772
- new Fr(this.config.l1ChainId),
773
- new Fr(this.config.rollupVersion),
774
- new Fr(blockNumber),
775
- ),
776
- severity: PeerErrorSeverity.HighToleranceError,
777
- },
778
- proofValidator: {
779
- validator: new TxProofValidator(this.proofVerifier),
780
- severity: PeerErrorSeverity.MidToleranceError,
781
- },
782
- doubleSpendValidator: {
783
- validator: new DoubleSpendTxValidator({
784
- nullifiersExist: async (nullifiers: Buffer[]) => {
785
- const merkleTree = this.worldStateSynchronizer.getCommitted();
786
- const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
787
- return indices.map(index => index !== undefined);
788
- },
789
- }),
790
- severity: PeerErrorSeverity.HighToleranceError,
791
- },
792
- gasValidator: {
793
- validator: new GasTxValidator(
794
- new DatabasePublicStateSource(merkleTree),
795
- ProtocolContractAddress.FeeJuice,
796
- gasFees,
797
- ),
798
- severity: PeerErrorSeverity.HighToleranceError,
799
- },
800
- phasesValidator: {
801
- validator: new PhasesTxValidator(this.archiver, allowedInSetup, blockNumber),
802
- severity: PeerErrorSeverity.MidToleranceError,
803
- },
804
- };
787
+ return createTxMessageValidators(
788
+ blockNumber,
789
+ this.worldStateSynchronizer,
790
+ gasFees,
791
+ this.config.l1ChainId,
792
+ this.config.rollupVersion,
793
+ protocolContractTreeRoot,
794
+ this.archiver,
795
+ this.proofVerifier,
796
+ allowedInSetup,
797
+ );
805
798
  }
806
799
 
807
800
  /**
@@ -820,24 +813,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
820
813
  });
821
814
 
822
815
  // A promise that resolves when all validations have been run
823
- const allValidations = Promise.all(validationPromises);
824
-
825
- // A promise that resolves when the first validation fails
826
- const firstFailure = Promise.race(
827
- validationPromises.map(async promise => {
828
- const result = await promise;
829
- return result.isValid ? new Promise(() => {}) : result;
830
- }),
831
- );
832
-
833
- // Wait for the first validation to fail or all validations to pass
834
- const result = await Promise.race([
835
- allValidations.then(() => ({ allPassed: true as const })),
836
- firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })),
837
- ]);
838
-
839
- // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
840
- return result;
816
+ const allValidations = await Promise.all(validationPromises);
817
+ const failed = allValidations.find(x => !x.isValid);
818
+ if (failed) {
819
+ return {
820
+ allPassed: false,
821
+ failure: {
822
+ isValid: { result: 'invalid' as const, reason: ['Failed validation'] },
823
+ name: failed.name,
824
+ severity: failed.severity,
825
+ },
826
+ };
827
+ } else {
828
+ return {
829
+ allPassed: true,
830
+ };
831
+ }
841
832
  }
842
833
 
843
834
  /**
@@ -881,8 +872,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
881
872
  * @returns True if the attestation is valid, false otherwise.
882
873
  */
883
874
  @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
884
- [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
885
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
875
+ [Attributes.BLOCK_NUMBER]: attestation.blockNumber.toNumber(),
876
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
886
877
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
887
878
  [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
888
879
  }))
@@ -903,7 +894,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
903
894
  * @returns True if the block proposal is valid, false otherwise.
904
895
  */
905
896
  @trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
906
- [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
897
+ [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
907
898
  }))
908
899
  public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
909
900
  const severity = await this.blockProposalValidator.validate(block);
@@ -926,7 +917,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
926
917
  const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
927
918
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
928
919
 
929
- const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message.toBuffer());
920
+ const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
930
921
  this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
931
922
  p2pMessageIdentifier: identifier,
932
923
  sourcePeer: this.node.peerId.toString(),
@@ -18,7 +18,10 @@ export class PeerManagerMetrics {
18
18
 
19
19
  public readonly tracer: Tracer;
20
20
 
21
- constructor(public readonly telemetryClient: TelemetryClient = getTelemetryClient(), name = 'PeerManager') {
21
+ constructor(
22
+ public readonly telemetryClient: TelemetryClient = getTelemetryClient(),
23
+ name = 'PeerManager',
24
+ ) {
22
25
  this.tracer = telemetryClient.getTracer(name);
23
26
 
24
27
  const meter = telemetryClient.getMeter(name);
@@ -14,6 +14,7 @@ import type { PubSubLibp2p } from '../../util.js';
14
14
  import { ReqRespSubProtocol } from '../reqresp/interface.js';
15
15
  import { GoodByeReason, prettyGoodbyeReason } from '../reqresp/protocols/goodbye.js';
16
16
  import type { ReqResp } from '../reqresp/reqresp.js';
17
+ import { ReqRespStatus } from '../reqresp/status.js';
17
18
  import type { PeerDiscoveryService } from '../service.js';
18
19
  import { PeerManagerMetrics } from './metrics.js';
19
20
  import { PeerScoreState, type PeerScoring } from './peer_scoring.js';
@@ -22,6 +23,7 @@ const MAX_DIAL_ATTEMPTS = 3;
22
23
  const MAX_CACHED_PEERS = 100;
23
24
  const MAX_CACHED_PEER_AGE_MS = 5 * 60 * 1000; // 5 minutes
24
25
  const FAILED_PEER_BAN_TIME_MS = 5 * 60 * 1000; // 5 minutes timeout after failing MAX_DIAL_ATTEMPTS
26
+ const GOODBYE_DIAL_TIMEOUT_MS = 1000;
25
27
 
26
28
  type CachedPeer = {
27
29
  peerId: PeerId;
@@ -455,7 +457,22 @@ export class PeerManager {
455
457
  this.metrics.recordGoodbyeSent(reason);
456
458
 
457
459
  try {
458
- await this.reqresp.sendRequestToPeer(peer, ReqRespSubProtocol.GOODBYE, Buffer.from([reason]));
460
+ const resp = await this.reqresp.sendRequestToPeer(
461
+ peer,
462
+ ReqRespSubProtocol.GOODBYE,
463
+ Buffer.from([reason]),
464
+ GOODBYE_DIAL_TIMEOUT_MS,
465
+ );
466
+
467
+ if (resp.status === ReqRespStatus.FAILURE) {
468
+ this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}`);
469
+ } else if (resp.status === ReqRespStatus.SUCCESS) {
470
+ this.logger.verbose(`Sent goodbye to peer ${peer.toString()}`);
471
+ } else {
472
+ this.logger.debug(
473
+ `Unexpected status sending goodbye to peer ${peer.toString()}: ${ReqRespStatus[resp.status]}`,
474
+ );
475
+ }
459
476
  } catch (error) {
460
477
  this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}: ${error}`);
461
478
  } finally {
@@ -22,7 +22,11 @@ export class BatchConnectionSampler {
22
22
  private readonly batch: PeerId[] = [];
23
23
  private readonly requestsPerPeer: number;
24
24
 
25
- constructor(private readonly connectionSampler: ConnectionSampler, batchSize: number, maxPeers: number) {
25
+ constructor(
26
+ private readonly connectionSampler: ConnectionSampler,
27
+ batchSize: number,
28
+ maxPeers: number,
29
+ ) {
26
30
  if (maxPeers <= 0) {
27
31
  throw new Error('Max peers cannot be 0');
28
32
  }
@@ -26,7 +26,7 @@ export class RandomSampler {
26
26
  export class ConnectionSampler {
27
27
  private readonly logger = createLogger('p2p:reqresp:connection-sampler');
28
28
  private cleanupInterval: NodeJS.Timeout;
29
- private abortController: AbortController = new AbortController();
29
+ private dialAttempts: AbortController[] = [];
30
30
 
31
31
  private readonly activeConnectionsCount: Map<PeerId, number> = new Map();
32
32
  private readonly streams: Map<string, StreamAndPeerId> = new Map();
@@ -51,7 +51,10 @@ export class ConnectionSampler {
51
51
  this.logger.info('Stopping connection sampler');
52
52
  clearInterval(this.cleanupInterval);
53
53
 
54
- this.abortController.abort();
54
+ for (const attempt of this.dialAttempts) {
55
+ attempt.abort();
56
+ }
57
+ this.dialAttempts = [];
55
58
  await this.dialQueue.end();
56
59
 
57
60
  // Close all active streams
@@ -184,24 +187,44 @@ export class ConnectionSampler {
184
187
  * @param protocol - The protocol
185
188
  * @returns The stream
186
189
  */
187
- async dialProtocol(peerId: PeerId, protocol: string): Promise<Stream> {
190
+ async dialProtocol(peerId: PeerId, protocol: string, timeout?: number): Promise<Stream> {
188
191
  // Dialling at the same time can cause race conditions where two different streams
189
192
  // end up with the same id, hence a serial queue
190
- const stream = await this.dialQueue.put(() =>
191
- this.libp2p.dialProtocol(peerId, protocol, { signal: this.abortController.signal }),
192
- );
193
-
194
- this.streams.set(stream.id, { stream, peerId });
195
- const updatedActiveConnectionsCount = (this.activeConnectionsCount.get(peerId) ?? 0) + 1;
196
- this.activeConnectionsCount.set(peerId, updatedActiveConnectionsCount);
197
-
198
- this.logger.trace('Dialed protocol', {
199
- streamId: stream.id,
200
- protocol,
201
- peerId: peerId.toString(),
202
- activeConnectionsCount: updatedActiveConnectionsCount,
203
- });
204
- return stream;
193
+ this.logger.debug(`Dial queue length: ${this.dialQueue.length()}`);
194
+
195
+ const abortController = new AbortController();
196
+ this.dialAttempts.push(abortController);
197
+ let timeoutHandle: NodeJS.Timeout | undefined;
198
+ if (timeout) {
199
+ timeoutHandle = setTimeout(() => abortController.abort(), timeout);
200
+ }
201
+
202
+ try {
203
+ const stream = await this.dialQueue.put(() =>
204
+ this.libp2p.dialProtocol(peerId, protocol, { signal: abortController.signal }),
205
+ );
206
+
207
+ this.streams.set(stream.id, { stream, peerId });
208
+ const updatedActiveConnectionsCount = (this.activeConnectionsCount.get(peerId) ?? 0) + 1;
209
+ this.activeConnectionsCount.set(peerId, updatedActiveConnectionsCount);
210
+
211
+ this.logger.trace('Dialed protocol', {
212
+ streamId: stream.id,
213
+ protocol,
214
+ peerId: peerId.toString(),
215
+ activeConnectionsCount: updatedActiveConnectionsCount,
216
+ });
217
+ return stream;
218
+ } finally {
219
+ if (timeoutHandle) {
220
+ clearTimeout(timeoutHandle);
221
+ }
222
+
223
+ const idx = this.dialAttempts.indexOf(abortController);
224
+ if (idx > -1) {
225
+ this.dialAttempts.splice(idx, 1);
226
+ }
227
+ }
205
228
  }
206
229
 
207
230
  /**
@@ -213,7 +236,7 @@ export class ConnectionSampler {
213
236
  try {
214
237
  const streamAndPeerId = this.streams.get(streamId);
215
238
  if (!streamAndPeerId) {
216
- this.logger.warn(`Stream ${streamId} not found`);
239
+ this.logger.debug(`Stream ${streamId} not found`);
217
240
  return;
218
241
  }
219
242
 
@@ -11,7 +11,10 @@ export class ReqRespMetrics {
11
11
  private readonly failedOutboundRequests: UpDownCounter;
12
12
  private readonly failedInboundRequests: UpDownCounter;
13
13
 
14
- constructor(readonly telemetryClient: TelemetryClient, name = 'ReqResp') {
14
+ constructor(
15
+ readonly telemetryClient: TelemetryClient,
16
+ name = 'ReqResp',
17
+ ) {
15
18
  this.tracer = telemetryClient.getTracer(name);
16
19
 
17
20
  const meter = telemetryClient.getMeter(name);
@@ -34,7 +34,7 @@ export function decodeGoodbyeReason(buffer: Buffer): GoodByeReason {
34
34
  throw new Error('Invalid goodbye reason buffer length');
35
35
  }
36
36
  return buffer[0] as GoodByeReason;
37
- } catch (error) {
37
+ } catch {
38
38
  return GoodByeReason.UNKNOWN;
39
39
  }
40
40
  }
@@ -180,7 +180,10 @@ export class RequestResponseRateLimiter {
180
180
 
181
181
  private cleanupInterval: NodeJS.Timeout | undefined = undefined;
182
182
 
183
- constructor(private peerScoring: PeerScoring, rateLimits: ReqRespSubProtocolRateLimits = DEFAULT_RATE_LIMITS) {
183
+ constructor(
184
+ private peerScoring: PeerScoring,
185
+ rateLimits: ReqRespSubProtocolRateLimits = DEFAULT_RATE_LIMITS,
186
+ ) {
184
187
  this.subProtocolRateLimiters = new Map();
185
188
 
186
189
  for (const [subProtocol, protocolLimits] of Object.entries(rateLimits)) {
@@ -262,7 +262,7 @@ export class ReqResp {
262
262
  subProtocol: SubProtocol,
263
263
  requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
264
264
  timeoutMs = 10000,
265
- maxPeers = Math.min(10, requests.length),
265
+ maxPeers = Math.max(10, Math.ceil(requests.length / 3)),
266
266
  maxRetryAttempts = 3,
267
267
  ): Promise<(InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined)[]> {
268
268
  const responseValidator = this.subProtocolValidators[subProtocol];
@@ -292,7 +292,8 @@ export class ReqResp {
292
292
  let retryAttempts = 0;
293
293
  while (pendingRequestIndices.size > 0 && batchSampler.activePeerCount > 0 && retryAttempts < maxRetryAttempts) {
294
294
  // Process requests in parallel for each available peer
295
- const requestBatches = new Map<PeerId, number[]>();
295
+ type BatchEntry = { peerId: PeerId; indices: number[] };
296
+ const requestBatches = new Map<string, BatchEntry>();
296
297
 
297
298
  // Group requests by peer
298
299
  for (const requestIndex of pendingRequestIndices) {
@@ -300,11 +301,11 @@ export class ReqResp {
300
301
  if (!peer) {
301
302
  break;
302
303
  }
303
-
304
- if (!requestBatches.has(peer)) {
305
- requestBatches.set(peer, []);
304
+ const peerAsString = peer.toString();
305
+ if (!requestBatches.has(peerAsString)) {
306
+ requestBatches.set(peerAsString, { peerId: peer, indices: [] });
306
307
  }
307
- requestBatches.get(peer)!.push(requestIndex);
308
+ requestBatches.get(peerAsString)!.indices.push(requestIndex);
308
309
  }
309
310
 
310
311
  // Make parallel requests for each peer's batch
@@ -316,7 +317,7 @@ export class ReqResp {
316
317
  // while simultaneously Peer Id 1 will send requests 4, 5, 6, 7 in serial
317
318
 
318
319
  const batchResults = await Promise.all(
319
- Array.from(requestBatches.entries()).map(async ([peer, indices]) => {
320
+ Array.from(requestBatches.entries()).map(async ([peerAsString, { peerId: peer, indices }]) => {
320
321
  try {
321
322
  // Requests all going to the same peer are sent synchronously
322
323
  const peerResults: { index: number; response: InstanceType<SubProtocolMap[SubProtocol]['response']> }[] =
@@ -327,9 +328,7 @@ export class ReqResp {
327
328
  // Check the status of the response buffer
328
329
  if (response.status !== ReqRespStatus.SUCCESS) {
329
330
  this.logger.debug(
330
- `Request to peer ${peer.toString()} failed with status ${prettyPrintReqRespStatus(
331
- response.status,
332
- )}`,
331
+ `Request to peer ${peerAsString} failed with status ${prettyPrintReqRespStatus(response.status)}`,
333
332
  );
334
333
 
335
334
  // If we hit a rate limit or some failure, we remove the peer and return the results,
@@ -350,7 +349,7 @@ export class ReqResp {
350
349
 
351
350
  return { peer, results: peerResults };
352
351
  } catch (error) {
353
- this.logger.debug(`Failed batch request to peer ${peer.toString()}:`, error);
352
+ this.logger.debug(`Failed batch request to peer ${peerAsString}:`, error);
354
353
  batchSampler.removePeerAndReplace(peer);
355
354
  return { peer, results: [] };
356
355
  }
@@ -421,12 +420,13 @@ export class ReqResp {
421
420
  peerId: PeerId,
422
421
  subProtocol: ReqRespSubProtocol,
423
422
  payload: Buffer,
423
+ dialTimeout?: number,
424
424
  ): Promise<ReqRespResponse> {
425
425
  let stream: Stream | undefined;
426
426
  try {
427
427
  this.metrics.recordRequestSent(subProtocol);
428
428
 
429
- stream = await this.connectionSampler.dialProtocol(peerId, subProtocol);
429
+ stream = await this.connectionSampler.dialProtocol(peerId, subProtocol, dialTimeout);
430
430
 
431
431
  // Open the stream with a timeout
432
432
  const result = await executeTimeout<ReqRespResponse>(