@aztec/p2p 0.86.0-starknet.1 → 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 (132) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/client/factory.d.ts +1 -1
  3. package/dest/client/factory.d.ts.map +1 -1
  4. package/dest/client/factory.js +11 -13
  5. package/dest/client/interface.d.ts +10 -1
  6. package/dest/client/interface.d.ts.map +1 -1
  7. package/dest/client/p2p_client.d.ts +18 -6
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +103 -53
  10. package/dest/config.d.ts +5 -6
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +6 -11
  13. package/dest/enr/generate-enr.d.ts +7 -0
  14. package/dest/enr/generate-enr.d.ts.map +1 -1
  15. package/dest/enr/generate-enr.js +23 -2
  16. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  17. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  18. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +3 -0
  19. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  20. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +44 -14
  21. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +2 -0
  22. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  23. package/dest/mem_pools/tx_pool/memory_tx_pool.js +6 -0
  24. package/dest/mem_pools/tx_pool/tx_pool.d.ts +7 -0
  25. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  26. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  27. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +13 -5
  28. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  29. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  30. package/dest/msg_validators/tx_validator/data_validator.js +15 -14
  31. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +0 -2
  32. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  33. package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
  34. package/dest/msg_validators/tx_validator/factory.d.ts +14 -0
  35. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
  36. package/dest/msg_validators/tx_validator/factory.js +62 -0
  37. package/dest/msg_validators/tx_validator/gas_validator.js +3 -3
  38. package/dest/msg_validators/tx_validator/metadata_validator.js +5 -5
  39. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  40. package/dest/msg_validators/tx_validator/phases_validator.js +1 -1
  41. package/dest/msg_validators/tx_validator/tx_proof_validator.js +1 -1
  42. package/dest/services/discv5/discV5_service.d.ts +2 -2
  43. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  44. package/dest/services/discv5/discV5_service.js +9 -13
  45. package/dest/services/dummy_service.d.ts +3 -3
  46. package/dest/services/dummy_service.d.ts.map +1 -1
  47. package/dest/services/dummy_service.js +6 -1
  48. package/dest/services/encoding.d.ts +1 -3
  49. package/dest/services/encoding.d.ts.map +1 -1
  50. package/dest/services/libp2p/libp2p_service.d.ts +4 -2
  51. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  52. package/dest/services/libp2p/libp2p_service.js +87 -88
  53. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  54. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  55. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  56. package/dest/services/peer-manager/peer_manager.js +11 -2
  57. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  58. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +2 -2
  59. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  60. package/dest/services/reqresp/connection-sampler/connection_sampler.js +41 -21
  61. package/dest/services/reqresp/interface.d.ts +1 -3
  62. package/dest/services/reqresp/interface.d.ts.map +1 -1
  63. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  64. package/dest/services/reqresp/protocols/goodbye.d.ts +0 -2
  65. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  66. package/dest/services/reqresp/protocols/goodbye.js +1 -1
  67. package/dest/services/reqresp/protocols/ping.d.ts +0 -2
  68. package/dest/services/reqresp/protocols/ping.d.ts.map +1 -1
  69. package/dest/services/reqresp/protocols/status.d.ts +0 -2
  70. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  71. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  72. package/dest/services/reqresp/reqresp.d.ts +1 -3
  73. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  74. package/dest/services/reqresp/reqresp.js +2 -2
  75. package/dest/services/service.d.ts +3 -2
  76. package/dest/services/service.d.ts.map +1 -1
  77. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  78. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  79. package/dest/test-helpers/make-test-p2p-clients.js +2 -2
  80. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  81. package/dest/test-helpers/reqresp-nodes.js +1 -1
  82. package/dest/testbench/p2p_client_testbench_worker.js +9 -6
  83. package/dest/testbench/testbench.js +1 -1
  84. package/dest/testbench/worker_client_manager.d.ts +0 -1
  85. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  86. package/dest/testbench/worker_client_manager.js +2 -2
  87. package/dest/types/index.d.ts +1 -0
  88. package/dest/types/index.d.ts.map +1 -1
  89. package/dest/types/index.js +1 -0
  90. package/dest/versioning.d.ts +2 -2
  91. package/dest/versioning.d.ts.map +1 -1
  92. package/dest/versioning.js +6 -1
  93. package/package.json +15 -15
  94. package/src/bootstrap/bootstrap.ts +1 -1
  95. package/src/client/factory.ts +33 -32
  96. package/src/client/interface.ts +13 -1
  97. package/src/client/p2p_client.ts +129 -55
  98. package/src/config.ts +11 -18
  99. package/src/enr/generate-enr.ts +33 -3
  100. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +2 -2
  101. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +4 -1
  102. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +72 -34
  103. package/src/mem_pools/tx_pool/memory_tx_pool.ts +12 -1
  104. package/src/mem_pools/tx_pool/tx_pool.ts +9 -0
  105. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +9 -3
  106. package/src/msg_validators/tx_validator/block_header_validator.ts +1 -1
  107. package/src/msg_validators/tx_validator/data_validator.ts +24 -18
  108. package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
  109. package/src/msg_validators/tx_validator/factory.ts +94 -0
  110. package/src/msg_validators/tx_validator/gas_validator.ts +3 -3
  111. package/src/msg_validators/tx_validator/metadata_validator.ts +5 -5
  112. package/src/msg_validators/tx_validator/phases_validator.ts +6 -2
  113. package/src/msg_validators/tx_validator/tx_proof_validator.ts +1 -1
  114. package/src/services/discv5/discV5_service.ts +14 -12
  115. package/src/services/dummy_service.ts +8 -2
  116. package/src/services/libp2p/libp2p_service.ts +95 -107
  117. package/src/services/peer-manager/metrics.ts +4 -1
  118. package/src/services/peer-manager/peer_manager.ts +18 -1
  119. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +5 -1
  120. package/src/services/reqresp/connection-sampler/connection_sampler.ts +42 -19
  121. package/src/services/reqresp/metrics.ts +4 -1
  122. package/src/services/reqresp/protocols/goodbye.ts +1 -1
  123. package/src/services/reqresp/rate-limiter/rate_limiter.ts +4 -1
  124. package/src/services/reqresp/reqresp.ts +2 -1
  125. package/src/services/service.ts +4 -1
  126. package/src/test-helpers/make-test-p2p-clients.ts +2 -1
  127. package/src/test-helpers/reqresp-nodes.ts +1 -1
  128. package/src/testbench/p2p_client_testbench_worker.ts +8 -4
  129. package/src/testbench/testbench.ts +1 -1
  130. package/src/testbench/worker_client_manager.ts +2 -2
  131. package/src/types/index.ts +1 -0
  132. package/src/versioning.ts +8 -1
