@aztec/p2p 0.0.1-commit.733c4a3 → 0.0.1-commit.7b86788

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 (60) hide show
  1. package/dest/client/factory.d.ts +4 -5
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +5 -5
  4. package/dest/client/interface.d.ts +4 -4
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +4 -4
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +1 -1
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
  10. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
  11. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
  12. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +2 -0
  13. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
  14. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  15. package/dest/mem_pools/tx_pool_v2/index.js +1 -1
  16. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +18 -4
  17. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  18. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +36 -7
  19. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
  20. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  21. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +9 -10
  22. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
  23. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  24. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +36 -22
  25. package/dest/services/encoding.d.ts +2 -2
  26. package/dest/services/encoding.d.ts.map +1 -1
  27. package/dest/services/encoding.js +7 -7
  28. package/dest/services/libp2p/libp2p_service.d.ts +6 -7
  29. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  30. package/dest/services/libp2p/libp2p_service.js +7 -10
  31. package/dest/services/reqresp/reqresp.d.ts +1 -1
  32. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  33. package/dest/services/reqresp/reqresp.js +2 -1
  34. package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
  35. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  36. package/dest/test-helpers/make-test-p2p-clients.js +1 -2
  37. package/dest/test-helpers/mock-pubsub.d.ts +2 -3
  38. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  39. package/dest/test-helpers/mock-pubsub.js +2 -2
  40. package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
  41. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  42. package/dest/test-helpers/reqresp-nodes.js +2 -2
  43. package/dest/testbench/p2p_client_testbench_worker.js +5 -5
  44. package/package.json +14 -14
  45. package/src/client/factory.ts +8 -13
  46. package/src/client/interface.ts +3 -9
  47. package/src/client/p2p_client.ts +2 -12
  48. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -2
  49. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
  50. package/src/mem_pools/tx_pool_v2/index.ts +1 -1
  51. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +52 -10
  52. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
  53. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +40 -20
  54. package/src/services/encoding.ts +5 -6
  55. package/src/services/libp2p/libp2p_service.ts +5 -11
  56. package/src/services/reqresp/reqresp.ts +3 -1
  57. package/src/test-helpers/make-test-p2p-clients.ts +0 -2
  58. package/src/test-helpers/mock-pubsub.ts +3 -6
  59. package/src/test-helpers/reqresp-nodes.ts +2 -5
  60. package/src/testbench/p2p_client_testbench_worker.ts +2 -6
