@aztec/p2p 0.85.0 → 0.86.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 (90) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/bootstrap/bootstrap.js +6 -3
  3. package/dest/client/p2p_client.d.ts +41 -3
  4. package/dest/client/p2p_client.d.ts.map +1 -1
  5. package/dest/client/p2p_client.js +58 -18
  6. package/dest/config.d.ts +13 -2
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +15 -3
  9. package/dest/enr/generate-enr.d.ts +1 -1
  10. package/dest/enr/generate-enr.d.ts.map +1 -1
  11. package/dest/enr/generate-enr.js +2 -2
  12. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  13. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +11 -11
  14. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +2 -2
  15. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  16. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +4 -4
  17. package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
  18. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  19. package/dest/mem_pools/attestation_pool/mocks.js +2 -2
  20. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +3 -0
  21. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  22. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +18 -0
  23. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +3 -0
  24. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  25. package/dest/mem_pools/tx_pool/memory_tx_pool.js +9 -0
  26. package/dest/mem_pools/tx_pool/tx_pool.d.ts +17 -0
  27. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  28. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  29. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +57 -0
  30. package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
  31. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +1 -0
  32. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  33. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +6 -1
  34. package/dest/services/discv5/discV5_service.d.ts +2 -1
  35. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  36. package/dest/services/discv5/discV5_service.js +22 -7
  37. package/dest/services/dummy_service.d.ts +2 -2
  38. package/dest/services/dummy_service.d.ts.map +1 -1
  39. package/dest/services/dummy_service.js +2 -2
  40. package/dest/services/libp2p/libp2p_service.d.ts +3 -1
  41. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  42. package/dest/services/libp2p/libp2p_service.js +28 -12
  43. package/dest/services/peer-manager/peer_manager.d.ts +21 -2
  44. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  45. package/dest/services/peer-manager/peer_manager.js +63 -18
  46. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +2 -2
  47. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +17 -1
  48. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  49. package/dest/services/reqresp/connection-sampler/connection_sampler.js +84 -36
  50. package/dest/services/reqresp/reqresp.d.ts +2 -2
  51. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  52. package/dest/services/reqresp/reqresp.js +7 -2
  53. package/dest/services/reqresp/status.d.ts +2 -1
  54. package/dest/services/reqresp/status.d.ts.map +1 -1
  55. package/dest/services/reqresp/status.js +3 -0
  56. package/dest/services/service.d.ts +4 -4
  57. package/dest/services/service.d.ts.map +1 -1
  58. package/dest/test-helpers/make-test-p2p-clients.d.ts +6 -1
  59. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  60. package/dest/test-helpers/make-test-p2p-clients.js +19 -2
  61. package/dest/testbench/p2p_client_testbench_worker.js +4 -1
  62. package/dest/util.d.ts.map +1 -1
  63. package/dest/util.js +5 -1
  64. package/package.json +12 -14
  65. package/src/bootstrap/bootstrap.ts +8 -4
  66. package/src/client/p2p_client.ts +212 -131
  67. package/src/config.ts +34 -4
  68. package/src/enr/generate-enr.ts +2 -2
  69. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +11 -15
  70. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +2 -2
  71. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +4 -4
  72. package/src/mem_pools/attestation_pool/mocks.ts +3 -3
  73. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +21 -0
  74. package/src/mem_pools/tx_pool/memory_tx_pool.ts +11 -0
  75. package/src/mem_pools/tx_pool/tx_pool.ts +20 -0
  76. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +43 -0
  77. package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
  78. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +10 -1
  79. package/src/services/discv5/discV5_service.ts +32 -6
  80. package/src/services/dummy_service.ts +2 -2
  81. package/src/services/libp2p/libp2p_service.ts +37 -12
  82. package/src/services/peer-manager/peer_manager.ts +79 -22
  83. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +2 -2
  84. package/src/services/reqresp/connection-sampler/connection_sampler.ts +82 -41
  85. package/src/services/reqresp/reqresp.ts +12 -6
  86. package/src/services/reqresp/status.ts +3 -0
  87. package/src/services/service.ts +4 -4
  88. package/src/test-helpers/make-test-p2p-clients.ts +20 -2
  89. package/src/testbench/p2p_client_testbench_worker.ts +3 -0
  90. package/src/util.ts +6 -1