@@ -10,9 +10,9 @@ import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';
10
10
  import EventEmitter from 'events';
11
11
 
12
12
  import type { P2PConfig } from '../../config.js';
13
+ import { createNodeENR } from '../../enr/generate-enr.js';
13
14
  import { AZTEC_ENR_KEY, Discv5Event, PeerEvent } from '../../types/index.js';
14
15
  import { convertToMultiaddr } from '../../util.js';
15
- import { setAztecEnrKey } from '../../versioning.js';
16
16
  import { type PeerDiscoveryService, PeerDiscoveryState } from '../service.js';
17
17
 
18
18
  const delayBeforeStart = 2000; // 2sec
@@ -22,7 +22,7 @@ const delayBeforeStart = 2000; // 2sec
22
22
  */
23
23
  export class DiscV5Service extends EventEmitter implements PeerDiscoveryService {
24
24
  /** The Discv5 instance */
25
- private discv5: Discv5 & Discv5EventEmitter;
25
+ private discv5: Discv5EventEmitter;
26
26
 
27
27
  /** This instance's ENR */
28
28
  private enr: SignableENR;
@@ -47,6 +47,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
47
47
  constructor(
48
48
  private peerId: PeerId,
49
49
  private config: P2PConfig,
50
+ private readonly packageVersion: string,
50
51
  telemetry: TelemetryClient = getTelemetryClient(),
51
52
  private logger = createLogger('p2p:discv5_service'),
52
53
  configOverrides: Partial<IDiscv5CreateOptions> = {},
@@ -57,10 +58,6 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
57
58
  this.bootstrapNodeEnrs = bootstrapNodes.map(x => ENR.decodeTxt(x));
58
59
  const privatePeerEnrs = new Set(privatePeers);
59
60
  this.trustedPeerEnrs = trustedPeers.filter(x => !privatePeerEnrs.has(x)).map(x => ENR.decodeTxt(x));
60
- // create ENR from PeerId
61
- this.enr = SignableENR.createFromPeerId(peerId);
62
- // Add aztec identification to ENR
63
- this.versions = setAztecEnrKey(this.enr, config);
64
61
 
65
62
  // If no overridden broadcast port is provided, use the p2p port as the broadcast port
66
63
  if (!p2pBroadcastPort) {
@@ -72,19 +69,24 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
72
69
  ip4: multiaddr(convertToMultiaddr(config.listenAddress, p2pPort, 'udp')),
73
70
  };
74
71
 
72
+ let multiAddrUdp, multiAddrTcp;
75
73
  if (p2pIp) {
76
- const multiAddrTcp = multiaddr(
74
+ multiAddrTcp = multiaddr(
77
75
  `${convertToMultiaddr(p2pIp!, config.p2pBroadcastPort!, 'tcp')}/p2p/${peerId.toString()}`,
78
76
  );
79
- const multiAddrUdp = multiaddr(
77
+ multiAddrUdp = multiaddr(
80
78
  `${convertToMultiaddr(p2pIp!, config.p2pBroadcastPort!, 'udp')}/p2p/${peerId.toString()}`,
81
79
  );
82
-
83
- // set location multiaddr in ENR record
84
- this.enr.setLocationMultiaddr(multiAddrUdp);
85
- this.enr.setLocationMultiaddr(multiAddrTcp);
86
80
  }
87
81
 
82
+ ({ enr: this.enr, versions: this.versions } = createNodeENR(
83
+ peerId,
84
+ multiAddrUdp,
85
+ multiAddrTcp,
86
+ config,
87
+ this.packageVersion,
88
+ ));
89
+
88
90
  const metricsRegistry = new OtelMetricsAdapter(telemetry);
89
91
  this.discv5 = Discv5.create({
90
92
  enr: this.enr,
@@ -1,6 +1,6 @@
1
1
  import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
2
2
  import type { BlockAttestation, BlockProposal, Gossipable } from '@aztec/stdlib/p2p';
3
- import { TxHash } from '@aztec/stdlib/tx';
3
+ import { Tx, TxHash } from '@aztec/stdlib/tx';
4
4
 
5
5
  import type { ENR } from '@chainsafe/enr';
6
6
  import type { PeerId } from '@libp2p/interface';
@@ -38,7 +38,9 @@ export class DummyP2PService implements P2PService {
38
38
  * Called to have the given message propagated through the P2P network.
39
39
  * @param _ - The message to be propagated.
40
40
  */
41
- public propagate<T extends Gossipable>(_: T) {}
41
+ public propagate<T extends Gossipable>(_: T) {
42
+ return Promise.resolve();
43
+ }
42
44
 
43
45
  /**
44
46
  * Called upon receipt of settled transactions.
@@ -84,6 +86,10 @@ export class DummyP2PService implements P2PService {
84
86
  public getEnr(): undefined {
85
87
  return undefined;
86
88
  }
89
+
90
+ validate(_txs: Tx[]): Promise<void> {
91
+ return Promise.resolve();
92
+ }
87
93
  }
88
94
 
89
95
  /**
@@ -1,11 +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 { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
8
- import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts';
6
+ import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
9
7
  import type { L2BlockSource } from '@aztec/stdlib/block';
10
8
  import type { ContractDataSource } from '@aztec/stdlib/contract';
11
9
  import { GasFees } from '@aztec/stdlib/gas';
@@ -15,13 +13,14 @@ import {
15
13
  BlockProposal,
16
14
  type Gossipable,
17
15
  P2PClientType,
16
+ P2PMessage,
18
17
  PeerErrorSeverity,
19
18
  TopicType,
20
19
  createTopicString,
21
20
  getTopicTypeForClientType,
22
21
  metricsTopicStrToLabels,
23
22
  } from '@aztec/stdlib/p2p';
24
- import { DatabasePublicStateSource, MerkleTreeId } from '@aztec/stdlib/trees';
23
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
25
24
  import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx';
26
25
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
27
26
  import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
@@ -50,14 +49,8 @@ import type { P2PConfig } from '../../config.js';
50
49
  import type { MemPools } from '../../mem_pools/interface.js';
51
50
  import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
52
51
  import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
53
- import {
54
- DataTxValidator,
55
- DoubleSpendTxValidator,
56
- GasTxValidator,
57
- MetadataTxValidator,
58
- PhasesTxValidator,
59
- TxProofValidator,
60
- } 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';
61
54
  import { GossipSubEvent } from '../../types/index.js';
62
55
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
63
56
  import { getVersions } from '../../versioning.js';
@@ -72,13 +65,6 @@ import { pingHandler, reqRespBlockHandler, reqRespTxHandler, statusHandler } fro
72
65
  import { ReqResp } from '../reqresp/reqresp.js';
73
66
  import type { P2PService, PeerDiscoveryService } from '../service.js';
74
67
 
75
- interface MessageValidator {
76
- validator: {
77
- validateTx(tx: Tx): Promise<TxValidationResult>;
78
- };
79
- severity: PeerErrorSeverity;
80
- }
81
-
82
68
  interface ValidationResult {
83
69
  name: string;
84
70
  isValid: TxValidationResult;
@@ -234,7 +220,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
234
220
  tcp({
235
221
  maxConnections: config.maxPeerCount,
236
222
  // socket option: the maximum length of the queue of pending connections
237
- // 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
238
224
  // it's not safe if we increase this number
239
225
  backlog: 5,
240
226
  closeServerOnMaxConnections: {
@@ -271,6 +257,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
271
257
  heartbeatInterval: config.gossipsubInterval,
272
258
  mcacheLength: config.gossipsubMcacheLength,
273
259
  mcacheGossip: config.gossipsubMcacheGossip,
260
+ seenTTL: config.gossipsubSeenTTL,
274
261
  msgIdFn: getMsgIdFn,
275
262
  msgIdToStrFn: msgIdToStrFn,
276
263
  fastMsgIdFn: fastMsgIdFn,
@@ -490,14 +477,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
490
477
  /**
491
478
  * Publishes data to a topic.
492
479
  * @param topic - The topic to publish to.
493
- * @param data - The data to publish.
480
+ * @param data - The message to publish.
494
481
  * @returns The number of recipients the data was sent to.
495
482
  */
496
- private async publishToTopic(topic: string, data: Uint8Array) {
483
+ private async publishToTopic(topic: string, message: Gossipable) {
497
484
  if (!this.node.services.pubsub) {
498
485
  throw new Error('Pubsub service not available.');
499
486
  }
500
- 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());
501
493
 
502
494
  return result.recipients.length;
503
495
  }
@@ -508,14 +500,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
508
500
  * @param data - The message data
509
501
  */
510
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
+ });
511
511
  if (msg.topic === this.topicStrings[TopicType.tx]) {
512
- await this.handleGossipedTx(msg, msgId, source);
512
+ await this.handleGossipedTx(p2pMessage.payload, msgId, source);
513
513
  }
514
514
  if (msg.topic === this.topicStrings[TopicType.block_attestation] && this.clientType === P2PClientType.Full) {
515
- await this.processAttestationFromPeer(msg, msgId, source);
515
+ await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
516
516
  }
517
517
  if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
518
- await this.processBlockFromPeer(msg, msgId, source);
518
+ await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
519
519
  }
520
520
 
521
521
  return;
@@ -541,9 +541,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
541
541
  return resultAndObj;
542
542
  }
543
543
 
544
- protected async handleGossipedTx(msg: Message, msgId: string, source: PeerId) {
544
+ protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
545
545
  const validationFunc = async () => {
546
- const tx = Tx.fromBuffer(Buffer.from(msg.data));
546
+ const tx = Tx.fromBuffer(payloadData);
547
547
  const result = await this.validatePropagatedTx(tx, source);
548
548
  return { result, obj: tx };
549
549
  };
@@ -564,9 +564,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
564
564
  *
565
565
  * @param attestation - The attestation to process.
566
566
  */
567
- private async processAttestationFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
567
+ private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
568
568
  const validationFunc = async () => {
569
- const attestation = BlockAttestation.fromBuffer(Buffer.from(msg.data));
569
+ const attestation = BlockAttestation.fromBuffer(payloadData);
570
570
  const result = await this.validateAttestation(source, attestation);
571
571
  this.logger.trace(`validatePropagatedAttestation: ${result}`, {
572
572
  [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
@@ -595,9 +595,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
595
595
  await this.mempools.attestationPool!.addAttestations([attestation]);
596
596
  }
597
597
 
598
- private async processBlockFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
598
+ private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
599
599
  const validationFunc = async () => {
600
- const block = BlockProposal.fromBuffer(Buffer.from(msg.data));
600
+ const block = BlockProposal.fromBuffer(payloadData);
601
601
  const result = await this.validateBlockProposal(source, block);
602
602
  this.logger.trace(`validatePropagatedBlock: ${result}`, {
603
603
  [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
@@ -630,6 +630,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
630
630
  block: block.blockNumber.toNumber(),
631
631
  },
632
632
  );
633
+ // Mark the txs in this proposal as non-evictable
634
+ await this.mempools.txPool.markTxsAsNonEvictable(block.payload.txHashes);
633
635
  const attestation = await this.blockReceivedCallback(block);
634
636
 
635
637
  // TODO: fix up this pattern - the abstraction is not nice
@@ -721,21 +723,25 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
721
723
  private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
722
724
  const blockNumber = (await this.archiver.getBlockNumber()) + 1;
723
725
  const messageValidators = await this.createMessageValidators(blockNumber);
724
- const outcome = await this.runValidations(tx, messageValidators);
725
726
 
726
- if (outcome.allPassed) {
727
- return true;
728
- }
729
- const { name } = outcome.failure;
730
- let { severity } = outcome.failure;
727
+ for (const validator of messageValidators) {
728
+ const outcome = await this.runValidations(tx, validator);
731
729
 
732
- // Double spend validator has a special case handler
733
- if (name === 'doubleSpendValidator') {
734
- severity = await this.handleDoubleSpendFailure(tx, blockNumber);
735
- }
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
+ }
736
740
 
737
- this.peerManager.penalizePeer(peerId, severity);
738
- return false;
741
+ this.peerManager.penalizePeer(peerId, severity);
742
+ return false;
743
+ }
744
+ return true;
739
745
  }
740
746
 
741
747
  private async getGasFees(blockNumber: number): Promise<GasFees> {
@@ -749,6 +755,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
749
755
  return gasFees;
750
756
  }
751
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
+
752
774
  /**
753
775
  * Create message validators for the given block number.
754
776
  *
@@ -758,53 +780,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
758
780
  * @param blockNumber - The block number to create validators for.
759
781
  * @returns The message validators.
760
782
  */
761
- private async createMessageValidators(blockNumber: number): Promise<Record<string, MessageValidator>> {
762
- const merkleTree = this.worldStateSynchronizer.getCommitted();
783
+ private async createMessageValidators(blockNumber: number): Promise<Record<string, MessageValidator>[]> {
763
784
  const gasFees = await this.getGasFees(blockNumber - 1);
764
785
  const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
765
786
 
766
- return {
767
- dataValidator: {
768
- validator: new DataTxValidator(),
769
- severity: PeerErrorSeverity.HighToleranceError,
770
- },
771
- metadataValidator: {
772
- validator: new MetadataTxValidator({
773
- l1ChainId: new Fr(this.config.l1ChainId),
774
- rollupVersion: new Fr(this.config.rollupVersion),
775
- blockNumber: new Fr(blockNumber),
776
- protocolContractTreeRoot,
777
- vkTreeRoot: getVKTreeRoot(),
778
- }),
779
- severity: PeerErrorSeverity.HighToleranceError,
780
- },
781
- proofValidator: {
782
- validator: new TxProofValidator(this.proofVerifier),
783
- severity: PeerErrorSeverity.MidToleranceError,
784
- },
785
- doubleSpendValidator: {
786
- validator: new DoubleSpendTxValidator({
787
- nullifiersExist: async (nullifiers: Buffer[]) => {
788
- const merkleTree = this.worldStateSynchronizer.getCommitted();
789
- const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
790
- return indices.map(index => index !== undefined);
791
- },
792
- }),
793
- severity: PeerErrorSeverity.HighToleranceError,
794
- },
795
- gasValidator: {
796
- validator: new GasTxValidator(
797
- new DatabasePublicStateSource(merkleTree),
798
- ProtocolContractAddress.FeeJuice,
799
- gasFees,
800
- ),
801
- severity: PeerErrorSeverity.HighToleranceError,
802
- },
803
- phasesValidator: {
804
- validator: new PhasesTxValidator(this.archiver, allowedInSetup, blockNumber),
805
- severity: PeerErrorSeverity.MidToleranceError,
806
- },
807
- };
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
+ );
808
798
  }
809
799
 
810
800
  /**
@@ -823,24 +813,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
823
813
  });
824
814
 
825
815
  // A promise that resolves when all validations have been run
826
- const allValidations = Promise.all(validationPromises);
827
-
828
- // A promise that resolves when the first validation fails
829
- const firstFailure = Promise.race(
830
- validationPromises.map(async promise => {
831
- const result = await promise;
832
- return result.isValid ? new Promise(() => {}) : result;
833
- }),
834
- );
835
-
836
- // Wait for the first validation to fail or all validations to pass
837
- const result = await Promise.race([
838
- allValidations.then(() => ({ allPassed: true as const })),
839
- firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })),
840
- ]);
841
-
842
- // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
843
- 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
+ }
844
832
  }
845
833
 
846
834
  /**
@@ -929,7 +917,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
929
917
  const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
930
918
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
931
919
 
932
- const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message.toBuffer());
920
+ const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
933
921
  this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
934
922
  p2pMessageIdentifier: identifier,
935
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)) {
@@ -420,12 +420,13 @@ export class ReqResp {
420
420
  peerId: PeerId,
421
421
  subProtocol: ReqRespSubProtocol,
422
422
  payload: Buffer,
423
+ dialTimeout?: number,
423
424
  ): Promise<ReqRespResponse> {
424
425
  let stream: Stream | undefined;
425
426
  try {
426
427
  this.metrics.recordRequestSent(subProtocol);
427
428
 
428
- stream = await this.connectionSampler.dialProtocol(peerId, subProtocol);
429
+ stream = await this.connectionSampler.dialProtocol(peerId, subProtocol, dialTimeout);
429
430
 
430
431
  // Open the stream with a timeout
431
432
  const result = await executeTimeout<ReqRespResponse>(