@@ -187,6 +187,30 @@ export class TxPoolV2Impl {
187
187
  const errors = new Map<string, TxPoolRejectionError>();
188
188
  const acceptedPending = new Set<string>();
189
189
 
190
+ // Phase 1: Pre-compute all throwable I/O outside the transaction.
191
+ // If any pre-computation throws, the entire call fails before mutations happen.
192
+ const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
193
+
194
+ const validator = await this.#createTxValidator();
195
+
196
+ for (const tx of txs) {
197
+ const txHash = tx.getTxHash();
198
+ const txHashStr = txHash.toString();
199
+
200
+ const meta = await buildTxMetaData(tx);
201
+ const minedBlockId = await this.#getMinedBlockId(txHash);
202
+
203
+ // Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
204
+ let isValid = true;
205
+ if (!minedBlockId) {
206
+ isValid = await this.#validateMeta(meta, validator);
207
+ }
208
+
209
+ precomputed.set(txHashStr, { meta, minedBlockId, isValid });
210
+ }
211
+
212
+ // Phase 2: Apply mutations inside the transaction using only pre-computed results,
213
+ // in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
190
214
  const poolAccess = this.#createPreAddPoolAccess();
191
215
  const preAddContext: PreAddContext | undefined =
192
216
  opts.feeComparisonOnly !== undefined ? { feeComparisonOnly: opts.feeComparisonOnly } : undefined;
@@ -202,22 +226,25 @@ export class TxPoolV2Impl {
202
226
  continue;
203
227
  }
204
228
 
205
- // Check mined status first (applies to all paths)
206
- const minedBlockId = await this.#getMinedBlockId(txHash);
229
+ const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
207
230
  const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
208
231
 
209
232
  if (minedBlockId) {
210
233
  // Already mined - add directly (protection already set if pre-protected)
211
- await this.#addTx(tx, { mined: minedBlockId }, opts);
234
+ await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
212
235
  accepted.push(txHash);
213
236
  } else if (preProtectedSlot !== undefined) {
214
237
  // Pre-protected and not mined - add as protected (bypass validation)
215
- await this.#addTx(tx, { protected: preProtectedSlot }, opts);
238
+ await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
216
239
  accepted.push(txHash);
240
+ } else if (!isValid) {
241
+ // Failed pre-computed validation
242
+ rejected.push(txHash);
217
243
  } else {
218
- // Regular pending tx - validate and run pre-add rules
244
+ // Regular pending tx - run pre-add rules using pre-computed metadata
219
245
  const result = await this.#tryAddRegularPendingTx(
220
246
  tx,
247
+ meta,
221
248
  opts,
222
249
  poolAccess,
223
250
  acceptedPending,
@@ -227,8 +254,6 @@ export class TxPoolV2Impl {
227
254
  );
228
255
  if (result.status === 'accepted') {
229
256
  acceptedPending.add(txHashStr);
230
- } else if (result.status === 'rejected') {
231
- rejected.push(txHash);
232
257
  } else {
233
258
  ignored.push(txHash);
234
259
  }
@@ -259,27 +284,21 @@ export class TxPoolV2Impl {
259
284
  return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
260
285
  }
261
286
 
262
- /** Validates and adds a regular pending tx. Returns status. */
287
+ /** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
263
288
  async #tryAddRegularPendingTx(
264
289
  tx: Tx,
290
+ precomputedMeta: TxMetaData,
265
291
  opts: { source?: string },
266
292
  poolAccess: PreAddPoolAccess,
267
293
  acceptedPending: Set<string>,
268
294
  ignored: TxHash[],
269
295
  errors: Map<string, TxPoolRejectionError>,
270
296
  preAddContext?: PreAddContext,
271
- ): Promise<{ status: 'accepted' | 'ignored' | 'rejected' }> {
272
- const txHash = tx.getTxHash();
273
- const txHashStr = txHash.toString();
274
-
275
- // Build metadata and validate using metadata
276
- const meta = await buildTxMetaData(tx);
277
- if (!(await this.#validateMeta(meta))) {
278
- return { status: 'rejected' };
279
- }
297
+ ): Promise<{ status: 'accepted' | 'ignored' }> {
298
+ const txHashStr = tx.getTxHash().toString();
280
299
 
281
300
  // Run pre-add rules
282
- const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess, preAddContext);
301
+ const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
283
302
 
284
303
  if (preAddResult.shouldIgnore) {
285
304
  this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
@@ -323,7 +342,7 @@ export class TxPoolV2Impl {
323
342
  }
324
343
 
325
344
  // Add the transaction
326
- await this.#addTx(tx, 'pending', opts);
345
+ await this.#addTx(tx, 'pending', opts, precomputedMeta);
327
346
  return { status: 'accepted' };
328
347
  }
329
348
 
@@ -765,9 +784,10 @@ export class TxPoolV2Impl {
765
784
  tx: Tx,
766
785
  state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
767
786
  opts: { source?: string } = {},
787
+ precomputedMeta?: TxMetaData,
768
788
  ): Promise<TxMetaData> {
769
789
  const txHashStr = tx.getTxHash().toString();
770
- const meta = await buildTxMetaData(tx);
790
+ const meta = precomputedMeta ?? (await buildTxMetaData(tx));
771
791
  meta.receivedAt = this.#dateProvider.now();
772
792
 
773
793
  await this.#txsDB.set(txHashStr, tx.toBuffer());
@@ -1,11 +1,11 @@
1
1
  // Taken from lodestar: https://github.com/ChainSafe/lodestar
2
- import { sha256 } from '@aztec/foundation/crypto/sha256';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { MAX_TX_SIZE_KB, TopicType, getTopicFromString } from '@aztec/stdlib/p2p';
5
4
 
6
5
  import type { RPC } from '@chainsafe/libp2p-gossipsub/message';
7
6
  import type { DataTransform } from '@chainsafe/libp2p-gossipsub/types';
8
7
  import type { Message } from '@libp2p/interface';
8
+ import { webcrypto } from 'node:crypto';
9
9
  import { compressSync, uncompressSync } from 'snappy';
10
10
  import xxhashFactory from 'xxhash-wasm';
11
11
 
@@ -44,11 +44,10 @@ export function msgIdToStrFn(msgId: Uint8Array): string {
44
44
  * @param message - The libp2p message
45
45
  * @returns The message identifier
46
46
  */
47
- export function getMsgIdFn(message: Message) {
48
- const { topic } = message;
49
-
50
- const vec = [Buffer.from(topic), message.data];
51
- return sha256(Buffer.concat(vec)).subarray(0, 20);
47
+ export async function getMsgIdFn({ topic, data }: Message): Promise<Uint8Array> {
48
+ const buffer = Buffer.concat([Buffer.from(topic), data]);
49
+ const hash = await webcrypto.subtle.digest('SHA-256', buffer);
50
+ return Buffer.from(hash.slice(0, 20));
52
51
  }
53
52
 
54
53
  const DefaultMaxSizesKb: Record<TopicType, number> = {
@@ -16,13 +16,12 @@ import {
16
16
  CheckpointProposal,
17
17
  type CheckpointProposalCore,
18
18
  type Gossipable,
19
- P2PClientType,
20
19
  P2PMessage,
21
20
  type ValidationResult as P2PValidationResult,
22
21
  PeerErrorSeverity,
23
22
  TopicType,
24
23
  createTopicString,
25
- getTopicsForClientAndConfig,
24
+ getTopicsForConfig,
26
25
  metricsTopicStrToLabels,
27
26
  } from '@aztec/stdlib/p2p';
28
27
  import { MerkleTreeId } from '@aztec/stdlib/trees';
@@ -135,7 +134,7 @@ type ReceivedMessageValidationResult<T, M = undefined> =
135
134
  /**
136
135
  * Lib P2P implementation of the P2PService interface.
137
136
  */
138
- export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
137
+ export class LibP2PService extends WithTracer implements P2PService {
139
138
  private discoveryRunningPromise?: RunningPromise;
140
139
  private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
141
140
 
@@ -182,7 +181,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
182
181
  protected logger: Logger;
183
182
 
184
183
  constructor(
185
- private clientType: T,
186
184
  private config: P2PConfig,
187
185
  protected node: PubSubLibp2p,
188
186
  private peerDiscoveryService: PeerDiscoveryService,
@@ -262,8 +260,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
262
260
  * @param txPool - The transaction pool to be accessed by the service.
263
261
  * @returns The new service.
264
262
  */
265
- public static async new<T extends P2PClientType>(
266
- clientType: T,
263
+ public static async new(
267
264
  config: P2PConfig,
268
265
  peerId: PeerId,
269
266
  deps: {
@@ -475,7 +472,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
475
472
  peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
476
473
 
477
474
  return new LibP2PService(
478
- clientType,
479
475
  config,
480
476
  node,
481
477
  peerDiscoveryService,
@@ -549,7 +545,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
549
545
  await this.node.start();
550
546
 
551
547
  // Subscribe to standard GossipSub topics by default
552
- for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
548
+ for (const topic of getTopicsForConfig(this.config.disableTransactions)) {
553
549
  this.subscribeToTopic(this.topicStrings[topic]);
554
550
  }
555
551
 
@@ -818,9 +814,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
818
814
  if (msg.topic === this.topicStrings[TopicType.tx]) {
819
815
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
820
816
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
821
- if (this.clientType === P2PClientType.Full) {
822
- await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
823
- }
817
+ await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
824
818
  } else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
825
819
  await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
826
820
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
@@ -627,7 +627,9 @@ export class ReqResp implements ReqRespInterface {
627
627
  // and that this stream should be dropped
628
628
  const isMessageToNotWarn =
629
629
  err instanceof Error &&
630
- ['stream reset', 'Cannot push value onto an ended pushable'].some(msg => err.message.includes(msg));
630
+ ['stream reset', 'Cannot push value onto an ended pushable', 'read ECONNRESET'].some(msg =>
631
+ err.message.includes(msg),
632
+ );
631
633
  const level = isMessageToNotWarn ? 'debug' : 'warn';
632
634
  this.logger[level]('Unknown stream error while handling the stream, aborting', {
633
635
  protocol,
@@ -7,7 +7,6 @@ import { sleep } from '@aztec/foundation/sleep';
7
7
  import type { DataStoreConfig } from '@aztec/kv-store/config';
8
8
  import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
9
9
  import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
10
- import { P2PClientType } from '@aztec/stdlib/p2p';
11
10
 
12
11
  import { createP2PClient } from '../client/index.js';
13
12
  import type { P2PClient } from '../client/p2p_client.js';
@@ -98,7 +97,6 @@ export async function makeTestP2PClient(
98
97
  const kvStore = await openTmpStore('test');
99
98
 
100
99
  const client = await createP2PClient(
101
- P2PClientType.Full,
102
100
  config,
103
101
  l2BlockSource,
104
102
  proofVerifier,
@@ -4,7 +4,6 @@ import type { AztecAsyncKVStore } from '@aztec/kv-store';
4
4
  import type { L2BlockSource } from '@aztec/stdlib/block';
5
5
  import type { ContractDataSource } from '@aztec/stdlib/contract';
6
6
  import type { ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
7
- import { P2PClientType } from '@aztec/stdlib/p2p';
8
7
  import type { TelemetryClient } from '@aztec/telemetry-client';
9
8
 
10
9
  import type { GossipsubEvents, GossipsubMessage } from '@chainsafe/libp2p-gossipsub';
@@ -42,11 +41,10 @@ type GossipSubService = PubSubLibp2p['services']['pubsub'];
42
41
  * Given a mock gossip sub network, returns a factory function that creates an instance LibP2PService connected to it.
43
42
  * Designed to be used in tests in P2PClientDeps.p2pServiceFactory.
44
43
  */
45
- export function getMockPubSubP2PServiceFactory<T extends P2PClientType>(
44
+ export function getMockPubSubP2PServiceFactory(
46
45
  network: MockGossipSubNetwork,
47
- ): (...args: Parameters<(typeof LibP2PService<T>)['new']>) => Promise<LibP2PService<T>> {
46
+ ): (...args: Parameters<(typeof LibP2PService)['new']>) => Promise<LibP2PService> {
48
47
  return (
49
- clientType: P2PClientType,
50
48
  config: P2PConfig,
51
49
  peerId: PeerId,
52
50
  deps: {
@@ -66,8 +64,7 @@ export function getMockPubSubP2PServiceFactory<T extends P2PClientType>(
66
64
  const peerManager = new DummyPeerManager(peerId, network);
67
65
  const reqresp: ReqRespInterface = new MockReqResp(peerId, network);
68
66
  const peerDiscoveryService = new DummyPeerDiscoveryService();
69
- const service = new LibP2PService<T>(
70
- clientType as T,
67
+ const service = new LibP2PService(
71
68
  config,
72
69
  libp2p,
73
70
  peerDiscoveryService,
@@ -12,7 +12,6 @@ import type {
12
12
  IVCProofVerificationResult,
13
13
  WorldStateSynchronizer,
14
14
  } from '@aztec/stdlib/interfaces/server';
15
- import type { P2PClientType } from '@aztec/stdlib/p2p';
16
15
  import type { Tx } from '@aztec/stdlib/tx';
17
16
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
18
17
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
@@ -107,8 +106,7 @@ export async function createLibp2pNode(
107
106
  *
108
107
  *
109
108
  */
110
- export async function createTestLibP2PService<T extends P2PClientType>(
111
- clientType: T,
109
+ export async function createTestLibP2PService(
112
110
  boostrapAddrs: string[] = [],
113
111
  archiver: L2BlockSource & ContractDataSource,
114
112
  worldStateSynchronizer: WorldStateSynchronizer,
@@ -159,8 +157,7 @@ export async function createTestLibP2PService<T extends P2PClientType>(
159
157
  p2pNode.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
160
158
  peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
161
159
 
162
- return new LibP2PService<T>(
163
- clientType,
160
+ return new LibP2PService(
164
161
  config,
165
162
  p2pNode as PubSubLibp2p,
166
163
  discoveryService,
@@ -19,7 +19,7 @@ import { protocolContractsHash } from '@aztec/protocol-contracts';
19
19
  import type { L2BlockSource } from '@aztec/stdlib/block';
20
20
  import type { ContractDataSource } from '@aztec/stdlib/contract';
21
21
  import type { ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
22
- import { type BlockProposal, P2PClientType, P2PMessage } from '@aztec/stdlib/p2p';
22
+ import { type BlockProposal, P2PMessage } from '@aztec/stdlib/p2p';
23
23
  import { ChonkProof } from '@aztec/stdlib/proofs';
24
24
  import { makeAztecAddress, makeBlockHeader, makeBlockProposal, mockTx } from '@aztec/stdlib/testing';
25
25
  import { Tx, TxHash, type TxValidationResult } from '@aztec/stdlib/tx';
@@ -86,12 +86,11 @@ export interface BenchReadyMessage {
86
86
  }
87
87
  const txCache = new Map<number, Tx[]>();
88
88
 
89
- class TestLibP2PService<T extends P2PClientType = P2PClientType.Full> extends LibP2PService<T> {
89
+ class TestLibP2PService extends LibP2PService {
90
90
  private disableTxValidation: boolean;
91
91
  private gossipMessageCount = 0;
92
92
 
93
93
  constructor(
94
- clientType: T,
95
94
  config: P2PConfig,
96
95
  node: PubSubLibp2p,
97
96
  peerDiscoveryService: PeerDiscoveryService,
@@ -107,7 +106,6 @@ class TestLibP2PService<T extends P2PClientType = P2PClientType.Full> extends Li
107
106
  disableTxValidation = true,
108
107
  ) {
109
108
  super(
110
- clientType,
111
109
  config,
112
110
  node,
113
111
  peerDiscoveryService,
@@ -365,7 +363,6 @@ process.on('message', async msg => {
365
363
  };
366
364
 
367
365
  const client = await createP2PClient(
368
- P2PClientType.Full,
369
366
  config as P2PConfig & DataStoreConfig,
370
367
  l2BlockSource,
371
368
  proofVerifier as ClientProtocolCircuitVerifier,
@@ -378,7 +375,6 @@ process.on('message', async msg => {
378
375
  );
379
376
 
380
377
  const testService = new TestLibP2PService(
381
- P2PClientType.Full,
382
378
  config,
383
379
  (client as any).p2pService.node,
384
380
  (client as any).p2pService.peerDiscoveryService,