package/src/config.ts CHANGED
@@ -53,6 +53,11 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
53
53
  */
54
54
  p2pPort: number;
55
55
 
56
+ /**
57
+ * The port to broadcast the P2P service on (included in the node's ENR).
58
+ */
59
+ p2pBroadcastPort?: number;
60
+
56
61
  /**
57
62
  * The IP address for the P2P service.
58
63
  */
@@ -175,6 +180,11 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
175
180
  */
176
181
  trustedPeers: string[];
177
182
 
183
+ /**
184
+ * A list of private peers.
185
+ */
186
+ privatePeers: string[];
187
+
178
188
  /**
179
189
  * The maximum possible size of the P2P DB in KB. Overwrites the general dataStoreMapSizeKB.
180
190
  */
@@ -189,6 +199,8 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
189
199
  maxTxPoolSize: number;
190
200
  }
191
201
 
202
+ export const DEFAULT_P2P_PORT = 40400;
203
+
192
204
  export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
193
205
  p2pEnabled: {
194
206
  env: 'P2P_ENABLED',
@@ -222,8 +234,12 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
222
234
  },
223
235
  p2pPort: {
224
236
  env: 'P2P_PORT',
225
- description: 'The port for the P2P service.',
226
- ...numberConfigHelper(40400),
237
+ description: `The port for the P2P service. Defaults to ${DEFAULT_P2P_PORT}`,
238
+ ...numberConfigHelper(DEFAULT_P2P_PORT),
239
+ },
240
+ p2pBroadcastPort: {
241
+ env: 'P2P_BROADCAST_PORT',
242
+ description: `The port to broadcast the P2P service on (included in the node's ENR). Defaults to P2P_PORT.`,
227
243
  },
