@aztec/p2p 0.47.1 → 0.49.2

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.
@@ -1,6 +1,6 @@
1
- import { Tx } from '@aztec/circuit-types';
2
- import { SerialQueue } from '@aztec/foundation/fifo';
1
+ import { type Gossipable, type RawGossipMessage, TopicType, TopicTypeMap, Tx } from '@aztec/circuit-types';
3
2
  import { createDebugLogger } from '@aztec/foundation/log';
3
+ import { SerialQueue } from '@aztec/foundation/queue';
4
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
5
5
  import type { AztecKVStore } from '@aztec/kv-store';
6
6
 
@@ -21,7 +21,6 @@ import { convertToMultiaddr } from '../util.js';
21
21
  import { AztecDatastore } from './data_store.js';
22
22
  import { PeerManager } from './peer_manager.js';
23
23
  import type { P2PService, PeerDiscoveryService } from './service.js';
24
- import { AztecTxMessageCreator } from './tx_messages.js';
25
24
 
26
25
  export interface PubSubLibp2p extends Libp2p {
27
26
  services: {
@@ -49,7 +48,6 @@ export async function createLibP2PPeerId(privateKey?: string): Promise<PeerId> {
49
48
  */
50
49
  export class LibP2PService implements P2PService {
51
50
  private jobQueue: SerialQueue = new SerialQueue();
52
- private messageCreator: AztecTxMessageCreator;
53
51
  private peerManager: PeerManager;
54
52
  private discoveryRunningPromise?: RunningPromise;
55
53
  constructor(
@@ -59,7 +57,6 @@ export class LibP2PService implements P2PService {
59
57
  private txPool: TxPool,
60
58
  private logger = createDebugLogger('aztec:libp2p_service'),
61
59
  ) {
62
- this.messageCreator = new AztecTxMessageCreator(config.txGossipVersion);
63
60
  this.peerManager = new PeerManager(node, peerDiscoveryService, config, logger);
64
61
  }
65
62
 
@@ -89,20 +86,22 @@ export class LibP2PService implements P2PService {
89
86
  this.logger.info(`Started P2P client with Peer ID ${this.node.peerId.toString()}`);
90
87
 
91
88
  // Subscribe to standard GossipSub topics by default
92
- this.subscribeToTopic(this.messageCreator.getTopic());
89
+ for (const topic in TopicType) {
90
+ this.subscribeToTopic(TopicTypeMap[topic].p2pTopic);
91
+ }
93
92
 
94
93
  // add GossipSub listener
95
94
  this.node.services.pubsub.addEventListener('gossipsub:message', async e => {
96
95
  const { msg } = e.detail;
97
96
  this.logger.debug(`Received PUBSUB message.`);
98
97
 
99
- await this.jobQueue.put(() => this.handleNewGossipMessage(msg.topic, msg.data));
98
+ await this.jobQueue.put(() => this.handleNewGossipMessage(msg));
100
99
  });
101
100
 
102
101
  // Start running promise for peer discovery
103
102
  this.discoveryRunningPromise = new RunningPromise(() => {
104
103
  this.peerManager.discover();
105
- }, this.config.p2pPeerCheckIntervalMS);
104
+ }, this.config.peerCheckIntervalMS);
106
105
  this.discoveryRunningPromise.start();
107
106
  }
108
107
 
@@ -142,20 +141,6 @@ export class LibP2PService implements P2PService {
142
141
 
143
142
  const datastore = new AztecDatastore(store);
144
143
 
145
- // The autonat service seems quite problematic in that using it seems to cause a lot of attempts
146
- // to dial ephemeral ports. I suspect that it works better if you can get the uPNPnat service to
147
- // work as then you would have a permanent port to be dialled.
148
- // Alas, I struggled to get this to work reliably either. I find there is a race between the
149
- // service that reads our listener addresses and the uPnP service.
150
- // The result being the uPnP service can't find an address to use for the port forward.
151
- // Need to investigate further.
152
- // if (enableNat) {
153
- // services.autoNAT = autoNATService({
154
- // protocolPrefix: 'aztec',
155
- // });
156
- // services.uPnPNAT = uPnPNATService();
157
- // }
158
-
159
144
  const node = await createLibp2p({
160
145
  start: false,
161
146
  peerId,
@@ -233,14 +218,13 @@ export class LibP2PService implements P2PService {
233
218
  * @param topic - The message's topic.
234
219
  * @param data - The message data
235
220
  */
236
- private async handleNewGossipMessage(topic: string, data: Uint8Array) {
237
- if (topic !== this.messageCreator.getTopic()) {
238
- // Invalid TX Topic, ignore
239
- return;
221
+ private async handleNewGossipMessage(message: RawGossipMessage) {
222
+ if (message.topic === Tx.p2pTopic) {
223
+ const tx = Tx.fromBuffer(Buffer.from(message.data));
224
+ await this.processTxFromPeer(tx);
240
225
  }
241
226
 
242
- const tx = Tx.fromBuffer(Buffer.from(data));
243
- await this.processTxFromPeer(tx);
227
+ return;
244
228
  }
245
229
 
246
230
  /**
@@ -248,7 +232,7 @@ export class LibP2PService implements P2PService {
248
232
  * @param tx - The transaction to propagate.
249
233
  */
250
234
  public propagateTx(tx: Tx): void {
251
- void this.jobQueue.put(() => Promise.resolve(this.sendTxToPeers(tx)));
235
+ void this.jobQueue.put(() => Promise.resolve(this.sendToPeers(tx)));
252
236
  }
253
237
 
254
238
  private async processTxFromPeer(tx: Tx): Promise<void> {
@@ -258,11 +242,14 @@ export class LibP2PService implements P2PService {
258
242
  await this.txPool.addTxs([tx]);
259
243
  }
260
244
 
261
- private async sendTxToPeers(tx: Tx) {
262
- const { data: txData } = this.messageCreator.createTxMessage(tx);
263
- this.logger.verbose(`Sending tx ${tx.getTxHash().toString()} to peers`);
264
- const recipientsNum = await this.publishToTopic(this.messageCreator.getTopic(), txData);
265
- this.logger.verbose(`Sent tx ${tx.getTxHash().toString()} to ${recipientsNum} peers`);
245
+ private async sendToPeers<T extends Gossipable>(message: T) {
246
+ const parent = message.constructor as typeof Gossipable;
247
+
248
+ const identifier = message.p2pMessageIdentifier().toString();
249
+ this.logger.verbose(`Sending tx ${identifier} to peers`);
250
+
251
+ const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
252
+ this.logger.verbose(`Sent tx ${identifier} to ${recipientsNum} peers`);
266
253
  }
267
254
 
268
255
  // Libp2p seems to hang sometimes if new peers are initiating connections.
@@ -42,11 +42,17 @@ export class AztecKVTxPool implements TxPool {
42
42
 
43
43
  public markAsMined(txHashes: TxHash[]): Promise<void> {
44
44
  return this.#store.transaction(() => {
45
+ let deleted = 0;
45
46
  for (const hash of txHashes) {
46
47
  const key = hash.toString();
47
48
  void this.#minedTxs.add(key);
48
- void this.#pendingTxs.delete(key);
49
+ if (this.#pendingTxs.has(key)) {
50
+ deleted++;
51
+ void this.#pendingTxs.delete(key);
52
+ }
49
53
  }
54
+ this.#metrics.recordRemovedTxs('pending', deleted);
55
+ this.#metrics.recordAddedTxs('mined', txHashes.length);
50
56
  });
51
57
  }
52
58
 
@@ -87,6 +93,7 @@ export class AztecKVTxPool implements TxPool {
87
93
  public addTxs(txs: Tx[]): Promise<void> {
88
94
  const txHashes = txs.map(tx => tx.getTxHash());
89
95
  return this.#store.transaction(() => {
96
+ let pendingCount = 0;
90
97
  for (const [i, tx] of txs.entries()) {
91
98
  const txHash = txHashes[i];
92
99
  this.#log.info(`Adding tx with id ${txHash.toString()}`, {
@@ -97,12 +104,14 @@ export class AztecKVTxPool implements TxPool {
97
104
  const key = txHash.toString();
98
105
  void this.#txs.set(key, tx.toBuffer());
99
106
  if (!this.#minedTxs.has(key)) {
107
+ pendingCount++;
100
108
  // REFACTOR: Use an lmdb conditional write to avoid race conditions with this write tx
101
109
  void this.#pendingTxs.add(key);
110
+ this.#metrics.recordTxSize(tx);
102
111
  }
103
112
  }
104
113
 
105
- this.#metrics.recordTxs(txs);
114
+ this.#metrics.recordAddedTxs('pending', pendingCount);
106
115
  });
107
116
  }
108
117
 
@@ -113,14 +122,24 @@ export class AztecKVTxPool implements TxPool {
113
122
  */
114
123
  public deleteTxs(txHashes: TxHash[]): Promise<void> {
115
124
  return this.#store.transaction(() => {
125
+ let pendingDeleted = 0;
126
+ let minedDeleted = 0;
116
127
  for (const hash of txHashes) {
117
128
  const key = hash.toString();
118
129
  void this.#txs.delete(key);
119
- void this.#pendingTxs.delete(key);
120
- void this.#minedTxs.delete(key);
130
+ if (this.#pendingTxs.has(key)) {
131
+ pendingDeleted++;
132
+ void this.#pendingTxs.delete(key);
133
+ }
134
+
135
+ if (this.#minedTxs.has(key)) {
136
+ minedDeleted++;
137
+ void this.#minedTxs.delete(key);
138
+ }
121
139
  }
122
140
 
123
- this.#metrics.removeTxs(txHashes.length);
141
+ this.#metrics.recordRemovedTxs('pending', pendingDeleted);
142
+ this.#metrics.recordRemovedTxs('mined', minedDeleted);
124
143
  });
125
144
  }
126
145
 
@@ -1,5 +1,7 @@
1
1
  import { type Tx } from '@aztec/circuit-types';
2
- import { type Histogram, Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
2
+ import { Attributes, type Histogram, Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
3
+
4
+ export type TxStatus = 'pending' | 'mined';
3
5
 
4
6
  /**
5
7
  * Instrumentation class for the TxPool.
@@ -33,26 +35,39 @@ export class TxPoolInstrumentation {
33
35
  });
34
36
  }
35
37
 
38
+ public recordTxSize(tx: Tx) {
39
+ this.txSize.record(tx.getSize());
40
+ }
41
+
36
42
  /**
37
43
  * Updates the metrics with the new transactions.
38
44
  * @param txs - The transactions to record
39
45
  */
40
- public recordTxs(txs: Tx[]) {
41
- for (const tx of txs) {
42
- this.txSize.record(tx.getSize());
46
+ public recordAddedTxs(status: string, count = 1) {
47
+ if (count < 0) {
48
+ throw new Error('Count must be positive');
43
49
  }
44
-
45
- this.txInMempool.add(txs.length);
50
+ if (count === 0) {
51
+ return;
52
+ }
53
+ this.txInMempool.add(count, {
54
+ [Attributes.STATUS]: status,
55
+ });
46
56
  }
47
57
 
48
58
  /**
49
59
  * Updates the metrics by removing transactions from the mempool.
50
60
  * @param count - The number of transactions to remove from the mempool
51
61
  */
52
- public removeTxs(count = 1) {
62
+ public recordRemovedTxs(status: string, count = 1) {
53
63
  if (count < 0) {
54
64
  throw new Error('Count must be positive');
55
65
  }
56
- this.txInMempool.add(-1 * count);
66
+ if (count === 0) {
67
+ return;
68
+ }
69
+ this.txInMempool.add(-1 * count, {
70
+ [Attributes.STATUS]: status,
71
+ });
57
72
  }
58
73
  }
@@ -36,6 +36,8 @@ export class InMemoryTxPool implements TxPool {
36
36
  this.minedTxs.add(key);
37
37
  this.pendingTxs.delete(key);
38
38
  }
39
+ this.metrics.recordRemovedTxs('pending', txHashes.length);
40
+ this.metrics.recordAddedTxs('mined', txHashes.length);
39
41
  return Promise.resolve();
40
42
  }
41
43
 
@@ -74,7 +76,7 @@ export class InMemoryTxPool implements TxPool {
74
76
  * @returns Empty promise.
75
77
  */
76
78
  public addTxs(txs: Tx[]): Promise<void> {
77
- this.metrics.recordTxs(txs);
79
+ let pending = 0;
78
80
  for (const tx of txs) {
79
81
  const txHash = tx.getTxHash();
80
82
  this.log.debug(`Adding tx with id ${txHash.toString()}`, {
@@ -85,9 +87,13 @@ export class InMemoryTxPool implements TxPool {
85
87
  const key = txHash.toBigInt();
86
88
  this.txs.set(key, tx);
87
89
  if (!this.minedTxs.has(key)) {
90
+ pending++;
91
+ this.metrics.recordTxSize(tx);
88
92
  this.pendingTxs.add(key);
89
93
  }
90
94
  }
95
+
96
+ this.metrics.recordAddedTxs('pending', pending);
91
97
  return Promise.resolve();
92
98
  }
93
99
 
@@ -97,13 +103,19 @@ export class InMemoryTxPool implements TxPool {
97
103
  * @returns The number of transactions that was deleted from the pool.
98
104
  */
99
105
  public deleteTxs(txHashes: TxHash[]): Promise<void> {
100
- this.metrics.removeTxs(txHashes.length);
106
+ let deletedMined = 0;
107
+ let deletedPending = 0;
108
+
101
109
  for (const txHash of txHashes) {
102
110
  const key = txHash.toBigInt();
103
111
  this.txs.delete(key);
104
- this.pendingTxs.delete(key);
105
- this.minedTxs.delete(key);
112
+ deletedPending += this.pendingTxs.delete(key) ? 1 : 0;
113
+ deletedMined += this.minedTxs.delete(key) ? 1 : 0;
106
114
  }
115
+
116
+ this.metrics.recordRemovedTxs('pending', deletedPending);
117
+ this.metrics.recordRemovedTxs('mined', deletedMined);
118
+
107
119
  return Promise.resolve();
108
120
  }
109
121
 
@@ -1,14 +0,0 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import { type Tx } from '@aztec/circuit-types';
3
- import { type SemVer } from 'semver';
4
- export declare const TX_MESSAGE_TOPIC = "";
5
- export declare class AztecTxMessageCreator {
6
- private readonly topic;
7
- constructor(version: SemVer);
8
- createTxMessage(tx: Tx): {
9
- topic: string;
10
- data: Buffer;
11
- };
12
- getTopic(): string;
13
- }
14
- //# sourceMappingURL=tx_messages.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tx_messages.d.ts","sourceRoot":"","sources":["../../src/service/tx_messages.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBACnB,OAAO,EAAE,MAAM;IAI3B,eAAe,CAAC,EAAE,EAAE,EAAE;;;;IAMtB,QAAQ;CAGT"}
@@ -1,14 +0,0 @@
1
- export const TX_MESSAGE_TOPIC = '';
2
- export class AztecTxMessageCreator {
3
- constructor(version) {
4
- this.topic = `/aztec/tx/${version.toString()}`;
5
- }
6
- createTxMessage(tx) {
7
- const messageData = tx.toBuffer();
8
- return { topic: this.topic, data: messageData };
9
- }
10
- getTopic() {
11
- return this.topic;
12
- }
13
- }
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfbWVzc2FnZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZS90eF9tZXNzYWdlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLENBQUM7QUFFbkMsTUFBTSxPQUFPLHFCQUFxQjtJQUVoQyxZQUFZLE9BQWU7UUFDekIsSUFBSSxDQUFDLEtBQUssR0FBRyxhQUFhLE9BQU8sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFFRCxlQUFlLENBQUMsRUFBTTtRQUNwQixNQUFNLFdBQVcsR0FBRyxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUM7UUFFbEMsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsQ0FBQztJQUNsRCxDQUFDO0lBRUQsUUFBUTtRQUNOLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQztJQUNwQixDQUFDO0NBQ0YifQ==
@@ -1,22 +0,0 @@
1
- import { type Tx } from '@aztec/circuit-types';
2
-
3
- import { type SemVer } from 'semver';
4
-
5
- export const TX_MESSAGE_TOPIC = '';
6
-
7
- export class AztecTxMessageCreator {
8
- private readonly topic: string;
9
- constructor(version: SemVer) {
10
- this.topic = `/aztec/tx/${version.toString()}`;
11
- }
12
-
13
- createTxMessage(tx: Tx) {
14
- const messageData = tx.toBuffer();
15
-
16
- return { topic: this.topic, data: messageData };
17
- }
18
-
19
- getTopic() {
20
- return this.topic;
21
- }
22
- }