@aztec/p2p 0.61.0 → 0.63.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 (54) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +3 -1
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/bootstrap/bootstrap.js +7 -3
  4. package/dest/client/index.d.ts +1 -1
  5. package/dest/client/index.d.ts.map +1 -1
  6. package/dest/client/index.js +2 -2
  7. package/dest/client/p2p_client.d.ts +11 -5
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +95 -27
  10. package/dest/config.d.ts +4 -4
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +6 -5
  13. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  14. package/dest/mem_pools/attestation_pool/mocks.js +5 -3
  15. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +3 -2
  16. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  17. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +30 -5
  18. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +3 -2
  19. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  20. package/dest/mem_pools/tx_pool/memory_tx_pool.js +26 -5
  21. package/dest/mem_pools/tx_pool/tx_pool.d.ts +8 -2
  22. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  23. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  24. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +30 -3
  25. package/dest/mocks/index.d.ts +3 -3
  26. package/dest/mocks/index.d.ts.map +1 -1
  27. package/dest/mocks/index.js +10 -9
  28. package/dest/service/discV5_service.d.ts +2 -1
  29. package/dest/service/discV5_service.d.ts.map +1 -1
  30. package/dest/service/discV5_service.js +6 -2
  31. package/dest/service/libp2p_service.d.ts.map +1 -1
  32. package/dest/service/libp2p_service.js +6 -3
  33. package/dest/tx_validator/double_spend_validator.d.ts.map +1 -1
  34. package/dest/tx_validator/double_spend_validator.js +2 -2
  35. package/dest/tx_validator/metadata_validator.d.ts.map +1 -1
  36. package/dest/tx_validator/metadata_validator.js +2 -5
  37. package/dest/util.d.ts +1 -1
  38. package/dest/util.d.ts.map +1 -1
  39. package/package.json +7 -6
  40. package/src/bootstrap/bootstrap.ts +5 -3
  41. package/src/client/index.ts +3 -2
  42. package/src/client/p2p_client.ts +118 -34
  43. package/src/config.ts +10 -9
  44. package/src/mem_pools/attestation_pool/mocks.ts +4 -2
  45. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +35 -6
  46. package/src/mem_pools/tx_pool/memory_tx_pool.ts +32 -6
  47. package/src/mem_pools/tx_pool/tx_pool.ts +9 -2
  48. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +34 -2
  49. package/src/mocks/index.ts +17 -9
  50. package/src/service/discV5_service.ts +10 -1
  51. package/src/service/libp2p_service.ts +6 -1
  52. package/src/tx_validator/double_spend_validator.ts +3 -1
  53. package/src/tx_validator/metadata_validator.ts +1 -5
  54. package/src/util.ts +1 -1
