@aztec/p2p 0.86.0 → 0.87.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 (159) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/client/factory.d.ts +4 -1
  3. package/dest/client/factory.d.ts.map +1 -1
  4. package/dest/client/factory.js +16 -14
  5. package/dest/client/index.d.ts +1 -0
  6. package/dest/client/index.d.ts.map +1 -1
  7. package/dest/client/index.js +1 -0
  8. package/dest/client/interface.d.ts +155 -0
  9. package/dest/client/interface.d.ts.map +1 -0
  10. package/dest/client/interface.js +9 -0
  11. package/dest/client/p2p_client.d.ts +26 -164
  12. package/dest/client/p2p_client.d.ts.map +1 -1
  13. package/dest/client/p2p_client.js +185 -114
  14. package/dest/config.d.ts +5 -6
  15. package/dest/config.d.ts.map +1 -1
  16. package/dest/config.js +6 -11
  17. package/dest/enr/generate-enr.d.ts +9 -1
  18. package/dest/enr/generate-enr.d.ts.map +1 -1
  19. package/dest/enr/generate-enr.js +24 -2
  20. package/dest/index.d.ts +1 -0
  21. package/dest/index.d.ts.map +1 -1
  22. package/dest/index.js +1 -0
  23. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +2 -0
  24. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  25. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +4 -4
  26. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +1 -0
  27. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  28. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +8 -2
  29. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +1 -0
  30. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  31. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +5 -2
  32. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/mocks.js +2 -2
  34. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +4 -0
  35. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +50 -14
  37. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +3 -0
  38. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  39. package/dest/mem_pools/tx_pool/memory_tx_pool.js +9 -0
  40. package/dest/mem_pools/tx_pool/tx_pool.d.ts +9 -0
  41. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  42. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +13 -5
  44. package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
  45. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +1 -1
  46. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  47. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  48. package/dest/msg_validators/tx_validator/data_validator.js +15 -14
  49. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +0 -2
  50. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  51. package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
  52. package/dest/msg_validators/tx_validator/factory.d.ts +14 -0
  53. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
  54. package/dest/msg_validators/tx_validator/factory.js +62 -0
  55. package/dest/msg_validators/tx_validator/gas_validator.js +3 -3
  56. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +8 -4
  57. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  58. package/dest/msg_validators/tx_validator/metadata_validator.js +35 -17
  59. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  60. package/dest/msg_validators/tx_validator/phases_validator.js +1 -1
  61. package/dest/msg_validators/tx_validator/tx_proof_validator.js +1 -1
  62. package/dest/services/discv5/discV5_service.d.ts +2 -2
  63. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  64. package/dest/services/discv5/discV5_service.js +9 -13
  65. package/dest/services/dummy_service.d.ts +3 -3
  66. package/dest/services/dummy_service.d.ts.map +1 -1
  67. package/dest/services/dummy_service.js +6 -1
  68. package/dest/services/encoding.d.ts +1 -3
  69. package/dest/services/encoding.d.ts.map +1 -1
  70. package/dest/services/libp2p/libp2p_service.d.ts +4 -2
  71. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  72. package/dest/services/libp2p/libp2p_service.js +94 -88
  73. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  74. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  75. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  76. package/dest/services/peer-manager/peer_manager.js +11 -2
  77. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  78. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +2 -2
  79. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  80. package/dest/services/reqresp/connection-sampler/connection_sampler.js +41 -21
  81. package/dest/services/reqresp/interface.d.ts +1 -3
  82. package/dest/services/reqresp/interface.d.ts.map +1 -1
  83. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  84. package/dest/services/reqresp/protocols/goodbye.d.ts +0 -2
  85. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  86. package/dest/services/reqresp/protocols/goodbye.js +1 -1
  87. package/dest/services/reqresp/protocols/ping.d.ts +0 -2
  88. package/dest/services/reqresp/protocols/ping.d.ts.map +1 -1
  89. package/dest/services/reqresp/protocols/status.d.ts +0 -2
  90. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  91. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  92. package/dest/services/reqresp/reqresp.d.ts +1 -3
  93. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  94. package/dest/services/reqresp/reqresp.js +13 -10
  95. package/dest/services/service.d.ts +4 -3
  96. package/dest/services/service.d.ts.map +1 -1
  97. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  98. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  99. package/dest/test-helpers/make-test-p2p-clients.js +2 -2
  100. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  101. package/dest/test-helpers/reqresp-nodes.js +1 -1
  102. package/dest/testbench/p2p_client_testbench_worker.js +11 -6
  103. package/dest/testbench/testbench.js +1 -1
  104. package/dest/testbench/worker_client_manager.d.ts +0 -1
  105. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  106. package/dest/testbench/worker_client_manager.js +2 -2
  107. package/dest/types/index.d.ts +1 -0
  108. package/dest/types/index.d.ts.map +1 -1
  109. package/dest/types/index.js +1 -0
  110. package/dest/versioning.d.ts +2 -2
  111. package/dest/versioning.d.ts.map +1 -1
  112. package/dest/versioning.js +6 -1
  113. package/package.json +15 -15
  114. package/src/bootstrap/bootstrap.ts +1 -1
  115. package/src/client/factory.ts +38 -33
  116. package/src/client/index.ts +1 -0
  117. package/src/client/interface.ts +186 -0
  118. package/src/client/p2p_client.ts +226 -287
  119. package/src/config.ts +11 -18
  120. package/src/enr/generate-enr.ts +35 -3
  121. package/src/index.ts +1 -0
  122. package/src/mem_pools/attestation_pool/attestation_pool.ts +3 -0
  123. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +4 -4
  124. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +11 -4
  125. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +10 -3
  126. package/src/mem_pools/attestation_pool/mocks.ts +2 -2
  127. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +79 -34
  128. package/src/mem_pools/tx_pool/memory_tx_pool.ts +16 -1
  129. package/src/mem_pools/tx_pool/tx_pool.ts +12 -0
  130. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +9 -3
  131. package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
  132. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +1 -1
  133. package/src/msg_validators/tx_validator/block_header_validator.ts +1 -1
  134. package/src/msg_validators/tx_validator/data_validator.ts +24 -18
  135. package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
  136. package/src/msg_validators/tx_validator/factory.ts +94 -0
  137. package/src/msg_validators/tx_validator/gas_validator.ts +3 -3
  138. package/src/msg_validators/tx_validator/metadata_validator.ts +50 -14
  139. package/src/msg_validators/tx_validator/phases_validator.ts +6 -2
  140. package/src/msg_validators/tx_validator/tx_proof_validator.ts +1 -1
  141. package/src/services/discv5/discV5_service.ts +14 -12
  142. package/src/services/dummy_service.ts +8 -2
  143. package/src/services/libp2p/libp2p_service.ts +102 -111
  144. package/src/services/peer-manager/metrics.ts +4 -1
  145. package/src/services/peer-manager/peer_manager.ts +18 -1
  146. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +5 -1
  147. package/src/services/reqresp/connection-sampler/connection_sampler.ts +42 -19
  148. package/src/services/reqresp/metrics.ts +4 -1
  149. package/src/services/reqresp/protocols/goodbye.ts +1 -1
  150. package/src/services/reqresp/rate-limiter/rate_limiter.ts +4 -1
  151. package/src/services/reqresp/reqresp.ts +12 -12
  152. package/src/services/service.ts +7 -1
  153. package/src/test-helpers/make-test-p2p-clients.ts +2 -1
  154. package/src/test-helpers/reqresp-nodes.ts +1 -1
  155. package/src/testbench/p2p_client_testbench_worker.ts +10 -4
  156. package/src/testbench/testbench.ts +1 -1
  157. package/src/testbench/worker_client_manager.ts +2 -2
  158. package/src/types/index.ts +1 -0
  159. package/src/versioning.ts +8 -1
