@aztec/p2p 0.87.7 → 0.87.8

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 (51) hide show
  1. package/dest/client/interface.d.ts +1 -1
  2. package/dest/client/interface.d.ts.map +1 -1
  3. package/dest/client/p2p_client.d.ts +2 -2
  4. package/dest/client/p2p_client.d.ts.map +1 -1
  5. package/dest/client/p2p_client.js +3 -3
  6. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  7. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +0 -9
  8. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +1 -0
  9. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  10. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +13 -8
  11. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +1 -0
  12. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  13. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +7 -6
  14. package/dest/mem_pools/instrumentation.d.ts +7 -11
  15. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  16. package/dest/mem_pools/instrumentation.js +25 -37
  17. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +2 -1
  18. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  19. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +22 -38
  20. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +1 -0
  21. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  22. package/dest/mem_pools/tx_pool/memory_tx_pool.js +13 -21
  23. package/dest/services/encoding.d.ts +2 -0
  24. package/dest/services/encoding.d.ts.map +1 -1
  25. package/dest/services/encoding.js +9 -1
  26. package/dest/services/reqresp/reqresp.d.ts +1 -1
  27. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  28. package/dest/services/reqresp/reqresp.js +9 -5
  29. package/dest/services/tx_collect_instrumentation.d.ts +13 -0
  30. package/dest/services/tx_collect_instrumentation.d.ts.map +1 -0
  31. package/dest/services/tx_collect_instrumentation.js +34 -0
  32. package/dest/services/tx_collector.d.ts +6 -2
  33. package/dest/services/tx_collector.d.ts.map +1 -1
  34. package/dest/services/tx_collector.js +61 -49
  35. package/dest/test-helpers/reqresp-nodes.d.ts +2 -0
  36. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  37. package/dest/test-helpers/reqresp-nodes.js +6 -0
  38. package/package.json +12 -12
  39. package/src/client/interface.ts +1 -1
  40. package/src/client/p2p_client.ts +3 -3
  41. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +0 -14
  42. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +17 -12
  43. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +8 -7
  44. package/src/mem_pools/instrumentation.ts +32 -46
  45. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +23 -58
  46. package/src/mem_pools/tx_pool/memory_tx_pool.ts +14 -26
  47. package/src/services/encoding.ts +9 -1
  48. package/src/services/reqresp/reqresp.ts +6 -6
  49. package/src/services/tx_collect_instrumentation.ts +44 -0
  50. package/src/services/tx_collector.ts +87 -66
  51. package/src/test-helpers/reqresp-nodes.ts +6 -0
@@ -0,0 +1,44 @@
1
+ import { Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
2
+
3
+ export class TxCollectorInstrumentation {
4
+ private txFromProposalCount: UpDownCounter;
5
+ private txFromMempoolCount: UpDownCounter;
6
+ private txFromP2PCount: UpDownCounter;
7
+ private missingTxsCount: UpDownCounter;
8
+
9
+ constructor(client: TelemetryClient, name: string) {
10
+ const meter = client.getMeter(name);
11
+
12
+ this.txFromProposalCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_TXS_FROM_PROPOSALS_COUNT, {
13
+ description: 'The number of txs taken from block proposals',
14
+ });
15
+
16
+ this.txFromMempoolCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_TXS_FROM_MEMPOOL_COUNT, {
17
+ description: 'The number of txs taken from the local mempool',
18
+ });
19
+
20
+ this.txFromP2PCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_TXS_FROM_P2P_COUNT, {
21
+ description: 'The number of txs taken from the p2p network',
22
+ });
23
+
24
+ this.missingTxsCount = meter.createUpDownCounter(Metrics.TX_COLLECTOR_MISSING_TXS_COUNT, {
25
+ description: 'The number of txs not found anywhere',
26
+ });
27
+ }
28
+
29
+ incTxsFromProposals(count: number) {
30
+ this.txFromProposalCount.add(count);
31
+ }
32
+
33
+ incTxsFromMempool(count: number) {
34
+ this.txFromMempoolCount.add(count);
35
+ }
36
+
37
+ incTxsFromP2P(count: number) {
38
+ this.txFromP2PCount.add(count);
39
+ }
40
+
41
+ incMissingTxs(count: number) {
42
+ this.missingTxsCount.add(count);
43
+ }
44
+ }
@@ -2,102 +2,123 @@ import { compactArray } from '@aztec/foundation/collection';
2
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import type { BlockProposal } from '@aztec/stdlib/p2p';
4
4
  import type { Tx, TxHash } from '@aztec/stdlib/tx';
5
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
6
+
7
+ import type { PeerId } from '@libp2p/interface';
5
8
 
6
9
  import type { P2PClient } from '../client/p2p_client.js';
10
+ import { TxCollectorInstrumentation } from './tx_collect_instrumentation.js';
7
11
 