228
244
  p2pIp: {
229
245
  env: 'P2P_IP',
@@ -357,7 +373,14 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
357
373
  trustedPeers: {
358
374
  env: 'P2P_TRUSTED_PEERS',
359
375
  parseEnv: (val: string) => val.split(','),
360
- description: 'A list of trusted peers ENRs. Separated by commas.',
376
+ description: 'A list of trusted peer ENRs that will always be persisted. Separated by commas.',
377
+ defaultValue: [],
378
+ },
379
+ privatePeers: {
380
+ env: 'P2P_PRIVATE_PEERS',
381
+ parseEnv: (val: string) => val.split(','),
382
+ description:
383
+ 'A list of private peer ENRs that will always be persisted and not be used for discovery. Separated by commas.',
361
384
  defaultValue: [],
362
385
  },
363
386
  p2pStoreMapSizeKb: {
@@ -398,7 +421,13 @@ export function getP2PDefaultConfig(): P2PConfig {
398
421
  */
399
422
  export type BootnodeConfig = Pick<
400
423
  P2PConfig,
401
- 'p2pIp' | 'p2pPort' | 'peerIdPrivateKey' | 'peerIdPrivateKeyPath' | 'bootstrapNodes' | 'listenAddress'
424
+ | 'p2pIp'
425
+ | 'p2pPort'
426
+ | 'p2pBroadcastPort'
427
+ | 'peerIdPrivateKey'
428
+ | 'peerIdPrivateKeyPath'
429
+ | 'bootstrapNodes'
430
+ | 'listenAddress'
402
431
  > &
403
432
  Required<Pick<P2PConfig, 'p2pIp' | 'p2pPort'>> &
404
433
  Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKB'> &
@@ -407,6 +436,7 @@ export type BootnodeConfig = Pick<
407
436
  const bootnodeConfigKeys: (keyof BootnodeConfig)[] = [
408
437
  'p2pIp',
409
438
  'p2pPort',
439
+ 'p2pBroadcastPort',
410
440
  'listenAddress',
411
441
  'peerIdPrivateKey',
412
442
  'peerIdPrivateKeyPath',
@@ -12,12 +12,12 @@ import { setAztecEnrKey } from '../versioning.js';
12
12
  export async function createBootnodeENRandPeerId(
13
13
  privateKey: string,
14
14
  p2pIp: string,
15
- p2pPort: number,
15
+ p2pBroadcastPort: number,
16
16
  l1ChainId: number,
17
17
  ): Promise<{ enr: SignableENR; peerId: PeerId }> {
18
18
  const peerId = await createLibP2PPeerIdFromPrivateKey(privateKey);
19
19
  const enr = SignableENR.createFromPeerId(peerId);
20
- const publicAddr = multiaddr(convertToMultiaddr(p2pIp, p2pPort, 'udp'));
20
+ const publicAddr = multiaddr(convertToMultiaddr(p2pIp, p2pBroadcastPort, 'udp'));
21
21
  enr.setLocationMultiaddr(publicAddr);
22
22
 
23
23
  const config: ChainConfig = {
@@ -30,7 +30,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
30
30
 
31
31
  const createAttestationsForSlot = (slotNumber: number) => {
32
32
  const archive = Fr.random();
33
- return Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive)));
33
+ return signers.map(signer => mockAttestation(signer, slotNumber, archive));
34
34
  };
35
35
 
36
36
  // We compare buffers as the objects can have cached values attached to them which are not serialised
@@ -45,9 +45,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
45
45
  it('should add attestations to pool', async () => {
46
46
  const slotNumber = 420;
47
47
  const archive = Fr.random();
48
- const attestations = await Promise.all(
49
- signers.slice(0, -1).map(signer => mockAttestation(signer, slotNumber, archive)),
50
- );
48
+ const attestations = signers.slice(0, -1).map(signer => mockAttestation(signer, slotNumber, archive));
51
49
 
52
50
  await ap.addAttestations(attestations);
53
51
 
@@ -63,7 +61,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
63
61
  compareAttestations(retrievedAttestationsForSlot, attestations);
64
62
 
65
63
  // Add another one
66
- const newAttestation = await mockAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive);
64
+ const newAttestation = mockAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive);
67
65
  await ap.addAttestations([newAttestation]);
68
66
  expect(metricsMock.recordAddedObjects).toHaveBeenCalledWith(1);
69
67
  const retrievedAttestationsAfterAdd = await ap.getAttestationsForSlotAndProposal(
@@ -96,7 +94,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
96
94
  const attestations: BlockAttestation[] = [];
97
95
  const signer = signers[0];
98
96
  for (let i = 0; i < NUMBER_OF_SIGNERS_PER_TEST; i++) {
99
- attestations.push(await mockAttestation(signer, slotNumber, archive, txs));
97
+ attestations.push(mockAttestation(signer, slotNumber, archive, txs));
100
98
  }
101
99
 
102
100
  // Add them to store and check we end up with only one
@@ -106,7 +104,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
106
104
  expect(retreivedAttestations.length).toBe(1);
107
105
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer());
108
106
  expect(retreivedAttestations[0].payload.txHashes).toEqual(txs);
109
- expect((await retreivedAttestations[0].getSender()).toString()).toEqual(signer.address.toString());
107
+ expect(retreivedAttestations[0].getSender().toString()).toEqual(signer.address.toString());
110
108
 
111
109
  // Try adding them on another operation and check they are still not duplicated
112
110
  await ap.addAttestations([attestations[0]]);
@@ -115,7 +113,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
115
113
 
116
114
  it('should store attestations by differing slot', async () => {
117
115
  const slotNumbers = [1, 2, 3, 4];
118
- const attestations = await Promise.all(signers.map((signer, i) => mockAttestation(signer, slotNumbers[i])));
116
+ const attestations = signers.map((signer, i) => mockAttestation(signer, slotNumbers[i]));
119
117
 
120
118
  await ap.addAttestations(attestations);
121
119
 
@@ -133,9 +131,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
133
131
  it('should store attestations by differing slot and archive', async () => {
134
132
  const slotNumbers = [1, 1, 2, 3];
135
133
  const archives = [Fr.random(), Fr.random(), Fr.random(), Fr.random()];
136
- const attestations = await Promise.all(
137
- signers.map((signer, i) => mockAttestation(signer, slotNumbers[i], archives[i])),
138
- );
134
+ const attestations = signers.map((signer, i) => mockAttestation(signer, slotNumbers[i], archives[i]));
139
135
 
140
136
  await ap.addAttestations(attestations);
141
137
 
@@ -153,7 +149,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
153
149
  it('should delete attestations', async () => {
154
150
  const slotNumber = 420;
155
151
  const archive = Fr.random();
156
- const attestations = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive)));
152
+ const attestations = signers.map(signer => mockAttestation(signer, slotNumber, archive));
157
153
  const proposalId = attestations[0].archive.toString();
158
154
 
159
155
  await ap.addAttestations(attestations);
@@ -175,7 +171,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
175
171
  it('should blanket delete attestations per slot', async () => {
176
172
  const slotNumber = 420;
177
173
  const archive = Fr.random();
178
- const attestations = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive)));
174
+ const attestations = signers.map(signer => mockAttestation(signer, slotNumber, archive));
179
175
  const proposalId = attestations[0].archive.toString();
180
176
 
181
177
  await ap.addAttestations(attestations);
@@ -193,12 +189,12 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
193
189
  it('should blanket delete attestations per slot and proposal', async () => {
194
190
  const slotNumber = 420;
195
191
  const archive = Fr.random();
196
- const attestations = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive)));
192
+ const attestations = signers.map(signer => mockAttestation(signer, slotNumber, archive));
197
193
  const proposalId = attestations[0].archive.toString();
198
194
 
199
195
  // Add another set of attestations with a different proposalId, yet the same slot
200
196
  const archive2 = Fr.random();
201
- const attestations2 = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive2)));
197
+ const attestations2 = signers.map(signer => mockAttestation(signer, slotNumber, archive2));
202
198
  const proposalId2 = attestations2[0].archive.toString();
203
199
 
204
200
  await ap.addAttestations(attestations);
@@ -48,7 +48,7 @@ export class KvAttestationPool implements AttestationPool {
48
48
  for (const attestation of attestations) {
49
49
  const slotNumber = attestation.payload.header.globalVariables.slotNumber;
50
50
  const proposalId = attestation.archive;
51
- const address = (await attestation.getSender()).toString();
51
+ const address = attestation.getSender().toString();
52
52
 
53
53
  await this.attestations.set(this.getAttestationKey(slotNumber, proposalId, address), attestation.toBuffer());
54
54
 
@@ -160,7 +160,7 @@ export class KvAttestationPool implements AttestationPool {
160
160
  for (const attestation of attestations) {
161
161
  const slotNumber = attestation.payload.header.globalVariables.slotNumber;
162
162
  const proposalId = attestation.archive;
163
- const address = (await attestation.getSender()).toString();
163
+ const address = attestation.getSender().toString();
164
164
 
165
165
  await this.attestations.delete(this.getAttestationKey(slotNumber, proposalId, address));
166
166
  await this.attestationsForProposal.deleteValue(
@@ -34,13 +34,13 @@ export class InMemoryAttestationPool implements AttestationPool {
34
34
  return Promise.resolve([]);
35
35
  }
36
36
 
37
- public async addAttestations(attestations: BlockAttestation[]): Promise<void> {
37
+ public addAttestations(attestations: BlockAttestation[]): Promise<void> {
38
38
  for (const attestation of attestations) {
39
39
  // Perf: order and group by slot before insertion
40
40
  const slotNumber = attestation.payload.header.globalVariables.slotNumber;
41
41
 
42
42
  const proposalId = attestation.archive.toString();
43
- const address = await attestation.getSender();
43
+ const address = attestation.getSender();
44
44
 
45
45
  const slotAttestationMap = getSlotOrDefault(this.attestations, slotNumber.toBigInt());
46
46
  const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
@@ -118,7 +118,7 @@ export class InMemoryAttestationPool implements AttestationPool {
118
118
  return Promise.resolve();
119
119
  }
120
120
 
121
- public async deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
121
+ public deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
122
122
  for (const attestation of attestations) {
123
123
  const slotNumber = attestation.payload.header.globalVariables.slotNumber;
124
124
  const slotAttestationMap = this.attestations.get(slotNumber.toBigInt());
@@ -126,7 +126,7 @@ export class InMemoryAttestationPool implements AttestationPool {
126
126
  const proposalId = attestation.archive.toString();
127
127
  const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
128
128
  if (proposalAttestationMap) {
129
- const address = await attestation.getSender();
129
+ const address = attestation.getSender();
130
130
  proposalAttestationMap.delete(address.toString());
131
131
  this.log.debug(`Deleted attestation for slot ${slotNumber} from ${address}`);
132
132
  }
@@ -27,17 +27,17 @@ export const generateAccount = (): LocalAccount => {
27
27
  * @param slot The slot number the attestation is for
28
28
  * @returns A Block Attestation
29
29
  */
30
- export const mockAttestation = async (
30
+ export const mockAttestation = (
31
31
  signer: Secp256k1Signer,
32
32
  slot: number = 0,
33
33
  archive: Fr = Fr.random(),
34
34
  txs: TxHash[] = [0, 1, 2, 3, 4, 5].map(() => TxHash.random()),
35
- ): Promise<BlockAttestation> => {
35
+ ): BlockAttestation => {
36
36
  // Use arbitrary numbers for all other than slot
37
37
  const header = makeHeader(1, 2, slot);
38
38
  const payload = new ConsensusPayload(header, archive, txs);
39
39
 
40
- const hash = await getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
40
+ const hash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
41
41
  const signature = signer.sign(hash);
42
42
 
43
43
  return new BlockAttestation(payload, signature);
@@ -218,6 +218,22 @@ export class AztecKVTxPool implements TxPool {
218
218
  return undefined;
219
219
  }
220
220
 
221
+ async getTxsByHash(txHashes: TxHash[]): Promise<(Tx | undefined)[]> {
222
+ const txs = await Promise.all(txHashes.map(txHash => this.#txs.getAsync(txHash.toString())));
223
+ return txs.map((buffer, index) => {
224
+ if (buffer) {
225
+ const tx = Tx.fromBuffer(buffer);
226
+ tx.setTxHash(txHashes[index]);
227
+ return tx;
228
+ }
229
+ return undefined;
230
+ });
231
+ }
232
+
233
+ async hasTxs(txHashes: TxHash[]): Promise<boolean[]> {
234
+ return await Promise.all(txHashes.map(txHash => this.#txs.hasAsync(txHash.toString())));
235
+ }
236
+
221
237
  /**
222
238
  * Checks if an archived tx exists and returns it.
223
239
  * @param txHash - The tx hash.
@@ -341,6 +357,11 @@ export class AztecKVTxPool implements TxPool {
341
357
  return vals.map(x => TxHash.fromString(x));
342
358
  }
343
359
 
360
+ public setMaxTxPoolSize(maxSizeBytes: number | undefined): Promise<void> {
361
+ this.#maxTxPoolSize = maxSizeBytes;
362
+ return Promise.resolve();
363
+ }
364
+
344
365
  /**
345
366
  * Creates a GasTxValidator instance.
346
367
  * @param db - DB for the validator to use
@@ -103,6 +103,13 @@ export class InMemoryTxPool implements TxPool {
103
103
  return Promise.resolve(result === undefined ? undefined : Tx.clone(result));
104
104
  }
105
105
 
106
+ getTxsByHash(txHashes: TxHash[]): Promise<(Tx | undefined)[]> {
107
+ return Promise.all(txHashes.map(txHash => this.getTxByHash(txHash)));
108
+ }
109
+ hasTxs(txHashes: TxHash[]): Promise<boolean[]> {
110
+ return Promise.resolve(txHashes.map(txHash => this.txs.has(txHash.toBigInt())));
111
+ }
112
+
106
113
  public getArchivedTxByHash(): Promise<Tx | undefined> {
107
114
  return Promise.resolve(undefined);
108
115
  }
@@ -171,4 +178,8 @@ export class InMemoryTxPool implements TxPool {
171
178
  public getAllTxHashes(): Promise<TxHash[]> {
172
179
  return Promise.resolve(Array.from(this.txs.keys()).map(x => TxHash.fromBigInt(x)));
173
180
  }
181
+
182
+ setMaxTxPoolSize(_maxSizeBytes: number | undefined): Promise<void> {
183
+ return Promise.resolve();
184
+ }
174
185
  }
@@ -17,6 +17,20 @@ export interface TxPool {
17
17
  */
18
18
  getTxByHash(txHash: TxHash): Promise<Tx | undefined>;
19
19
 
20
+ /**
21
+ * Checks if transactions exist in the pool and returns them.
22
+ * @param txHashes - The hashes of the transactions
23
+ * @returns The transactions, if found, 'undefined' otherwise.
24
+ */
25
+ getTxsByHash(txHashes: TxHash[]): Promise<(Tx | undefined)[]>;
26
+
27
+ /**
28
+ * Checks if transactions exist in the pool
29
+ * @param txHashes - The hashes of the transactions to check for
30
+ * @returns True or False for each tx hash
31
+ */
32
+ hasTxs(txHashes: TxHash[]): Promise<boolean[]>;
33
+
20
34
  /**
21
35
  * Checks if an archived transaction exists in the pool and returns it.
22
36
  * @param txHash - The hash of the transaction, used as an ID.
@@ -73,4 +87,10 @@ export interface TxPool {
73
87
  * @returns Pending or mined depending on its status, or undefined if not found.
74
88
  */
75
89
  getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined>;
90
+
91
+ /**
92
+ * Configure the maximum size of the tx pool
93
+ * @param maxSizeBytes - The maximum size in bytes of the mempool. Set to undefined to disable it
94
+ */
95
+ setMaxTxPoolSize(maxSizeBytes: number | undefined): Promise<void>;
76
96
  }
@@ -110,6 +110,49 @@ export function describeTxPool(getTxPool: () => TxPool) {
110
110
  );
111
111
  });
112
112
 
113
+ it('Returns txs by their hash', async () => {
114
+ const tx1 = await mockTx(1);
115
+ const tx2 = await mockTx(2);
116
+ const tx3 = await mockTx(3);
117
+
118
+ await pool.addTxs([tx1, tx2, tx3]);
119
+
120
+ const requestedTxs = await pool.getTxsByHash([await tx1.getTxHash(), await tx3.getTxHash()]);
121
+ expect(requestedTxs).toHaveLength(2);
122
+ expect(requestedTxs).toEqual(expect.arrayContaining([tx1, tx3]));
123
+ });
124
+
125
+ it('Returns a large number of transactions by their hash', async () => {
126
+ const numTxs = 1000;
127
+ const txs = await Promise.all(Array.from({ length: numTxs }, (_, i) => mockTx(i)));
128
+ const hashes = await Promise.all(txs.map(tx => tx.getTxHash()));
129
+ await pool.addTxs(txs);
130
+ const requestedTxs = await pool.getTxsByHash(hashes);
131
+ expect(requestedTxs).toHaveLength(numTxs);
132
+ expect(requestedTxs).toEqual(expect.arrayContaining(txs));
133
+ });
134
+
135
+ it('Returns whether or not txs exist', async () => {
136
+ const tx1 = await mockTx(1);
137
+ const tx2 = await mockTx(2);
138
+ const tx3 = await mockTx(3);
139
+
140
+ await pool.addTxs([tx1, tx2, tx3]);
141
+
142
+ const tx4 = await mockTx(4);
143
+ const tx5 = await mockTx(5);
144
+
145
+ const availability = await pool.hasTxs([
146
+ await tx1.getTxHash(),
147
+ await tx2.getTxHash(),
148
+ await tx3.getTxHash(),
149
+ await tx4.getTxHash(),
150
+ await tx5.getTxHash(),
151
+ ]);
152
+ expect(availability).toHaveLength(5);
153
+ expect(availability).toEqual(expect.arrayContaining([true, true, true, false, false]));
154
+ });
155
+
113
156
  it('Returns pending tx hashes sorted by priority', async () => {
114
157
  const withPriorityFee = (tx: Tx, fee: number) => {
115
158
  unfreeze(tx.data.constants.txContext.gasSettings).maxPriorityFeesPerGas = new GasFees(fee, fee);
@@ -16,7 +16,7 @@ export class AttestationValidator implements P2PValidator<BlockAttestation> {
16
16
  return PeerErrorSeverity.HighToleranceError;
17
17
  }
18
18
 
19
- const attester = await message.getSender();
19
+ const attester = message.getSender();
20
20
  if (!(await this.epochCache.isInCommittee(attester))) {
21
21
  return PeerErrorSeverity.HighToleranceError;
22
22
  }
@@ -1,11 +1,14 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
+ import { type Logger, createLogger } from '@aztec/foundation/log';
2
3
  import { type BlockProposal, type P2PValidator, PeerErrorSeverity } from '@aztec/stdlib/p2p';
3
4
 
4
5
  export class BlockProposalValidator implements P2PValidator<BlockProposal> {
5
6
  private epochCache: EpochCacheInterface;
7
+ private logger: Logger;
6
8
 
7
9
  constructor(epochCache: EpochCacheInterface) {
8
10
  this.epochCache = epochCache;
11
+ this.logger = createLogger('p2p:block_proposal_validator');
9
12
  }
10
13
 
11
14
  async validate(block: BlockProposal): Promise<PeerErrorSeverity | undefined> {
@@ -15,12 +18,18 @@ export class BlockProposalValidator implements P2PValidator<BlockProposal> {
15
18
  // Check that the attestation is for the current or next slot
16
19
  const slotNumberBigInt = block.payload.header.globalVariables.slotNumber.toBigInt();
17
20
  if (slotNumberBigInt !== currentSlot && slotNumberBigInt !== nextSlot) {
21
+ this.logger.debug(
22
+ `Penalizing peer for invalid slot number ${slotNumberBigInt}, current slot: ${currentSlot}, next slot: ${nextSlot}`,
23
+ );
18
24
  return PeerErrorSeverity.HighToleranceError;
19
25
  }
20
26
 
21
27
  // Check that the block proposal is from the current or next proposer
22
- const proposer = await block.getSender();
28
+ const proposer = block.getSender();
23
29
  if (!proposer.equals(currentProposer) && !proposer.equals(nextProposer)) {
30
+ this.logger.debug(
31
+ `Penalizing peer for invalid proposer ${proposer.toString()}, current proposer: ${currentProposer.toString()}, next proposer: ${nextProposer.toString()}`,
32
+ );
24
33
  return PeerErrorSeverity.HighToleranceError;
25
34
  }
26
35
 
@@ -34,6 +34,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
34
34
 
35
35
  private bootstrapNodePeerIds: PeerId[] = [];
36
36
  public bootstrapNodeEnrs: ENR[] = [];
37
+ private trustedPeerEnrs: ENR[] = [];
37
38
 
38
39
  private startTime = 0;
39
40
 
@@ -51,21 +52,33 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
51
52
  configOverrides: Partial<IDiscv5CreateOptions> = {},
52
53
  ) {
53
54
  super();
54
- const { p2pIp, p2pPort, bootstrapNodes } = config;
55
+ const { p2pIp, p2pPort, p2pBroadcastPort, bootstrapNodes, trustedPeers, privatePeers } = config;
56
+
55
57
  this.bootstrapNodeEnrs = bootstrapNodes.map(x => ENR.decodeTxt(x));
58
+ const privatePeerEnrs = new Set(privatePeers);
59
+ this.trustedPeerEnrs = trustedPeers.filter(x => !privatePeerEnrs.has(x)).map(x => ENR.decodeTxt(x));
56
60
  // create ENR from PeerId
57
61
  this.enr = SignableENR.createFromPeerId(peerId);
58
62
  // Add aztec identification to ENR
59
63
  this.versions = setAztecEnrKey(this.enr, config);
60
64
 
65
+ // If no overridden broadcast port is provided, use the p2p port as the broadcast port
66
+ if (!p2pBroadcastPort) {
67
+ this.logger.warn('No p2pBroadcastPort provided, using p2pPort as broadcast port');
68
+ config.p2pBroadcastPort = p2pPort;
69
+ }
70
+
61
71
  const bindAddrs: any = {
62
72
  ip4: multiaddr(convertToMultiaddr(config.listenAddress, p2pPort, 'udp')),
63
73
  };
64
74
 
65
75
  if (p2pIp) {
66
- const multiAddrTcp = multiaddr(`${convertToMultiaddr(p2pIp!, p2pPort, 'tcp')}/p2p/${peerId.toString()}`);
67
- // if no udp announce address is provided, use the tcp announce address
68
- const multiAddrUdp = multiaddr(`${convertToMultiaddr(p2pIp!, p2pPort, 'udp')}/p2p/${peerId.toString()}`);
76
+ const multiAddrTcp = multiaddr(
77
+ `${convertToMultiaddr(p2pIp!, config.p2pBroadcastPort!, 'tcp')}/p2p/${peerId.toString()}`,
78
+ );
79
+ const multiAddrUdp = multiaddr(
80
+ `${convertToMultiaddr(p2pIp!, config.p2pBroadcastPort!, 'udp')}/p2p/${peerId.toString()}`,
81
+ );
69
82
 
70
83
  // set location multiaddr in ENR record
71
84
  this.enr.setLocationMultiaddr(multiAddrUdp);
@@ -113,7 +126,8 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
113
126
 
114
127
  private onMultiaddrUpdated(m: Multiaddr) {
115
128
  // We want to update our tcp port to match the udp port
116
- const multiAddrTcp = multiaddr(convertToMultiaddr(m.nodeAddress().address, this.config.p2pPort, 'tcp'));
129
+ // p2pBroadcastPort is optional on config, however it is set to default within the p2p client factory
130
+ const multiAddrTcp = multiaddr(convertToMultiaddr(m.nodeAddress().address, this.config.p2pBroadcastPort!, 'tcp'));
117
131
  this.enr.setLocationMultiaddr(multiAddrTcp);
118
132
  this.logger.info('Multiaddr updated', { multiaddr: multiAddrTcp.toString() });
119
133
  }
@@ -159,6 +173,18 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
159
173
  }
160
174
  }
161
175
  }
176
+
177
+ // Add trusted peer ENRs if provided
178
+ if (this.trustedPeerEnrs?.length) {
179
+ this.logger.info(
180
+ `Adding ${this.trustedPeerEnrs.length} trusted peer ENRs: ${this.trustedPeerEnrs
181
+ .map(enr => enr.encodeTxt())
182
+ .join(', ')}`,
183
+ );
184
+ for (const enr of this.trustedPeerEnrs) {
185
+ this.discv5.addEnr(enr);
186
+ }
187
+ }
162
188
  }
163
189
 
164
190
  public async runRandomNodesQuery(): Promise<void> {
@@ -180,7 +206,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
180
206
  }
181
207
  }
182
208
 
183
- public getAllPeers(): ENR[] {
209
+ public getKadValues(): ENR[] {
184
210
  return this.discv5.kadValues();
185
211
  }
186
212
 
@@ -111,9 +111,9 @@ export class DummyPeerDiscoveryService extends EventEmitter implements PeerDisco
111
111
  }
112
112
  /**
113
113
  * Called to discover peers in the network.
114
- * @returns An array of discovered peer addresses.
114
+ * @returns An array of Enrs.
115
115
  */
116
- public getAllPeers() {
116
+ public getKadValues() {
117
117
  return [];
118
118
  }
119
119