@@ -19,7 +19,7 @@ export class AztecKVTxPool implements TxPool {
19
19
  /** Index for pending txs. */
20
20
  #pendingTxs: AztecSet<string>;
21
21
  /** Index for mined txs. */
22
- #minedTxs: AztecSet<string>;
22
+ #minedTxs: AztecMap<string, number>;
23
23
 
24
24
  #log: Logger;
25
25
 
@@ -32,7 +32,7 @@ export class AztecKVTxPool implements TxPool {
32
32
  */
33
33
  constructor(store: AztecKVStore, telemetry: TelemetryClient, log = createDebugLogger('aztec:tx_pool')) {
34
34
  this.#txs = store.openMap('txs');
35
- this.#minedTxs = store.openSet('minedTxs');
35
+ this.#minedTxs = store.openMap('minedTxs');
36
36
  this.#pendingTxs = store.openSet('pendingTxs');
37
37
 
38
38
  this.#store = store;
@@ -40,12 +40,12 @@ export class AztecKVTxPool implements TxPool {
40
40
  this.#metrics = new PoolInstrumentation(telemetry, 'AztecKVTxPool');
41
41
  }
42
42
 
43
- public markAsMined(txHashes: TxHash[]): Promise<void> {
43
+ public markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
44
44
  return this.#store.transaction(() => {
45
45
  let deleted = 0;
46
46
  for (const hash of txHashes) {
47
47
  const key = hash.toString();
48
- void this.#minedTxs.add(key);
48
+ void this.#minedTxs.set(key, blockNumber);
49
49
  if (this.#pendingTxs.has(key)) {
50
50
  deleted++;
51
51
  void this.#pendingTxs.delete(key);
@@ -56,12 +56,41 @@ export class AztecKVTxPool implements TxPool {
56
56
  });
57
57
  }
58
58
 
59
+ public markMinedAsPending(txHashes: TxHash[]): Promise<void> {
60
+ if (txHashes.length === 0) {
61
+ return Promise.resolve();
62
+ }
63
+
64
+ return this.#store.transaction(() => {
65
+ let deleted = 0;
66
+ let added = 0;
67
+ for (const hash of txHashes) {
68
+ const key = hash.toString();
69
+ if (this.#minedTxs.has(key)) {
70
+ deleted++;
71
+ void this.#minedTxs.delete(key);
72
+ }
73
+
74
+ if (this.#txs.has(key)) {
75
+ added++;
76
+ void this.#pendingTxs.add(key);
77
+ }
78
+ }
79
+
80
+ this.#metrics.recordRemovedObjects(deleted, 'mined');
81
+ this.#metrics.recordAddedObjects(added, 'pending');
82
+ });
83
+ }
84
+
59
85
  public getPendingTxHashes(): TxHash[] {
60
86
  return Array.from(this.#pendingTxs.entries()).map(x => TxHash.fromString(x));
61
87
  }
62
88
 
63
- public getMinedTxHashes(): TxHash[] {
64
- return Array.from(this.#minedTxs.entries()).map(x => TxHash.fromString(x));
89
+ public getMinedTxHashes(): [TxHash, number][] {
90
+ return Array.from(this.#minedTxs.entries()).map(([txHash, blockNumber]) => [
91
+ TxHash.fromString(txHash),
92
+ blockNumber,
93
+ ]);
65
94
  }
66
95
 
67
96
  public getTxStatus(txHash: TxHash): 'pending' | 'mined' | undefined {
@@ -14,7 +14,7 @@ export class InMemoryTxPool implements TxPool {
14
14
  * Our tx pool, stored as a Map in-memory, with K: tx hash and V: the transaction.
15
15
  */
16
16
  private txs: Map<bigint, Tx>;
17
- private minedTxs: Set<bigint>;
17
+ private minedTxs: Map<bigint, number>;
18
18
  private pendingTxs: Set<bigint>;
19
19
 
20
20
  private metrics: PoolInstrumentation<Tx>;
@@ -25,15 +25,15 @@ export class InMemoryTxPool implements TxPool {
25
25
  */
26
26
  constructor(telemetry: TelemetryClient, private log = createDebugLogger('aztec:tx_pool')) {
27
27
  this.txs = new Map<bigint, Tx>();
28
- this.minedTxs = new Set();
28
+ this.minedTxs = new Map();
29
29
  this.pendingTxs = new Set();
30
30
  this.metrics = new PoolInstrumentation(telemetry, 'InMemoryTxPool');
31
31
  }
32
32
 
33
- public markAsMined(txHashes: TxHash[]): Promise<void> {
33
+ public markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
34
34
  const keys = txHashes.map(x => x.toBigInt());
35
35
  for (const key of keys) {
36
- this.minedTxs.add(key);
36
+ this.minedTxs.set(key, blockNumber);
37
37
  this.pendingTxs.delete(key);
38
38
  }
39
39
  this.metrics.recordRemovedObjects(txHashes.length, 'pending');
@@ -41,12 +41,38 @@ export class InMemoryTxPool implements TxPool {
41
41
  return Promise.resolve();
42
42
  }
43
43
 
44
+ public markMinedAsPending(txHashes: TxHash[]): Promise<void> {
45
+ if (txHashes.length === 0) {
46
+ return Promise.resolve();
47
+ }
48
+
49
+ const keys = txHashes.map(x => x.toBigInt());
50
+ let deleted = 0;
51
+ let added = 0;
52
+ for (const key of keys) {
53
+ if (this.minedTxs.delete(key)) {
54
+ deleted++;
55
+ }
56
+
57
+ // only add back to the pending set if we have the tx object
58
+ if (this.txs.has(key)) {
59
+ added++;
60
+ this.pendingTxs.add(key);
61
+ }
62
+ }
63
+
64
+ this.metrics.recordRemovedObjects(deleted, 'mined');
65
+ this.metrics.recordAddedObjects(added, 'pending');
66
+
67
+ return Promise.resolve();
68
+ }
69
+
44
70
  public getPendingTxHashes(): TxHash[] {
45
71
  return Array.from(this.pendingTxs).map(x => TxHash.fromBigInt(x));
46
72
  }
47
73
 
48
- public getMinedTxHashes(): TxHash[] {
49
- return Array.from(this.minedTxs).map(x => TxHash.fromBigInt(x));
74
+ public getMinedTxHashes(): [TxHash, number][] {
75
+ return Array.from(this.minedTxs.entries()).map(([txHash, blockNumber]) => [TxHash.fromBigInt(txHash), blockNumber]);
50
76
  }
51
77
 
52
78
  public getTxStatus(txHash: TxHash): 'pending' | 'mined' | undefined {
@@ -21,7 +21,14 @@ export interface TxPool {
21
21
  * Marks the set of txs as mined, as opposed to pending.
22
22
  * @param txHashes - Hashes of the txs to flag as mined.
23
23
  */
24
- markAsMined(txHashes: TxHash[]): Promise<void>;
24
+ markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void>;
25
+
26
+ /**
27
+ * Moves mined txs back to the pending set in the case of a reorg.
28
+ * Note: txs not known by this peer will be ignored.
29
+ * @param txHashes - Hashes of the txs to flag as pending.
30
+ */
31
+ markMinedAsPending(txHashes: TxHash[]): Promise<void>;
25
32
 
26
33
  /**
27
34
  * Deletes transactions from the pool. Tx hashes that are not present are ignored.
@@ -51,7 +58,7 @@ export interface TxPool {
51
58
  * Gets the hashes of mined transactions currently in the tx pool.
52
59
  * @returns An array of mined transaction hashes found in the tx pool.
53
60
  */
54
- getMinedTxHashes(): TxHash[];
61
+ getMinedTxHashes(): [tx: TxHash, blockNumber: number][];
55
62
 
56
63
  /**
57
64
  * Returns whether the given tx hash is flagged as pending or mined.
@@ -38,14 +38,46 @@ export function describeTxPool(getTxPool: () => TxPool) {
38
38
  const tx2 = mockTx(2);
39
39
 
40
40
  await pool.addTxs([tx1, tx2]);
41
- await pool.markAsMined([tx1.getTxHash()]);
41
+ await pool.markAsMined([tx1.getTxHash()], 1);
42
42
 
43
43
  expect(pool.getTxByHash(tx1.getTxHash())).toEqual(tx1);
44
44
  expect(pool.getTxStatus(tx1.getTxHash())).toEqual('mined');
45
- expect(pool.getMinedTxHashes()).toEqual([tx1.getTxHash()]);
45
+ expect(pool.getMinedTxHashes()).toEqual([[tx1.getTxHash(), 1]]);
46
46
  expect(pool.getPendingTxHashes()).toEqual([tx2.getTxHash()]);
47
47
  });
48
48
 
49
+ it('Marks txs as pending after being mined', async () => {
50
+ const tx1 = mockTx(1);
51
+ const tx2 = mockTx(2);
52
+
53
+ await pool.addTxs([tx1, tx2]);
54
+ await pool.markAsMined([tx1.getTxHash()], 1);
55
+
56
+ await pool.markMinedAsPending([tx1.getTxHash()]);
57
+ expect(pool.getMinedTxHashes()).toEqual([]);
58
+ const pending = pool.getPendingTxHashes();
59
+ expect(pending).toHaveLength(2);
60
+ expect(pending).toEqual(expect.arrayContaining([tx1.getTxHash(), tx2.getTxHash()]));
61
+ });
62
+
63
+ it('Only marks txs as pending if they are known', async () => {
64
+ const tx1 = mockTx(1);
65
+ // simulate a situation where not all peers have all the txs
66
+ const someTxHashThatThisPeerDidNotSee = mockTx(2).getTxHash();
67
+ await pool.addTxs([tx1]);
68
+ // this peer knows that tx2 was mined, but it does not have the tx object
69
+ await pool.markAsMined([tx1.getTxHash(), someTxHashThatThisPeerDidNotSee], 1);
70
+ expect(pool.getMinedTxHashes()).toEqual([
71
+ [tx1.getTxHash(), 1],
72
+ [someTxHashThatThisPeerDidNotSee, 1],
73
+ ]);
74
+
75
+ // reorg: both txs should now become available again
76
+ await pool.markMinedAsPending([tx1.getTxHash(), someTxHashThatThisPeerDidNotSee]);
77
+ expect(pool.getMinedTxHashes()).toEqual([]);
78
+ expect(pool.getPendingTxHashes()).toEqual([tx1.getTxHash()]); // tx2 is not in the pool
79
+ });
80
+
49
81
  it('Returns all transactions in the pool', async () => {
50
82
  const tx1 = mockTx(1);
51
83
  const tx2 = mockTx(2);
@@ -4,8 +4,9 @@ import {
4
4
  type Tx,
5
5
  type WorldStateSynchronizer,
6
6
  } from '@aztec/circuit-types';
7
- import { type DataStoreConfig } from '@aztec/kv-store/utils';
7
+ import { type DataStoreConfig } from '@aztec/kv-store/config';
8
8
  import { type TelemetryClient } from '@aztec/telemetry-client';
9
+ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
9
10
 
10
11
  import { gossipsub } from '@chainsafe/libp2p-gossipsub';
11
12
  import { noise } from '@chainsafe/libp2p-noise';
@@ -90,7 +91,7 @@ export async function createLibp2pNode(
90
91
  * Test Libp2p service
91
92
  * P2P functionality is operational, however everything else is default
92
93
  *
93
- * WORKTODO: more description
94
+ *
94
95
  */
95
96
  export async function createTestLibP2PService(
96
97
  boostrapAddrs: string[] = [],
@@ -114,7 +115,7 @@ export async function createTestLibP2PService(
114
115
  p2pEnabled: true,
115
116
  peerIdPrivateKey: Buffer.from(peerId.privateKey!).toString('hex'),
116
117
  } as P2PConfig & DataStoreConfig;
117
- const discoveryService = new DiscV5Service(peerId, config);
118
+ const discoveryService = new DiscV5Service(peerId, config, telemetry);
118
119
  const proofVerifier = new AlwaysTrueCircuitVerifier();
119
120
 
120
121
  // No bootstrap nodes provided as the libp2p service will register them in the constructor
@@ -233,20 +234,27 @@ export function createBootstrapNodeConfig(privateKey: string, port: number): Boo
233
234
  };
234
235
  }
235
236
 
236
- export function createBootstrapNodeFromPrivateKey(privateKey: string, port: number): Promise<BootstrapNode> {
237
+ export function createBootstrapNodeFromPrivateKey(
238
+ privateKey: string,
239
+ port: number,
240
+ telemetry: TelemetryClient = new NoopTelemetryClient(),
241
+ ): Promise<BootstrapNode> {
237
242
  const config = createBootstrapNodeConfig(privateKey, port);
238
- return startBootstrapNode(config);
243
+ return startBootstrapNode(config, telemetry);
239
244
  }
240
245
 
241
- export async function createBootstrapNode(port: number): Promise<BootstrapNode> {
246
+ export async function createBootstrapNode(
247
+ port: number,
248
+ telemetry: TelemetryClient = new NoopTelemetryClient(),
249
+ ): Promise<BootstrapNode> {
242
250
  const peerId = await createLibP2PPeerId();
243
251
  const config = createBootstrapNodeConfig(Buffer.from(peerId.privateKey!).toString('hex'), port);
244
252
 
245
- return startBootstrapNode(config);
253
+ return startBootstrapNode(config, telemetry);
246
254
  }
247
255
 
248
- async function startBootstrapNode(config: BootnodeConfig) {
249
- const bootstrapNode = new BootstrapNode();
256
+ async function startBootstrapNode(config: BootnodeConfig, telemetry: TelemetryClient) {
257
+ const bootstrapNode = new BootstrapNode(telemetry);
250
258
  await bootstrapNode.start(config);
251
259
  return bootstrapNode;
252
260
  }
@@ -1,5 +1,6 @@
1
1
  import { createDebugLogger } from '@aztec/foundation/log';
2
2
  import { sleep } from '@aztec/foundation/sleep';
3
+ import { OtelMetricsAdapter, type TelemetryClient } from '@aztec/telemetry-client';
3
4
 
4
5
  import { Discv5, type Discv5EventEmitter } from '@chainsafe/discv5';
5
6
  import { ENR, SignableENR } from '@chainsafe/enr';
@@ -41,7 +42,12 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
41
42
 
42
43
  private startTime = 0;
43
44
 
44
- constructor(private peerId: PeerId, config: P2PConfig, private logger = createDebugLogger('aztec:discv5_service')) {
45
+ constructor(
46
+ private peerId: PeerId,
47
+ config: P2PConfig,
48
+ telemetry: TelemetryClient,
49
+ private logger = createDebugLogger('aztec:discv5_service'),
50
+ ) {
45
51
  super();
46
52
  const { tcpAnnounceAddress, udpAnnounceAddress, udpListenAddress, bootstrapNodes } = config;
47
53
  this.bootstrapNodes = bootstrapNodes;
@@ -66,14 +72,17 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
66
72
  this.enr.setLocationMultiaddr(multiAddrUdp);
67
73
  this.enr.setLocationMultiaddr(multiAddrTcp);
68
74
 
75
+ const metricsRegistry = new OtelMetricsAdapter(telemetry);
69
76
  this.discv5 = Discv5.create({
70
77
  enr: this.enr,
71
78
  peerId,
72
79
  bindAddrs: { ip4: listenMultiAddrUdp },
73
80
  config: {
74
81
  lookupTimeout: 2000,
82
+ requestTimeout: 2000,
75
83
  allowUnverifiedSessions: true,
76
84
  },
85
+ metricsRegistry,
77
86
  });
78
87
 
79
88
  this.logger.info(`ENR NodeId: ${this.enr.nodeId}`);
@@ -12,13 +12,14 @@ import {
12
12
  Tx,
13
13
  TxHash,
14
14
  type WorldStateSynchronizer,
15
+ metricsTopicStrToLabels,
15
16
  } from '@aztec/circuit-types';
16
17
  import { Fr } from '@aztec/circuits.js';
17
18
  import { createDebugLogger } from '@aztec/foundation/log';
18
19
  import { SerialQueue } from '@aztec/foundation/queue';
19
20
  import { RunningPromise } from '@aztec/foundation/running-promise';
20
21
  import type { AztecKVStore } from '@aztec/kv-store';
21
- import { Attributes, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
22
+ import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
22
23
 
23
24
  import { type ENR } from '@chainsafe/enr';
24
25
  import { type GossipSub, type GossipSubComponents, gossipsub } from '@chainsafe/libp2p-gossipsub';
@@ -218,6 +219,8 @@ export class LibP2PService extends WithTracer implements P2PService {
218
219
 
219
220
  const datastore = new AztecDatastore(store);
220
221
 
222
+ const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
223
+
221
224
  const node = await createLibp2p({
222
225
  start: false,
223
226
  peerId,
@@ -257,6 +260,8 @@ export class LibP2PService extends WithTracer implements P2PService {
257
260
  heartbeatInterval: config.gossipsubInterval,
258
261
  mcacheLength: config.gossipsubMcacheLength,
259
262
  mcacheGossip: config.gossipsubMcacheGossip,
263
+ metricsRegister: otelMetricsAdapter,
264
+ metricsTopicStrToLabel: metricsTopicStrToLabels(),
260
265
  scoreParams: createPeerScoreParams({
261
266
  topics: {
262
267
  [Tx.p2pTopic]: createTopicScoreParams({
@@ -36,7 +36,9 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
36
36
  }
37
37
 
38
38
  async #uniqueNullifiers(tx: AnyTx, thisBlockNullifiers: Set<bigint>): Promise<boolean> {
39
- const nullifiers = tx.data.getNonEmptyNullifiers().map(x => x.toBigInt());
39
+ const nullifiers = (tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers).map(x =>
40
+ x.toBigInt(),
41
+ );
40
42
 
41
43
  // Ditch this tx if it has repeated nullifiers
42
44
  const uniqueNullifiers = new Set(nullifiers);
@@ -45,11 +45,7 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
45
45
  }
46
46
 
47
47
  #isValidForBlockNumber(tx: T): boolean {
48
- const target =
49
- tx instanceof Tx
50
- ? tx.data.forRollup?.rollupValidationRequests || tx.data.forPublic!.validationRequests.forRollup
51
- : tx.data.rollupValidationRequests;
52
- const maxBlockNumber = target.maxBlockNumber;
48
+ const maxBlockNumber = tx.data.rollupValidationRequests.maxBlockNumber;
53
49
 
54
50
  if (maxBlockNumber.isSome && maxBlockNumber.value < this.blockNumber) {
55
51
  this.#log.warn(
package/src/util.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type DataStoreConfig } from '@aztec/kv-store/utils';
1
+ import { type DataStoreConfig } from '@aztec/kv-store/config';
2
2
 
3
3
  import type { GossipSub } from '@chainsafe/libp2p-gossipsub';
4
4
  import { resolve } from 'dns/promises';