8
12
  export class TxCollector {
13
+ private instrumentation: TxCollectorInstrumentation;
14
+
9
15
  constructor(
10
16
  private p2pClient: Pick<
11
17
  P2PClient,
12
- 'getTxsByHashFromPool' | 'hasTxsInPool' | 'getTxsByHash' | 'validate' | 'requestTxsByHash'
18
+ 'getTxsByHashFromPool' | 'hasTxsInPool' | 'getTxsByHash' | 'validate' | 'requestTxsByHash' | 'addTxsToPool'
13
19
  >,
14
20
  private log: Logger = createLogger('p2p:tx-collector'),
15
- ) {}
21
+ client: TelemetryClient = getTelemetryClient(),
22
+ ) {
23
+ this.instrumentation = new TxCollectorInstrumentation(client, 'TxCollector');
24
+ }
25
+
26
+ // Checks the proposal for transactions we don't already have, validates them and adds them to our pool
27
+ private async collectFromProposal(proposal: BlockProposal): Promise<number> {
28
+ // Does this proposal have any transactions?
29
+ if (!proposal.txs || proposal.txs.length === 0) {
30
+ return 0;
31
+ }
32
+
33
+ const proposalHashes = new Set<string>((proposal.payload.txHashes ?? []).map(txHash => txHash.toString()));
34
+
35
+ // Get the transactions from the proposal and their hashes
36
+ // also, we are only interested in txs that are part of the proposal
37
+ const txsFromProposal = compactArray(
38
+ await Promise.all(
39
+ proposal.txs.map(tx =>
40
+ tx === undefined
41
+ ? Promise.resolve(undefined)
42
+ : tx.getTxHash().then(hash => ({
43
+ txHash: hash,
44
+ tx,
45
+ })),
46
+ ),
47
+ ),
48
+ ).filter(tx => proposalHashes.has(tx.txHash.toString()));
49
+
50
+ // Of the transactions from the proposal, retrieve those that we have in the pool already
51
+ const txsToValidate = [];
52
+ const txsWeAlreadyHave = await this.p2pClient.getTxsByHashFromPool(txsFromProposal.map(tx => tx.txHash));
53
+
54
+ // Txs we already have will have holes where we did not find them
55
+ // Where that is the case we need to validate the tx in the proposal
56
+ for (let i = 0; i < txsWeAlreadyHave.length; i++) {
57
+ if (txsWeAlreadyHave[i] === undefined) {
58
+ txsToValidate.push(txsFromProposal[i].tx);
59
+ }
60
+ }
61
+
62
+ // Now validate all the transactions from the proposal that we don't have
63
+ // This will throw if any of the transactions are invalid, this is probably correct, if someone sends us a proposal with invalid
64
+ // transactions we probably shouldn't spend any more effort on it
65
+ try {
66
+ await this.p2pClient.validate(txsToValidate);
67
+ } catch (err) {
68
+ this.log.error(`Received proposal with invalid transactions, skipping`);
69
+ throw err;
70
+ }
71
+
72
+ // Now store these transactions in our pool, provided these are the txs in proposal.payload.txHashes they will be pinned already
73
+ await this.p2pClient.addTxsToPool(txsToValidate);
74
+
75
+ return txsToValidate.length;
76
+ }
16
77
 