package/src/config.ts CHANGED
@@ -99,12 +99,6 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
99
99
  */
100
100
  queryForIp: boolean;
101
101
 
102
- /** How many blocks have to pass after a block is proven before its txs are deleted (zero to delete immediately once proven) */
103
- keepProvenTxsInPoolFor: number;
104
-
105
- /** How many slots to keep attestations for. */
106
- keepAttestationsInPoolFor: number;
107
-
108
102
  /**
109
103
  * The interval of the gossipsub heartbeat to perform maintenance tasks.
110
104
  */
@@ -145,6 +139,11 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
145
139
  */
146
140
  gossipsubMcacheGossip: number;
147
141
 
142
+ /**
143
+ * How long to keep message IDs in the seen cache (ms).
144
+ */
145
+ gossipsubSeenTTL: number;
146
+
148
147
  /**
149
148
  * The 'age' (in # of L2 blocks) of a processed tx after which we heavily penalize a peer for re-sending it.
150
149
  */
@@ -281,17 +280,6 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
281
280
  'If announceUdpAddress or announceTcpAddress are not provided, query for the IP address of the machine. Default is false.',
282
281
  ...booleanConfigHelper(),
283
282
  },
284
- keepProvenTxsInPoolFor: {
285
- env: 'P2P_TX_POOL_KEEP_PROVEN_FOR',
286
- description:
287
- 'How many blocks have to pass after a block is proven before its txs are deleted (zero to delete immediately once proven)',
288
- ...numberConfigHelper(0),
289
- },
290
- keepAttestationsInPoolFor: {
291
- env: 'P2P_ATTESTATION_POOL_KEEP_FOR',
292
- description: 'How many slots to keep attestations for.',
293
- ...numberConfigHelper(96),
294
- },
295
283
  gossipsubInterval: {
296
284
  env: 'P2P_GOSSIPSUB_INTERVAL_MS',
297
285
  description: 'The interval of the gossipsub heartbeat to perform maintenance tasks.',
@@ -329,9 +317,14 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
329
317
  },
330
318
  gossipsubMcacheGossip: {
331
319
  env: 'P2P_GOSSIPSUB_MCACHE_GOSSIP',
332
- description: 'How many message cache windows to include when gossiping with other pears.',
320
+ description: 'How many message cache windows to include when gossiping with other peers.',
333
321
  ...numberConfigHelper(3),
334
322
  },
323
+ gossipsubSeenTTL: {
324
+ env: 'P2P_GOSSIPSUB_SEEN_TTL',
325
+ description: 'How long to keep message IDs in the seen cache.',
326
+ ...numberConfigHelper(20 * 60 * 1000),
327
+ },
335
328
  gossipsubTxTopicWeight: {
336
329
  env: 'P2P_GOSSIPSUB_TX_TOPIC_WEIGHT',
337
330
  description: 'The weight of the tx topic for the gossipsub protocol.',
@@ -1,13 +1,16 @@
1
1
  import type { LogFn } from '@aztec/foundation/log';
2
2
  import { type ChainConfig, emptyChainConfig } from '@aztec/stdlib/config';
3
+ import type { ComponentsVersions } from '@aztec/stdlib/versioning';
3
4
 
4
5
  import { ENR, SignableENR } from '@chainsafe/enr';
5
6
  import type { PeerId } from '@libp2p/interface';
6
- import { multiaddr } from '@multiformats/multiaddr';
7
+ import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';
7
8
 
8
- import { AZTEC_ENR_KEY } from '../types/index.js';
9
+ import { AZTEC_ENR_CLIENT_VERSION_KEY, AZTEC_ENR_KEY } from '../types/index.js';
9
10
  import { convertToMultiaddr, createLibP2PPeerIdFromPrivateKey } from '../util.js';
10
- import { setAztecEnrKey } from '../versioning.js';
11
+ import { setAztecClientVersionEnrKey, setAztecEnrKey } from '../versioning.js';
12
+
13
+ export { ENR };
11
14
 
12
15
  export async function createBootnodeENRandPeerId(
13
16
  privateKey: string,
@@ -29,12 +32,41 @@ export async function createBootnodeENRandPeerId(
29
32
  return { enr, peerId };
30
33
  }
31
34
 
35
+ export function createNodeENR(
36
+ peerId: PeerId,
37
+ multiAddrUdp: Multiaddr | undefined,
38
+ multiAddrTcp: Multiaddr | undefined,
39
+ chainConfig: ChainConfig,
40
+ packageVersion: string,
41
+ ): { enr: SignableENR; versions: ComponentsVersions } {
42
+ // create ENR from PeerId
43
+ const enr = SignableENR.createFromPeerId(peerId);
44
+ // Add aztec identification to ENR
45
+ const versions = setAztecEnrKey(enr, chainConfig);
46
+ // Add aztec client version to ENR
47
+ setAztecClientVersionEnrKey(enr, packageVersion);
48
+
49
+ // set location multiaddr in ENR record
50
+ if (multiAddrUdp) {
51
+ enr.setLocationMultiaddr(multiAddrUdp);
52
+ }
53
+ if (multiAddrTcp) {
54
+ enr.setLocationMultiaddr(multiAddrTcp);
55
+ }
56
+
57
+ return { enr, versions };
58
+ }
59
+
32
60
  export async function printENR(enr: string, log: LogFn) {
33
61
  const decoded = ENR.decodeTxt(enr);
34
62
  log(`PeerID: ${await decoded.peerId()}`);
35
63
  log(`IP: ${decoded.ip}`);
36
64
  log(`UDP: ${decoded.udp}`);
37
65
  log(`TCP: ${decoded.tcp}`);
66
+
38
67
  const aztec = decoded.kvs.get(AZTEC_ENR_KEY);
39
68
  log(`Aztec version: ${aztec?.toString()}`);
69
+
70
+ const aztecClientVersion = decoded.kvs.get(AZTEC_ENR_CLIENT_VERSION_KEY);
71
+ log(`Aztec client version ${aztecClientVersion ? aztecClientVersion!.toString() : 'N/A'}`);
40
72
  }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './bootstrap/bootstrap.js';
2
2
  export * from './client/index.js';
3
+ export * from './enr/index.js';
3
4
  export * from './config.js';
4
5
  export * from './mem_pools/attestation_pool/index.js';
5
6
  export * from './mem_pools/tx_pool/index.js';
@@ -69,4 +69,7 @@ export interface AttestationPool {
69
69
  * @return BlockAttestations
70
70
  */
71
71
  getAttestationsForSlotAndProposal(slot: bigint, proposalId: string): Promise<BlockAttestation[]>;
72
+
73
+ /** Returns whether the pool is empty. */
74
+ isEmpty(): Promise<boolean>;
72
75
  }
@@ -118,13 +118,13 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
118
118
  await ap.addAttestations(attestations);
119
119
 
120
120
  for (const attestation of attestations) {
121
- const slot = attestation.payload.header.globalVariables.slotNumber;
121
+ const slot = attestation.payload.header.slotNumber;
122
122
  const archive = attestation.archive.toString();
123
123
 
124
124
  const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(slot.toBigInt(), archive);
125
125
  expect(retreivedAttestations.length).toBe(1);
126
126
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
127
- expect(retreivedAttestations[0].payload.header.globalVariables.slotNumber).toEqual(slot);
127
+ expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
128
128
  }
129
129
  });
130
130
 
@@ -136,13 +136,13 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
136
136
  await ap.addAttestations(attestations);
137
137
 