17
78
  async collectForBlockProposal(
18
79
  proposal: BlockProposal,
19
- peerWhoSentTheProposal: any,
80
+ peerWhoSentTheProposal: PeerId | undefined,
20
81
  ): Promise<{ txs: Tx[]; missing?: TxHash[] }> {
21
82
  if (proposal.payload.txHashes.length === 0) {
22
83
  this.log.verbose(`Received block proposal with no transactions, skipping transaction availability check`);
23
84
  return { txs: [] };
24
85
  }
25
- // Is this a new style proposal?
26
- if (proposal.txs && proposal.txs.length > 0 && proposal.txs.length === proposal.payload.txHashes.length) {
27
- // Yes, any txs that we already have we should use
28
- this.log.info(`Using new style proposal with ${proposal.txs.length} transactions`);
29
-
30
- // Request from the pool based on the signed hashes in the payload
31
- const hashesFromPayload = proposal.payload.txHashes;
32
- const txsToUse = await this.p2pClient.getTxsByHashFromPool(hashesFromPayload);
33
-
34
- const missingTxs = txsToUse.filter(tx => tx === undefined).length;
35
- if (missingTxs > 0) {
36
- this.log.verbose(
37
- `Missing ${missingTxs}/${hashesFromPayload.length} transactions in the tx pool, will attempt to take from the proposal`,
38
- );
39
- }
40
86
 
41
- let usedFromProposal = 0;
42
-
43
- // Fill any holes with txs in the proposal, provided their hash matches the hash in the payload
44
- for (let i = 0; i < txsToUse.length; i++) {
45
- if (txsToUse[i] === undefined) {
46
- // We don't have the transaction, take from the proposal, provided the hash is the same
47
- const hashOfTxInProposal = await proposal.txs[i].getTxHash();
48
- if (hashOfTxInProposal.equals(hashesFromPayload[i])) {
49
- // Hash is equal, we can use the tx from the proposal
50
- txsToUse[i] = proposal.txs[i];
51
- usedFromProposal++;
52
- } else {
53
- this.log.warn(
54
- `Unable to take tx: ${hashOfTxInProposal.toString()} from the proposal, it does not match payload hash: ${hashesFromPayload[
55
- i
56
- ].toString()}`,
57
- );
58
- }
59
- }
60
- }
61
-
62
- // See if we still have any holes, if there are then we were not successful and will try the old method
63
- if (txsToUse.some(tx => tx === undefined)) {
64
- this.log.warn(`Failed to use transactions from proposal. Falling back to old proposal logic`);
65
- } else {
66
- this.log.info(
67
- `Successfully used ${usedFromProposal}/${hashesFromPayload.length} transactions from the proposal`,
68
- );
69
-
70
- await this.p2pClient.validate(txsToUse as Tx[]);
71
- return { txs: txsToUse as Tx[] };
72
- }
73
- }
87
+ const txsInMempool = (await this.p2pClient.hasTxsInPool(proposal.payload.txHashes)).filter(Boolean).length;
88
+ this.instrumentation.incTxsFromMempool(txsInMempool);
74
89
 
75
- this.log.info(`Using old style proposal with ${proposal.payload.txHashes.length} transactions`);
90
+ // Take txs from the proposal if there are any
91
+ const txTakenFromProposal = await this.collectFromProposal(proposal);
92
+ this.instrumentation.incTxsFromProposals(txTakenFromProposal);
76
93
 
77
- // Old style proposal, we will perform a request by hash from pool
78
- // This will request from network any txs that are missing
94
+ // Now get the txs we need, either from the pool or the p2p network
79
95
  const txHashes: TxHash[] = proposal.payload.txHashes;
80
96
 
81
- // This part is just for logging that we are requesting from the network
82
- const availability = await this.p2pClient.hasTxsInPool(txHashes);
83
- const notAvailable = availability.filter(availability => availability === false);
84
- if (notAvailable.length) {
85
- this.log.verbose(
86
- `Missing ${notAvailable.length} transactions in the tx pool, will need to request from the network`,
87
- );
88
- }
89
-
90
97
  // This will request from the network any txs that are missing
91
98
  // NOTE: this could still return missing txs so we need to (1) be careful to handle undefined and (2) keep the txs in the correct order for re-execution
92
99
  const maybeRetrievedTxs = await this.p2pClient.getTxsByHash(txHashes, peerWhoSentTheProposal);
100
+
101
+ // Get the txs that we didn't get from the network, if any. This will be empty if we got them al
93
102
  const missingTxs = compactArray(
94
103
  maybeRetrievedTxs.map((tx, index) => (tx === undefined ? txHashes[index] : undefined)),
95
104
  );
96
- // if we found all txs, this is a noop. If we didn't find all txs then validate the ones we did find and tell the validator to skip attestations because missingTxs.length > 0
97
- const retrievedTxs = compactArray(maybeRetrievedTxs);
105
+ this.instrumentation.incMissingTxs(missingTxs.length);
106
+
107
+ const txsFromP2P = txHashes.length - txTakenFromProposal - txsInMempool - missingTxs.length;
108
+ this.instrumentation.incTxsFromP2P(txsFromP2P);
98
109
 
99
- await this.p2pClient.validate(retrievedTxs);
110
+ // if we found all txs, this is a noop. If we didn't find all txs then tell the validator to skip attestations because missingTxs.length > 0
111
+ const retrievedTxs = compactArray(maybeRetrievedTxs);
100
112
 
113
+ this.log.info(`Retrieved ${retrievedTxs.length}/${txHashes.length} txs for block proposal`, {
114
+ blockNumber: proposal.blockNumber.toNumber(),
115
+ slotNumber: proposal.slotNumber.toNumber(),
116
+ totalTxsInProposal: txHashes.length,
117
+ txsFromProposal: txTakenFromProposal,
118
+ txsFromMempool: txsInMempool,
119
+ txsFromP2P,
120
+ missingTxs: missingTxs.length,
121
+ });
101
122
  return { txs: retrievedTxs, missing: missingTxs };
102
123
  }
103
124
  }
@@ -225,11 +225,17 @@ export const connectToPeers = async (nodes: ReqRespNode[]): Promise<void> => {
225
225
 
226
226
  // Mock circuit verifier for testing - reimplementation from bb to avoid dependency
227
227
  export class AlwaysTrueCircuitVerifier implements ClientProtocolCircuitVerifier {
228
+ stop(): Promise<void> {
229
+ return Promise.resolve();
230
+ }
228
231
  verifyProof(_tx: Tx): Promise<boolean> {
229
232
  return Promise.resolve(true);
230
233
  }
231
234
  }
232
235
  export class AlwaysFalseCircuitVerifier implements ClientProtocolCircuitVerifier {
236
+ stop(): Promise<void> {
237
+ return Promise.resolve();
238
+ }
233
239
  verifyProof(_tx: Tx): Promise<boolean> {
234
240
  return Promise.resolve(false);
235
241
  }