138
138
  for (const attestation of attestations) {
139
- const slot = attestation.payload.header.globalVariables.slotNumber;
139
+ const slot = attestation.payload.header.slotNumber;
140
140
  const proposalId = attestation.archive.toString();
141
141
 
142
142
  const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(slot.toBigInt(), proposalId);
143
143
  expect(retreivedAttestations.length).toBe(1);
144
144
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
145
- expect(retreivedAttestations[0].payload.header.globalVariables.slotNumber).toEqual(slot);
145
+ expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
146
146
  }
147
147
  });
148
148
 
@@ -27,14 +27,21 @@ export class KvAttestationPool implements AttestationPool {
27
27
  this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL);
28
28
  }
29
29
 
30
+ public async isEmpty(): Promise<boolean> {
31
+ for await (const _ of this.attestations.entriesAsync()) {
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+
30
37
  private getProposalKey(slot: number | bigint | Fr | string, proposalId: Fr | string | Buffer): string {
31
38
  const slotStr = typeof slot === 'string' ? slot : new Fr(slot).toString();
32
39
  const proposalIdStr =
33
40
  typeof proposalId === 'string'
34
41
  ? proposalId
35
42
  : Buffer.isBuffer(proposalId)
36
- ? Fr.fromBuffer(proposalId).toString()
37
- : proposalId.toString();
43
+ ? Fr.fromBuffer(proposalId).toString()
44
+ : proposalId.toString();
38
45
 
39
46
  return `${slotStr}-${proposalIdStr}`;
40
47
  }
@@ -46,7 +53,7 @@ export class KvAttestationPool implements AttestationPool {
46
53
  public async addAttestations(attestations: BlockAttestation[]): Promise<void> {
47
54
  await this.store.transactionAsync(async () => {
48
55
  for (const attestation of attestations) {
49
- const slotNumber = attestation.payload.header.globalVariables.slotNumber;
56
+ const slotNumber = attestation.payload.header.slotNumber;
50
57
  const proposalId = attestation.archive;
51
58
  const address = attestation.getSender().toString();
52
59
 
@@ -158,7 +165,7 @@ export class KvAttestationPool implements AttestationPool {
158
165
  public async deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
159
166
  await this.store.transactionAsync(async () => {
160
167
  for (const attestation of attestations) {
161
- const slotNumber = attestation.payload.header.globalVariables.slotNumber;
168
+ const slotNumber = attestation.payload.header.slotNumber;
162
169
  const proposalId = attestation.archive;
163
170
  const address = attestation.getSender().toString();
164
171
 
@@ -10,11 +10,18 @@ export class InMemoryAttestationPool implements AttestationPool {
10
10
 
11
11
  private attestations: Map</*slot=*/ bigint, Map</*proposalId*/ string, Map</*address=*/ string, BlockAttestation>>>;
12
12
 
13
- constructor(telemetry: TelemetryClient = getTelemetryClient(), private log = createLogger('p2p:attestation_pool')) {
13
+ constructor(
14
+ telemetry: TelemetryClient = getTelemetryClient(),
15
+ private log = createLogger('p2p:attestation_pool'),
16
+ ) {
14
17
  this.attestations = new Map();
15
18
  this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL);
16
19
  }
17
20
 
21
+ public isEmpty(): Promise<boolean> {
22
+ return Promise.resolve(this.attestations.size === 0);
23
+ }
24
+
18
25
  public getAttestationsForSlot(slot: bigint): Promise<BlockAttestation[]> {
19
26
  return Promise.resolve(
20
27
  Array.from(this.attestations.get(slot)?.values() ?? []).flatMap(proposalAttestationMap =>
@@ -37,7 +44,7 @@ export class InMemoryAttestationPool implements AttestationPool {
37
44
  public addAttestations(attestations: BlockAttestation[]): Promise<void> {
38
45
  for (const attestation of attestations) {
39
46
  // Perf: order and group by slot before insertion
40
- const slotNumber = attestation.payload.header.globalVariables.slotNumber;
47
+ const slotNumber = attestation.payload.header.slotNumber;
41
48
 
42
49
  const proposalId = attestation.archive.toString();
43
50
  const address = attestation.getSender();
@@ -120,7 +127,7 @@ export class InMemoryAttestationPool implements AttestationPool {
120
127
 
121
128
  public deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
122
129
  for (const attestation of attestations) {
123
- const slotNumber = attestation.payload.header.globalVariables.slotNumber;
130
+ const slotNumber = attestation.payload.header.slotNumber;
124
131
  const slotAttestationMap = this.attestations.get(slotNumber.toBigInt());
125
132
  if (slotAttestationMap) {
126
133
  const proposalId = attestation.archive.toString();
@@ -35,10 +35,10 @@ export const mockAttestation = (
35
35
  ): BlockAttestation => {
36
36
  // Use arbitrary numbers for all other than slot
37
37
  const header = makeHeader(1, 2, slot);
38
- const payload = new ConsensusPayload(header, archive, txs);
38
+ const payload = new ConsensusPayload(header.toPropose(), archive, header.state, txs);
39
39
 
40
40
  const hash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
41
41
  const signature = signer.sign(hash);
42
42
 
43
- return new BlockAttestation(payload, signature);
43
+ return new BlockAttestation(header.globalVariables.blockNumber, payload, signature);
44
44
  };
@@ -44,9 +44,15 @@ export class AztecKVTxPool implements TxPool {
44
44
  /** The cumulative tx size in bytes that the pending txs in the pool take up. */
45
45
  #pendingTxSize: AztecAsyncSingleton<number>;
46
46
 
47
+ /** Count of total pending txs. */
48
+ #pendingTxCount: AztecAsyncSingleton<number>;
49
+
47
50
  /** In-memory mapping of pending tx hashes to the hydrated pending tx in the pool. */
48
51
  #pendingTxs: Map<string, Tx>;
49
52
 
53
+ /** In-memory set of txs that should not be evicted from the pool. */
54
+ #nonEvictableTxs: Set<string>;
55
+
50
56
  /** KV store for archived txs. */
51
57
  #archive: AztecAsyncKVStore;
52
58
 
@@ -91,8 +97,10 @@ export class AztecKVTxPool implements TxPool {
91
97
  this.#pendingTxHashToSize = store.openMap('pendingTxHashToSize');
92
98
  this.#pendingTxHashToHeaderHash = store.openMap('pendingTxHashToHeaderHash');
93
99
  this.#pendingTxSize = store.openSingleton('pendingTxSize');
100
+ this.#pendingTxCount = store.openSingleton('pendingTxCount');
94
101
  this.#maxTxPoolSize = config.maxTxPoolSize;
95
102
  this.#pendingTxs = new Map<string, Tx>();
103
+ this.#nonEvictableTxs = new Set<string>();
96
104
 
97
105
  this.#archivedTxs = archive.openMap('archivedTxs');
98
106
  this.#archivedTxIndices = archive.openMap('archivedTxIndices');
@@ -105,7 +113,14 @@ export class AztecKVTxPool implements TxPool {
105
113
  this.#metrics = new PoolInstrumentation(telemetry, PoolName.TX_POOL, () => store.estimateSize());
106
114
  }
107
115
 
108
- public markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
116
+ public async isEmpty(): Promise<boolean> {
117
+ for await (const _ of this.#txs.entriesAsync()) {
118
+ return false;
119
+ }
120
+ return true;
121
+ }
122
+
123
+ public async markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
109
124
  if (txHashes.length === 0) {
110
125
  return Promise.resolve();
111
126
  }
@@ -113,7 +128,8 @@ export class AztecKVTxPool implements TxPool {
113
128
  let deletedPending = 0;
114
129
  const minedNullifiers = new Set<string>();
115
130
  const minedFeePayers = new Set<string>();
116
- return this.#store.transactionAsync(async () => {
131
+
132
+ await this.#store.transactionAsync(async () => {
117
133
  let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
118
134
  for (const hash of txHashes) {
119
135
  const key = hash.toString();
@@ -132,6 +148,7 @@ export class AztecKVTxPool implements TxPool {
132
148
  }
133
149
  this.#metrics.recordAddedObjects(txHashes.length, 'mined');
134
150
  await this.#pendingTxSize.set(pendingTxSize);
151
+ await this.increasePendingTxCount(-deletedPending);
135
152
 
136
153
  const numTxsEvicted = await this.evictInvalidTxsAfterMining(
137
154
  txHashes,
@@ -141,43 +158,44 @@ export class AztecKVTxPool implements TxPool {
141
158
  );
142
159
  this.#metrics.recordRemovedObjects(deletedPending + numTxsEvicted, 'pending');
143
160
  });
161
+ // We update this after the transaction above. This ensures that the non-evictable transactions are not evicted
162
+ // until any that have been mined are marked as such.
163
+ // The non-evictable set is not considered when evicting transactions that are invalid after a block is mined.
164
+ this.#nonEvictableTxs.clear();
144
165
  }
145
166
 
146
- public markMinedAsPending(txHashes: TxHash[]): Promise<void> {
167
+ public async markMinedAsPending(txHashes: TxHash[]): Promise<void> {
147
168
  if (txHashes.length === 0) {
148
169
  return Promise.resolve();
149
170
  }
150
171
 
151
172
  let markedAsPending = 0;
152
- return this.#store
153
- .transactionAsync(async () => {
154
- let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
155
- for (const hash of txHashes) {
156
- const key = hash.toString();
157
- await this.#minedTxHashToBlock.delete(key);
173
+ await this.#store.transactionAsync(async () => {
174
+ let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
175
+ for (const hash of txHashes) {
176
+ const key = hash.toString();
177
+ await this.#minedTxHashToBlock.delete(key);
158
178
 
159
- // Rehydrate the tx in the in-memory pending txs mapping
160
- const tx = await this.getPendingTxByHash(hash);
161
- if (tx) {
162
- await this.addPendingTxIndices(tx, key);
163
- pendingTxSize += tx.getSize();
164
- markedAsPending++;
165
- }
179
+ // Rehydrate the tx in the in-memory pending txs mapping
180
+ const tx = await this.getPendingTxByHash(hash);
181
+ if (tx) {
182
+ await this.addPendingTxIndices(tx, key);
183
+ pendingTxSize += tx.getSize();
184
+ markedAsPending++;
166
185
  }
186
+ }
187
+
188
+ await this.#pendingTxSize.set(pendingTxSize);
189
+ });
190
+
191
+ const numInvalidTxsEvicted = await this.evictInvalidTxsAfterReorg(txHashes);
192
+ const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(txHashes);
167
193
 
168
- await this.#pendingTxSize.set(pendingTxSize);
169
- })
170
- .then(async () => {
171
- const numInvalidTxsEvicted = await this.evictInvalidTxsAfterReorg(txHashes);
172
- const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(txHashes);
173
-
174
- this.#metrics.recordAddedObjects(markedAsPending - numNewTxsEvicted, 'pending');
175
- this.#metrics.recordRemovedObjects(
176
- numInvalidTxsEvicted + numLowPriorityTxsEvicted - numNewTxsEvicted,
177
- 'pending',
178
- );
179
- this.#metrics.recordRemovedObjects(markedAsPending, 'mined');
180
- });
194
+ await this.increasePendingTxCount(markedAsPending);
195
+
196
+ this.#metrics.recordAddedObjects(markedAsPending - numNewTxsEvicted, 'pending');
197
+ this.#metrics.recordRemovedObjects(numInvalidTxsEvicted + numLowPriorityTxsEvicted - numNewTxsEvicted, 'pending');
198
+ this.#metrics.recordRemovedObjects(markedAsPending, 'mined');
181
199
  }
182
200
 
183
201
  public async getPendingTxHashes(): Promise<TxHash[]> {
@@ -190,6 +208,10 @@ export class AztecKVTxPool implements TxPool {
190
208
  return vals.map(([txHash, blockNumber]) => [TxHash.fromString(txHash), blockNumber]);
191
209
  }
192
210
 
211
+ public async getPendingTxCount(): Promise<number> {
212
+ return (await this.#pendingTxCount.getAsync()) ?? 0;
213
+ }
214
+
193
215
  public async getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined> {
194
216
  const key = txHash.toString();
195
217
  const [isMined, isKnown] = await Promise.all([this.#minedTxHashToBlock.hasAsync(key), this.#txs.hasAsync(key)]);
@@ -259,21 +281,26 @@ export class AztecKVTxPool implements TxPool {
259
281
  txs.map(async tx => ({ txHash: await tx.getTxHash(), txStats: await tx.getStats() })),
260
282
  );
261
283
  await this.#store.transactionAsync(async () => {
262
- let pendingCount = 0;
284
+ let addedCount = 0;
263
285
  let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
264
286
  await Promise.all(
265
287
  txs.map(async (tx, i) => {
266
288
  const { txHash, txStats } = hashesAndStats[i];
289
+ const key = txHash.toString();
290
+ if (await this.#txs.hasAsync(key)) {
291
+ this.#log.debug(`Tx ${txHash.toString()} already exists in the pool`);
292
+ return;
293
+ }
294
+
267
295
  this.#log.verbose(`Adding tx ${txHash.toString()} to pool`, {
268
296
  eventName: 'tx-added-to-pool',
269
297
  ...txStats,
270
298
  } satisfies TxAddedToPoolStats);
271
299
 
272
- const key = txHash.toString();
273
300
  await this.#txs.set(key, tx.toBuffer());
274
301
 
275
302
  if (!(await this.#minedTxHashToBlock.hasAsync(key))) {
276
- pendingCount++;
303
+ addedCount++;
277
304
  pendingTxSize += tx.getSize();
278
305
  await this.addPendingTxIndices(tx, key);
279
306
  this.#metrics.recordSize(tx);
@@ -281,13 +308,13 @@ export class AztecKVTxPool implements TxPool {
281
308
  }),
282
309
  );
283
310
 
311
+ await this.increasePendingTxCount(addedCount);
284
312
  await this.#pendingTxSize.set(pendingTxSize);
285
-
286
313
  const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(
287
314
  hashesAndStats.map(({ txHash }) => txHash),
288
315
  );
289
316
 
290
- this.#metrics.recordAddedObjects(pendingCount - numNewTxsEvicted, 'pending');
317
+ this.#metrics.recordAddedObjects(addedCount - numNewTxsEvicted, 'pending');
291
318
  this.#metrics.recordRemovedObjects(numLowPriorityTxsEvicted - numNewTxsEvicted, 'pending');
292
319
  });
293
320
  }
@@ -328,6 +355,8 @@ export class AztecKVTxPool implements TxPool {
328
355
  }
329
356
 
330
357
  await this.#pendingTxSize.set(pendingTxSize);
358
+ await this.increasePendingTxCount(-pendingDeleted);
359
+
331
360
  this.#metrics.recordRemovedObjects(pendingDeleted, 'pending');
332
361
  this.#metrics.recordRemovedObjects(minedDeleted, 'mined');
333
362
  });
@@ -362,6 +391,11 @@ export class AztecKVTxPool implements TxPool {
362
391
  return Promise.resolve();
363
392
  }
364
393
 
394
+ public markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void> {
395
+ txHashes.forEach(txHash => this.#nonEvictableTxs.add(txHash.toString()));
396
+ return Promise.resolve();
397
+ }
398
+
365
399
  /**
366
400
  * Creates a GasTxValidator instance.
367
401
  * @param db - DB for the validator to use
@@ -459,6 +493,9 @@ export class AztecKVTxPool implements TxPool {
459
493
  let pendingTxsSize = (await this.#pendingTxSize.getAsync()) ?? 0;
460
494
  if (pendingTxsSize > this.#maxTxPoolSize) {
461
495
  for await (const txHash of this.#pendingTxPriorityToHash.valuesAsync()) {
496
+ if (this.#nonEvictableTxs.has(txHash.toString())) {
497
+ continue;
498
+ }
462
499
  this.#log.verbose(`Evicting tx ${txHash} from pool due to low priority to satisfy max tx size limit`);
463
500
  txsToEvict.push(TxHash.fromString(txHash));
464
501
 
@@ -606,4 +643,12 @@ export class AztecKVTxPool implements TxPool {
606
643
  await this.#pendingTxHashToHeaderHash.delete(txHash);
607
644
  this.#pendingTxs.delete(txHash);
608
645
  }
646
+
647
+ private async increasePendingTxCount(count: number): Promise<void> {
648
+ const pendingTxCount = (await this.#pendingTxCount.getAsync()) ?? 0;
649
+ this.#log.debug(
650
+ `Increasing pending tx count: current ${pendingTxCount} + count ${count} = ${pendingTxCount + count}`,
651
+ );
652
+ await this.#pendingTxCount.set(pendingTxCount + count);
653
+ }
609
654
  }
@@ -24,13 +24,20 @@ export class InMemoryTxPool implements TxPool {
24
24
  * Class constructor for in-memory TxPool. Initiates our transaction pool as a JS Map.
25
25
  * @param log - A logger.
26
26
  */
27
- constructor(telemetry: TelemetryClient = getTelemetryClient(), private log = createLogger('p2p:tx_pool')) {
27
+ constructor(
28
+ telemetry: TelemetryClient = getTelemetryClient(),
29
+ private log = createLogger('p2p:tx_pool'),
30
+ ) {
28
31
  this.txs = new Map<bigint, Tx>();
29
32
  this.minedTxs = new Map();
30
33
  this.pendingTxs = new Set();
31
34
  this.metrics = new PoolInstrumentation(telemetry, PoolName.TX_POOL);
32
35
  }
33
36
 
37
+ public isEmpty(): Promise<boolean> {
38
+ return Promise.resolve(this.txs.size === 0);
39
+ }
40
+
34
41
  public markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
35
42
  const keys = txHashes.map(x => x.toBigInt());
36
43
  for (const key of keys) {
@@ -82,6 +89,10 @@ export class InMemoryTxPool implements TxPool {
82
89
  );
83
90
  }
84
91
 
92
+ public getPendingTxCount(): Promise<number> {
93
+ return Promise.resolve(this.pendingTxs.size);
94
+ }
95
+
85
96
  public getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined> {
86
97
  const key = txHash.toBigInt();
87
98
  if (this.pendingTxs.has(key)) {
@@ -182,4 +193,8 @@ export class InMemoryTxPool implements TxPool {
182
193
  setMaxTxPoolSize(_maxSizeBytes: number | undefined): Promise<void> {
183
194
  return Promise.resolve();
184
195
  }
196
+
197
+ markTxsAsNonEvictable(_: TxHash[]): Promise<void> {
198
+ return Promise.resolve();
199
+ }
185
200
  }
@@ -75,6 +75,9 @@ export interface TxPool {
75
75
  */
76
76
  getPendingTxHashes(): Promise<TxHash[]>;
77
77
 
78
+ /** Returns the number of pending txs in the pool. */
79
+ getPendingTxCount(): Promise<number>;
80
+
78
81
  /**
79
82
  * Gets the hashes of mined transactions currently in the tx pool.
80
83
  * @returns An array of mined transaction hashes found in the tx pool.
@@ -93,4 +96,13 @@ export interface TxPool {
93
96
  * @param maxSizeBytes - The maximum size in bytes of the mempool. Set to undefined to disable it
94
97
  */
95
98
  setMaxTxPoolSize(maxSizeBytes: number | undefined): Promise<void>;
99
+
100
+ /** Returns whether the pool is empty. */
101
+ isEmpty(): Promise<boolean>;
102
+
103
+ /**
104
+ * Marks transactions as non-evictible in the pool.
105
+ * @param txHashes - Hashes of the transactions to mark as non-evictible.
106
+ */
107
+ markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void>;
96
108
  }
@@ -24,6 +24,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
24
24
  expect(await poolTx!.getTxHash()).toEqual(await tx1.getTxHash());
25
25
  await expect(pool.getTxStatus(await tx1.getTxHash())).resolves.toEqual('pending');
26
26
  await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx1.getTxHash()]);
27
+ await expect(pool.getPendingTxCount()).resolves.toEqual(1);
27
28
  });
28
29
 
29
30
  it('Removes txs from the pool', async () => {
@@ -34,6 +35,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
34
35
 
35
36
  await expect(pool.getTxByHash(await tx1.getTxHash())).resolves.toBeFalsy();
36
37
  await expect(pool.getTxStatus(await tx1.getTxHash())).resolves.toBeUndefined();
38
+ await expect(pool.getPendingTxCount()).resolves.toEqual(0);
37
39
  });
38
40
 
39
41
  it('Marks txs as mined', async () => {
@@ -47,6 +49,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
47
49
  await expect(pool.getTxStatus(await tx1.getTxHash())).resolves.toEqual('mined');
48
50
  await expect(pool.getMinedTxHashes()).resolves.toEqual([[await tx1.getTxHash(), 1]]);
49
51
  await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx2.getTxHash()]);
52
+ await expect(pool.getPendingTxCount()).resolves.toEqual(1);
50
53
  });
51
54
 
52
55
  it('Marks txs as pending after being mined', async () => {
@@ -61,6 +64,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
61
64
  const pending = await pool.getPendingTxHashes();
62
65
  expect(pending).toHaveLength(2);
63
66
  expect(pending).toEqual(expect.arrayContaining([await tx1.getTxHash(), await tx2.getTxHash()]));
67
+ await expect(pool.getPendingTxCount()).resolves.toEqual(2);
64
68
  });
65
69
 
66
70
  it('Only marks txs as pending if they are known', async () => {
@@ -82,6 +86,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
82
86
  await pool.markMinedAsPending([await tx1.getTxHash(), someTxHashThatThisPeerDidNotSee]);
83
87
  await expect(pool.getMinedTxHashes()).resolves.toEqual([]);
84
88
  await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx1.getTxHash()]); // tx2 is not in the pool
89
+ await expect(pool.getPendingTxCount()).resolves.toEqual(1);
85
90
  });
86
91
 
87
92
  it('Returns all transactions in the pool', async () => {
@@ -94,6 +99,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
94
99
  const poolTxs = await pool.getAllTxs();
95
100
  expect(poolTxs).toHaveLength(3);
96
101
  expect(poolTxs).toEqual(expect.arrayContaining([tx1, tx2, tx3]));
102
+ await expect(pool.getPendingTxCount()).resolves.toEqual(3);
97
103
  });
98
104
 
99
105
  it('Returns all txHashes in the pool', async () => {
@@ -104,10 +110,10 @@ export function describeTxPool(getTxPool: () => TxPool) {
104
110
  await pool.addTxs([tx1, tx2, tx3]);
105
111
 
106
112
  const poolTxHashes = await pool.getAllTxHashes();
113
+ const expectedHashes = await Promise.all([tx1, tx2, tx3].map(tx => tx.getTxHash()));
107
114
  expect(poolTxHashes).toHaveLength(3);
108
- expect(poolTxHashes).toEqual(
109
- expect.arrayContaining([await tx1.getTxHash(), await tx2.getTxHash(), await tx3.getTxHash()]),
110
- );
115
+ expect(poolTxHashes).toEqual(expect.arrayContaining(expectedHashes));
116
+ await expect(pool.getPendingTxCount()).resolves.toEqual(3);
111
117
  });
112
118
 
113
119
  it('Returns txs by their hash', async () => {
@@ -11,7 +11,7 @@ export class AttestationValidator implements P2PValidator<BlockAttestation> {
11
11
  async validate(message: BlockAttestation): Promise<PeerErrorSeverity | undefined> {
12
12
  const { currentSlot, nextSlot } = await this.epochCache.getProposerInCurrentOrNextSlot();
13
13
 
14
- const slotNumberBigInt = message.payload.header.globalVariables.slotNumber.toBigInt();
14
+ const slotNumberBigInt = message.payload.header.slotNumber.toBigInt();
15
15
  if (slotNumberBigInt !== currentSlot && slotNumberBigInt !== nextSlot) {
16
16
  return PeerErrorSeverity.HighToleranceError;
17
17
  }
@@ -16,7 +16,7 @@ export class BlockProposalValidator implements P2PValidator<BlockProposal> {
16
16
  await this.epochCache.getProposerInCurrentOrNextSlot();
17
17
 
18
18
  // Check that the attestation is for the current or next slot
19
- const slotNumberBigInt = block.payload.header.globalVariables.slotNumber.toBigInt();
19
+ const slotNumberBigInt = block.payload.header.slotNumber.toBigInt();
20
20
  if (slotNumberBigInt !== currentSlot && slotNumberBigInt !== nextSlot) {
21
21
  this.logger.debug(
22
22
  `Penalizing peer for invalid slot number ${slotNumberBigInt}, current slot: ${currentSlot}, next slot: ${nextSlot}`,
@@ -17,7 +17,7 @@ export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
17
17
  async validateTx(tx: T): Promise<TxValidationResult> {
18
18
  const [index] = await this.#archiveSource.getArchiveIndices([await tx.data.constants.historicalHeader.hash()]);
19
19
  if (index === undefined) {
20
- this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for referencing an unknown block header`);
20
+ this.#log.verbose(`Rejecting tx ${await Tx.getHash(tx)} for referencing an unknown block header`);
21
21
  return { result: 'invalid', reason: [TX_ERROR_BLOCK_HEADER] };
22
22
  }
23
23
  return { result: 'valid' };