@aztec/p2p 3.0.0-devnet.2 → 3.0.0-devnet.2-patch.1

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 (259) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +1 -1
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/client/factory.d.ts +1 -1
  4. package/dest/client/index.d.ts +1 -1
  5. package/dest/client/interface.d.ts +4 -2
  6. package/dest/client/interface.d.ts.map +1 -1
  7. package/dest/client/p2p_client.d.ts +8 -26
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +31 -24
  10. package/dest/config.d.ts +60 -54
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +12 -2
  13. package/dest/enr/generate-enr.d.ts +1 -1
  14. package/dest/enr/index.d.ts +1 -1
  15. package/dest/errors/attestation-pool.error.d.ts +7 -0
  16. package/dest/errors/attestation-pool.error.d.ts.map +1 -0
  17. package/dest/errors/attestation-pool.error.js +12 -0
  18. package/dest/errors/reqresp.error.d.ts +1 -1
  19. package/dest/errors/reqresp.error.d.ts.map +1 -1
  20. package/dest/index.d.ts +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +43 -6
  22. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  24. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  25. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +67 -34
  26. package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
  27. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +15 -6
  28. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  29. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +57 -18
  30. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +13 -6
  31. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  32. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +51 -7
  33. package/dest/mem_pools/attestation_pool/mocks.d.ts +226 -5
  34. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  35. package/dest/mem_pools/attestation_pool/mocks.js +2 -2
  36. package/dest/mem_pools/index.d.ts +1 -1
  37. package/dest/mem_pools/instrumentation.d.ts +3 -1
  38. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  39. package/dest/mem_pools/instrumentation.js +11 -2
  40. package/dest/mem_pools/interface.d.ts +1 -1
  41. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +5 -38
  42. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +9 -3
  44. package/dest/mem_pools/tx_pool/index.d.ts +1 -1
  45. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +5 -3
  46. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  47. package/dest/mem_pools/tx_pool/memory_tx_pool.js +7 -0
  48. package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
  49. package/dest/mem_pools/tx_pool/tx_pool.d.ts +10 -3
  50. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  51. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
  52. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  53. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +5 -4
  54. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -3
  55. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  56. package/dest/msg_validators/attestation_validator/attestation_validator.js +12 -12
  57. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +20 -0
  58. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
  59. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +67 -0
  60. package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
  61. package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
  62. package/dest/msg_validators/attestation_validator/index.js +1 -0
  63. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +1 -1
  64. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  65. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +22 -10
  66. package/dest/msg_validators/block_proposal_validator/index.d.ts +1 -1
  67. package/dest/msg_validators/index.d.ts +1 -1
  68. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +1 -1
  69. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -1
  70. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  71. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  72. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +1 -1
  73. package/dest/msg_validators/tx_validator/archive_cache.d.ts +2 -2
  74. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
  75. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +2 -2
  76. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  77. package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
  78. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  79. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +1 -1
  80. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  81. package/dest/msg_validators/tx_validator/factory.d.ts +4 -3
  82. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  83. package/dest/msg_validators/tx_validator/factory.js +1 -1
  84. package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
  85. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  86. package/dest/msg_validators/tx_validator/index.d.ts +1 -1
  87. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +2 -2
  88. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  89. package/dest/msg_validators/tx_validator/phases_validator.d.ts +1 -1
  90. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  91. package/dest/msg_validators/tx_validator/phases_validator.js +3 -1
  92. package/dest/msg_validators/tx_validator/test_utils.d.ts +2 -2
  93. package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -1
  94. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +3 -2
  95. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  96. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +1 -1
  97. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
  98. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +1 -1
  99. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  100. package/dest/services/data_store.d.ts +1 -1
  101. package/dest/services/data_store.d.ts.map +1 -1
  102. package/dest/services/discv5/discV5_service.d.ts +1 -1
  103. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  104. package/dest/services/dummy_service.d.ts +1 -1
  105. package/dest/services/dummy_service.d.ts.map +1 -1
  106. package/dest/services/encoding.d.ts +25 -4
  107. package/dest/services/encoding.d.ts.map +1 -1
  108. package/dest/services/encoding.js +74 -6
  109. package/dest/services/gossipsub/scoring.d.ts +1 -1
  110. package/dest/services/index.d.ts +1 -1
  111. package/dest/services/libp2p/instrumentation.d.ts +3 -1
  112. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  113. package/dest/services/libp2p/instrumentation.js +9 -2
  114. package/dest/services/libp2p/libp2p_service.d.ts +25 -74
  115. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  116. package/dest/services/libp2p/libp2p_service.js +308 -84
  117. package/dest/services/peer-manager/interface.d.ts +1 -1
  118. package/dest/services/peer-manager/metrics.d.ts +3 -1
  119. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  120. package/dest/services/peer-manager/metrics.js +11 -0
  121. package/dest/services/peer-manager/peer_manager.d.ts +1 -32
  122. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  123. package/dest/services/peer-manager/peer_manager.js +4 -2
  124. package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
  125. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  126. package/dest/services/peer-manager/peer_scoring.js +40 -2
  127. package/dest/services/reqresp/config.d.ts +1 -1
  128. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +1 -1
  129. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  130. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +1 -4
  131. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  132. package/dest/services/reqresp/index.d.ts +1 -1
  133. package/dest/services/reqresp/interface.d.ts +2 -2
  134. package/dest/services/reqresp/interface.d.ts.map +1 -1
  135. package/dest/services/reqresp/interface.js +1 -1
  136. package/dest/services/reqresp/metrics.d.ts +1 -1
  137. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  138. package/dest/services/reqresp/protocols/auth.d.ts +2 -2
  139. package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
  140. package/dest/services/reqresp/protocols/auth.js +2 -2
  141. package/dest/services/reqresp/protocols/block.d.ts +1 -1
  142. package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
  143. package/dest/services/reqresp/protocols/block.js +3 -2
  144. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +1 -1
  145. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  146. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
  147. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +4 -6
  148. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  149. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +1 -1
  150. package/dest/services/reqresp/protocols/block_txs/index.d.ts +1 -1
  151. package/dest/services/reqresp/protocols/goodbye.d.ts +1 -1
  152. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  153. package/dest/services/reqresp/protocols/index.d.ts +1 -1
  154. package/dest/services/reqresp/protocols/ping.d.ts +1 -1
  155. package/dest/services/reqresp/protocols/status.d.ts +6 -5
  156. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  157. package/dest/services/reqresp/protocols/status.js +4 -3
  158. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  159. package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
  160. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +2 -2
  161. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  162. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  163. package/dest/services/reqresp/reqresp.d.ts +1 -41
  164. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  165. package/dest/services/reqresp/reqresp.js +2 -2
  166. package/dest/services/reqresp/status.d.ts +2 -2
  167. package/dest/services/reqresp/status.d.ts.map +1 -1
  168. package/dest/services/service.d.ts +1 -1
  169. package/dest/services/tx_collection/config.d.ts +1 -1
  170. package/dest/services/tx_collection/fast_tx_collection.d.ts +4 -9
  171. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  172. package/dest/services/tx_collection/index.d.ts +1 -1
  173. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  174. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  175. package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -7
  176. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  177. package/dest/services/tx_collection/slow_tx_collection.js +2 -1
  178. package/dest/services/tx_collection/tx_collection.d.ts +11 -11
  179. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  180. package/dest/services/tx_collection/tx_collection_sink.d.ts +3 -3
  181. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  182. package/dest/services/tx_collection/tx_source.d.ts +1 -1
  183. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  184. package/dest/services/tx_provider.d.ts +5 -4
  185. package/dest/services/tx_provider.d.ts.map +1 -1
  186. package/dest/services/tx_provider_instrumentation.d.ts +1 -1
  187. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
  188. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
  189. package/dest/test-helpers/get-ports.d.ts +1 -1
  190. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  191. package/dest/test-helpers/index.d.ts +1 -1
  192. package/dest/test-helpers/make-enrs.d.ts +1 -1
  193. package/dest/test-helpers/make-test-p2p-clients.d.ts +2 -2
  194. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  195. package/dest/test-helpers/mock-pubsub.d.ts +4 -4
  196. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  197. package/dest/test-helpers/mock-tx-helpers.d.ts +2 -2
  198. package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -1
  199. package/dest/test-helpers/mock-tx-helpers.js +1 -1
  200. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  201. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  202. package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
  203. package/dest/testbench/p2p_client_testbench_worker.js +14 -8
  204. package/dest/testbench/parse_log_file.d.ts +1 -1
  205. package/dest/testbench/testbench.d.ts +1 -1
  206. package/dest/testbench/testbench.js +2 -2
  207. package/dest/testbench/worker_client_manager.d.ts +1 -1
  208. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  209. package/dest/types/index.d.ts +1 -1
  210. package/dest/util.d.ts +2 -1
  211. package/dest/util.d.ts.map +1 -1
  212. package/dest/util.js +11 -2
  213. package/dest/versioning.d.ts +1 -1
  214. package/package.json +19 -18
  215. package/src/client/interface.ts +4 -1
  216. package/src/client/p2p_client.ts +51 -43
  217. package/src/config.ts +19 -2
  218. package/src/errors/attestation-pool.error.ts +13 -0
  219. package/src/mem_pools/attestation_pool/attestation_pool.ts +46 -5
  220. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +84 -34
  221. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +87 -24
  222. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +77 -15
  223. package/src/mem_pools/attestation_pool/mocks.ts +3 -3
  224. package/src/mem_pools/instrumentation.ts +13 -0
  225. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +15 -9
  226. package/src/mem_pools/tx_pool/memory_tx_pool.ts +13 -6
  227. package/src/mem_pools/tx_pool/tx_pool.ts +10 -2
  228. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +5 -4
  229. package/src/msg_validators/attestation_validator/attestation_validator.ts +14 -16
  230. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +91 -0
  231. package/src/msg_validators/attestation_validator/index.ts +1 -0
  232. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +26 -10
  233. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  234. package/src/msg_validators/tx_validator/block_header_validator.ts +1 -1
  235. package/src/msg_validators/tx_validator/factory.ts +3 -2
  236. package/src/msg_validators/tx_validator/metadata_validator.ts +1 -1
  237. package/src/msg_validators/tx_validator/phases_validator.ts +3 -1
  238. package/src/msg_validators/tx_validator/test_utils.ts +1 -1
  239. package/src/msg_validators/tx_validator/timestamp_validator.ts +2 -1
  240. package/src/services/encoding.ts +81 -6
  241. package/src/services/libp2p/instrumentation.ts +10 -1
  242. package/src/services/libp2p/libp2p_service.ts +334 -91
  243. package/src/services/peer-manager/metrics.ts +10 -0
  244. package/src/services/peer-manager/peer_manager.ts +4 -2
  245. package/src/services/peer-manager/peer_scoring.ts +46 -3
  246. package/src/services/reqresp/interface.ts +1 -1
  247. package/src/services/reqresp/protocols/auth.ts +2 -2
  248. package/src/services/reqresp/protocols/block.ts +3 -2
  249. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +1 -1
  250. package/src/services/reqresp/protocols/status.ts +9 -8
  251. package/src/services/reqresp/reqresp.ts +2 -2
  252. package/src/services/tx_collection/fast_tx_collection.ts +3 -2
  253. package/src/services/tx_collection/slow_tx_collection.ts +7 -6
  254. package/src/services/tx_collection/tx_collection.ts +10 -9
  255. package/src/services/tx_provider.ts +4 -3
  256. package/src/test-helpers/mock-tx-helpers.ts +1 -1
  257. package/src/testbench/p2p_client_testbench_worker.ts +11 -5
  258. package/src/testbench/testbench.ts +2 -2
  259. package/src/util.ts +12 -2
@@ -1,14 +1,20 @@
1
+ import type { SlotNumber } from '@aztec/foundation/branded-types';
1
2
  import { createLogger } from '@aztec/foundation/log';
2
3
  import type { BlockAttestation, BlockProposal } from '@aztec/stdlib/p2p';
3
4
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
4
5
 
5
6
  import { PoolInstrumentation, PoolName, type PoolStatsCallback } from '../instrumentation.js';
6
7
  import type { AttestationPool } from './attestation_pool.js';
8
+ import { ATTESTATION_CAP_BUFFER, MAX_PROPOSALS_PER_SLOT } from './kv_attestation_pool.js';
7
9
 
8
10
  export class InMemoryAttestationPool implements AttestationPool {
9
11
  private metrics: PoolInstrumentation<BlockAttestation>;
10
12
 
11
- private attestations: Map</*slot=*/ bigint, Map</*proposalId*/ string, Map</*address=*/ string, BlockAttestation>>>;
13
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
14
+ private attestations: Map<
15
+ /*slot=*/ SlotNumber,
16
+ Map</*proposalId*/ string, Map</*address=*/ string, BlockAttestation>>
17
+ >;
12
18
  private proposals: Map<string, BlockProposal>;
13
19
 
14
20
  constructor(
@@ -30,7 +36,7 @@ export class InMemoryAttestationPool implements AttestationPool {
30
36
  return Promise.resolve(this.attestations.size === 0);
31
37
  }
32
38
 
33
- public getAttestationsForSlot(slot: bigint): Promise<BlockAttestation[]> {
39
+ public getAttestationsForSlot(slot: SlotNumber): Promise<BlockAttestation[]> {
34
40
  return Promise.resolve(
35
41
  Array.from(this.attestations.get(slot)?.values() ?? []).flatMap(proposalAttestationMap =>
36
42
  Array.from(proposalAttestationMap.values()),
@@ -38,7 +44,7 @@ export class InMemoryAttestationPool implements AttestationPool {
38
44
  );
39
45
  }
40
46
 
41
- public getAttestationsForSlotAndProposal(slot: bigint, proposalId: string): Promise<BlockAttestation[]> {
47
+ public getAttestationsForSlotAndProposal(slot: SlotNumber, proposalId: string): Promise<BlockAttestation[]> {
42
48
  const slotAttestationMap = this.attestations.get(slot);
43
49
  if (slotAttestationMap) {
44
50
  const proposalAttestationMap = slotAttestationMap.get(proposalId);
@@ -59,7 +65,7 @@ export class InMemoryAttestationPool implements AttestationPool {
59
65
 
60
66
  // Skip attestations with invalid signatures
61
67
  if (!sender) {
62
- this.log.warn(`Skipping attestation with invalid signature for slot ${slotNumber.toBigInt()}`, {
68
+ this.log.warn(`Skipping attestation with invalid signature for slot ${slotNumber}`, {
63
69
  signature: attestation.signature.toString(),
64
70
  slotNumber,
65
71
  proposalId,
@@ -67,11 +73,11 @@ export class InMemoryAttestationPool implements AttestationPool {
67
73
  continue;
68
74
  }
69
75
 
70
- const slotAttestationMap = getSlotOrDefault(this.attestations, slotNumber.toBigInt());
76
+ const slotAttestationMap = getSlotOrDefault(this.attestations, slotNumber);
71
77
  const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
72
78
  proposalAttestationMap.set(sender.toString(), attestation);
73
79
 
74
- this.log.verbose(`Added attestation for slot ${slotNumber.toBigInt()} from ${sender}`, {
80
+ this.log.verbose(`Added attestation for slot ${slotNumber} from ${sender}`, {
75
81
  signature: attestation.signature.toString(),
76
82
  slotNumber,
77
83
  address: sender,
@@ -82,7 +88,7 @@ export class InMemoryAttestationPool implements AttestationPool {
82
88
  return Promise.resolve();
83
89
  }
84
90
 
85
- #getNumberOfAttestationsInSlot(slot: bigint): number {
91
+ #getNumberOfAttestationsInSlot(slot: SlotNumber): number {
86
92
  let total = 0;
87
93
  const slotAttestationMap = getSlotOrDefault(this.attestations, slot);
88
94
 
@@ -94,7 +100,7 @@ export class InMemoryAttestationPool implements AttestationPool {
94
100
  return total;
95
101
  }
96
102
 
97
- public async deleteAttestationsOlderThan(oldestSlot: bigint): Promise<void> {
103
+ public async deleteAttestationsOlderThan(oldestSlot: SlotNumber): Promise<void> {
98
104
  const olderThan = [];
99
105
 
100
106
  // Entries are iterated in insertion order, so we can break as soon as we find a slot that is older than the oldestSlot.
@@ -115,7 +121,7 @@ export class InMemoryAttestationPool implements AttestationPool {
115
121
  return Promise.resolve();
116
122
  }
117
123
 
118
- public deleteAttestationsForSlot(slot: bigint): Promise<void> {
124
+ public deleteAttestationsForSlot(slot: SlotNumber): Promise<void> {
119
125
  // We count the number of attestations we are removing
120
126
  const numberOfAttestations = this.#getNumberOfAttestationsInSlot(slot);
121
127
  const proposalIdsToDelete = this.attestations.get(slot)?.keys();
@@ -133,7 +139,7 @@ export class InMemoryAttestationPool implements AttestationPool {
133
139
  return Promise.resolve();
134
140
  }
135
141
 
136
- public deleteAttestationsForSlotAndProposal(slot: bigint, proposalId: string): Promise<void> {
142
+ public deleteAttestationsForSlotAndProposal(slot: SlotNumber, proposalId: string): Promise<void> {
137
143
  const slotAttestationMap = getSlotOrDefault(this.attestations, slot);
138
144
  if (slotAttestationMap) {
139
145
  if (slotAttestationMap.has(proposalId)) {
@@ -152,7 +158,7 @@ export class InMemoryAttestationPool implements AttestationPool {
152
158
  public deleteAttestations(attestations: BlockAttestation[]): Promise<void> {
153
159
  for (const attestation of attestations) {
154
160
  const slotNumber = attestation.payload.header.slotNumber;
155
- const slotAttestationMap = this.attestations.get(slotNumber.toBigInt());
161
+ const slotAttestationMap = this.attestations.get(slotNumber);
156
162
  if (slotAttestationMap) {
157
163
  const proposalId = attestation.archive.toString();
158
164
  const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
@@ -161,7 +167,7 @@ export class InMemoryAttestationPool implements AttestationPool {
161
167
 
162
168
  // Skip attestations with invalid signatures
163
169
  if (!sender) {
164
- this.log.warn(`Skipping deletion of attestation with invalid signature for slot ${slotNumber.toBigInt()}`);
170
+ this.log.warn(`Skipping deletion of attestation with invalid signature for slot ${slotNumber}`);
165
171
  continue;
166
172
  }
167
173
 
@@ -173,10 +179,33 @@ export class InMemoryAttestationPool implements AttestationPool {
173
179
  return Promise.resolve();
174
180
  }
175
181
 
182
+ public hasAttestation(attestation: BlockAttestation): Promise<boolean> {
183
+ const slotNumber = attestation.payload.header.slotNumber;
184
+ const proposalId = attestation.archive.toString();
185
+ const sender = attestation.getSender();
186
+
187
+ // Attestations with invalid signatures are never in the pool
188
+ if (!sender) {
189
+ return Promise.resolve(false);
190
+ }
191
+
192
+ const slotAttestationMap = this.attestations.get(slotNumber);
193
+ if (!slotAttestationMap) {
194
+ return Promise.resolve(false);
195
+ }
196
+
197
+ const proposalAttestationMap = slotAttestationMap.get(proposalId);
198
+ if (!proposalAttestationMap) {
199
+ return Promise.resolve(false);
200
+ }
201
+
202
+ return Promise.resolve(proposalAttestationMap.has(sender.toString()));
203
+ }
204
+
176
205
  public addBlockProposal(blockProposal: BlockProposal): Promise<void> {
177
206
  // We initialize slot-proposal mapping if it does not exist
178
207
  // This is important to ensure we can delete this proposal if there were not attestations for it
179
- const slotProposalMapping = getSlotOrDefault(this.attestations, blockProposal.slotNumber.toBigInt());
208
+ const slotProposalMapping = getSlotOrDefault(this.attestations, blockProposal.slotNumber);
180
209
  slotProposalMapping.set(blockProposal.payload.archive.toString(), new Map<string, BlockAttestation>());
181
210
 
182
211
  this.proposals.set(blockProposal.payload.archive.toString(), blockProposal);
@@ -186,6 +215,38 @@ export class InMemoryAttestationPool implements AttestationPool {
186
215
  public getBlockProposal(id: string): Promise<BlockProposal | undefined> {
187
216
  return Promise.resolve(this.proposals.get(id));
188
217
  }
218
+
219
+ public hasBlockProposal(idOrProposal: string | BlockProposal): Promise<boolean> {
220
+ const id = typeof idOrProposal === 'string' ? idOrProposal : idOrProposal.payload.archive.toString();
221
+ return Promise.resolve(this.proposals.has(id));
222
+ }
223
+
224
+ public hasReachedProposalCap(slot: SlotNumber): Promise<boolean> {
225
+ const slotAttestationMap = this.attestations.get(slot);
226
+ const proposalCount = slotAttestationMap?.size ?? 0;
227
+ return Promise.resolve(proposalCount >= MAX_PROPOSALS_PER_SLOT);
228
+ }
229
+
230
+ public hasReachedAttestationCap(slot: SlotNumber, proposalId: string, committeeSize: number): Promise<boolean> {
231
+ const limit = committeeSize + ATTESTATION_CAP_BUFFER;
232
+ const count = this.attestations.get(slot)?.get(proposalId)?.size ?? 0;
233
+ return Promise.resolve(limit <= 0 || count >= limit);
234
+ }
235
+
236
+ public async canAddProposal(block: BlockProposal): Promise<boolean> {
237
+ return this.proposals.has(block.archive.toString()) || !(await this.hasReachedProposalCap(block.slotNumber));
238
+ }
239
+
240
+ public async canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean> {
241
+ const sender = attestation.getSender();
242
+ const slot = attestation.payload.header.slotNumber;
243
+ const pid = attestation.archive.toString();
244
+ return (
245
+ !!sender &&
246
+ ((this.attestations.get(slot)?.get(pid)?.has(sender.toString()) ?? false) ||
247
+ !(await this.hasReachedAttestationCap(slot, pid, committeeSize)))
248
+ );
249
+ }
189
250
  }
190
251
 
191
252
  /**
@@ -197,8 +258,9 @@ export class InMemoryAttestationPool implements AttestationPool {
197
258
  * @returns The slot mapping
198
259
  */
199
260
  function getSlotOrDefault(
200
- map: Map<bigint, Map<string, Map<string, BlockAttestation>>>,
201
- slot: bigint,
261
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
262
+ map: Map<SlotNumber, Map<string, Map<string, BlockAttestation>>>,
263
+ slot: SlotNumber,
202
264
  ): Map<string, Map<string, BlockAttestation>> {
203
265
  if (!map.has(slot)) {
204
266
  map.set(slot, new Map<string, Map<string, BlockAttestation>>());
@@ -1,5 +1,5 @@
1
- import type { Secp256k1Signer } from '@aztec/foundation/crypto';
2
- import { Fr } from '@aztec/foundation/fields';
1
+ import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import {
4
4
  BlockAttestation,
5
5
  ConsensusPayload,
@@ -33,7 +33,7 @@ export const mockAttestation = (
33
33
  ): BlockAttestation => {
34
34
  // Use arbitrary numbers for all other than slot
35
35
  const header = makeL2BlockHeader(1, 2, slot);
36
- const payload = new ConsensusPayload(header.toCheckpointHeader(), archive, header.state);
36
+ const payload = new ConsensusPayload(header.toCheckpointHeader(), archive);
37
37
 
38
38
  const attestationHash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
39
39
  const attestationSignature = signer.sign(attestationHash);
@@ -10,6 +10,7 @@ import {
10
10
  type MetricsType,
11
11
  type ObservableGauge,
12
12
  type TelemetryClient,
13
+ type UpDownCounter,
13
14
  } from '@aztec/telemetry-client';
14
15
 
15
16
  export enum PoolName {
@@ -20,6 +21,7 @@ export enum PoolName {
20
21
  type MetricsLabels = {
21
22
  objectInMempool: MetricsType;
22
23
  objectSize: MetricsType;
24
+ itemsAdded: MetricsType;
23
25
  };
24
26
 
25
27
  /**
@@ -32,11 +34,13 @@ function getMetricsLabels(name: PoolName): MetricsLabels {
32
34
  return {
33
35
  objectInMempool: Metrics.MEMPOOL_TX_COUNT,
34
36
  objectSize: Metrics.MEMPOOL_TX_SIZE,
37
+ itemsAdded: Metrics.MEMPOOL_TX_ADDED_COUNT,
35
38
  };
36
39
  } else if (name === PoolName.ATTESTATION_POOL) {
37
40
  return {
38
41
  objectInMempool: Metrics.MEMPOOL_ATTESTATIONS_COUNT,
39
42
  objectSize: Metrics.MEMPOOL_ATTESTATIONS_SIZE,
43
+ itemsAdded: Metrics.MEMPOOL_ATTESTATIONS_ADDED_COUNT,
40
44
  };
41
45
  }
42
46
 
@@ -53,6 +57,7 @@ export type PoolStatsCallback = () => Promise<{
53
57
  export class PoolInstrumentation<PoolObject extends Gossipable> {
54
58
  /** The number of txs in the mempool */
55
59
  private objectsInMempool: ObservableGauge;
60
+ private addObjectCounter: UpDownCounter;
56
61
  /** Tracks tx size */
57
62
  private objectSize: Histogram;
58
63
 
@@ -89,6 +94,10 @@ export class PoolInstrumentation<PoolObject extends Gossipable> {
89
94
  dbStats,
90
95
  );
91
96
 
97
+ this.addObjectCounter = this.meter.createUpDownCounter(metricsLabels.itemsAdded, {
98
+ description: 'The number of transactions added to the mempool',
99
+ });
100
+
92
101
  this.meter.addBatchObservableCallback(this.observeStats, [this.objectsInMempool]);
93
102
  }
94
103
 
@@ -96,6 +105,10 @@ export class PoolInstrumentation<PoolObject extends Gossipable> {
96
105
  this.objectSize.record(poolObject.getSize());
97
106
  }
98
107
 
108
+ public incrementAddedObjects(count: number) {
109
+ this.addObjectCounter.add(count);
110
+ }
111
+
99
112
  private observeStats = async (observer: BatchObservableResult) => {
100
113
  const { itemCount } = await this.poolStats();
101
114
  if (typeof itemCount === 'number') {
@@ -1,4 +1,5 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
3
  import { toArray } from '@aztec/foundation/iterable';
3
4
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
5
  import type { TypedEventEmitter } from '@aztec/foundation/types';
@@ -6,7 +7,7 @@ import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap, AztecAsyncSi
6
7
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
7
8
  import { GasFees } from '@aztec/stdlib/gas';
8
9
  import type { MerkleTreeReadOperations, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
9
- import { ClientIvcProof } from '@aztec/stdlib/proofs';
10
+ import { ChonkProof } from '@aztec/stdlib/proofs';
10
11
  import type { TxAddedToPoolStats } from '@aztec/stdlib/stats';
11
12
  import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
12
13
  import { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
@@ -37,7 +38,7 @@ export class AztecKVTxPool extends (EventEmitter as new () => TypedEventEmitter<
37
38
  txPoolOverflowFactor: number = 1;
38
39
 
39
40
  /** Index from tx hash to the block number in which they were mined, filtered by mined txs. */
40
- #minedTxHashToBlock: AztecAsyncMap<string, number>;
41
+ #minedTxHashToBlock: AztecAsyncMap<string, BlockNumber>;
41
42
 
42
43
  /** Index from tx priority (stored as hex) to its tx hash, filtered by pending txs. */
43
44
  #pendingTxPriorityToHash: AztecAsyncMultiMap<string, string>;
@@ -49,10 +50,10 @@ export class AztecKVTxPool extends (EventEmitter as new () => TypedEventEmitter<
49
50
  #pendingTxHashToHeaderHash: AztecAsyncMap<string, string>;
50
51
 
51
52
  /** Map from tx hash to the block number it was originally mined in (for soft-deleted txs). */
52
- #deletedMinedTxHashes: AztecAsyncMap<string, number>;
53
+ #deletedMinedTxHashes: AztecAsyncMap<string, BlockNumber>;
53
54
 
54
55
  /** MultiMap from block number to deleted mined tx hashes for efficient cleanup. */
55
- #blockToDeletedMinedTxHash: AztecAsyncMultiMap<number, string>;
56
+ #blockToDeletedMinedTxHash: AztecAsyncMultiMap<BlockNumber, string>;
56
57
 
57
58
  /** The cumulative tx size in bytes that the pending txs in the pool take up. */
58
59
  #pendingTxSize: AztecAsyncSingleton<number>;
@@ -222,7 +223,7 @@ export class AztecKVTxPool extends (EventEmitter as new () => TypedEventEmitter<
222
223
  return vals.map(TxHash.fromString);
223
224
  }
224
225
 
225
- public async getMinedTxHashes(): Promise<[TxHash, number][]> {
226
+ public async getMinedTxHashes(): Promise<[TxHash, BlockNumber][]> {
226
227
  const vals = await toArray(this.#minedTxHashToBlock.entriesAsync());
227
228
  return vals.map(([txHash, blockNumber]) => [TxHash.fromString(txHash), blockNumber]);
228
229
  }
@@ -273,6 +274,11 @@ export class AztecKVTxPool extends (EventEmitter as new () => TypedEventEmitter<
273
274
  return await Promise.all(txHashes.map(txHash => this.#txs.hasAsync(txHash.toString())));
274
275
  }
275
276
 
277
+ async hasTx(txHash: TxHash): Promise<boolean> {
278
+ const result = await this.hasTxs([txHash]);
279
+ return result[0];
280
+ }
281
+
276
282
  /**
277
283
  * Checks if an archived tx exists and returns it.
278
284
  * @param txHash - The tx hash.
@@ -436,10 +442,10 @@ export class AztecKVTxPool extends (EventEmitter as new () => TypedEventEmitter<
436
442
  * @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.
437
443
  * @returns The number of transactions permanently deleted.
438
444
  */
439
- public async cleanupDeletedMinedTxs(blockNumber: number): Promise<number> {
445
+ public async cleanupDeletedMinedTxs(blockNumber: BlockNumber): Promise<number> {
440
446
  let deletedCount = 0;
441
447
  const txHashesToDelete: string[] = [];
442
- const blocksToDelete: number[] = [];
448
+ const blocksToDelete: BlockNumber[] = [];
443
449
 
444
450
  await this.#store.transactionAsync(async () => {
445
451
  // Iterate through all entries and check block numbers
@@ -546,7 +552,7 @@ export class AztecKVTxPool extends (EventEmitter as new () => TypedEventEmitter<
546
552
  const archivedTx: Tx = new Tx(
547
553
  tx.txHash,
548
554
  tx.data,
549
- ClientIvcProof.empty(),
555
+ ChonkProof.empty(),
550
556
  tx.contractClassLogFields,
551
557
  tx.publicFunctionCalldata,
552
558
  );
@@ -1,3 +1,4 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
1
2
  import { createLogger } from '@aztec/foundation/log';
2
3
  import type { TypedEventEmitter } from '@aztec/foundation/types';
3
4
  import type { TxAddedToPoolStats } from '@aztec/stdlib/stats';
@@ -18,10 +19,11 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
18
19
  * Our tx pool, stored as a Map in-memory, with K: tx hash and V: the transaction.
19
20
  */
20
21
  private txs: Map<bigint, Tx>;
21
- private minedTxs: Map<bigint, number>;
22
+ private minedTxs: Map<bigint, BlockNumber>;
22
23
  private pendingTxs: Set<bigint>;
23
- private deletedMinedTxHashes: Map<bigint, number>;
24
- private blockToDeletedMinedTxHash: Map<number, Set<bigint>>;
24
+ private deletedMinedTxHashes: Map<bigint, BlockNumber>;
25
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
26
+ private blockToDeletedMinedTxHash: Map<BlockNumber, Set<bigint>>;
25
27
 
26
28
  private metrics: PoolInstrumentation<Tx>;
27
29
 
@@ -108,7 +110,7 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
108
110
  });
109
111
  }
110
112
 
111
- public getMinedTxHashes(): Promise<[TxHash, number][]> {
113
+ public getMinedTxHashes(): Promise<[TxHash, BlockNumber][]> {
112
114
  return Promise.resolve(
113
115
  Array.from(this.minedTxs.entries()).map(([txHash, blockNumber]) => [TxHash.fromBigInt(txHash), blockNumber]),
114
116
  );
@@ -151,6 +153,11 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
151
153
  return Promise.resolve(txHashes.map(txHash => this.txs.has(txHash.toBigInt())));
152
154
  }
153
155
 
156
+ async hasTx(txHash: TxHash): Promise<boolean> {
157
+ const result = await this.hasTxs([txHash]);
158
+ return result[0];
159
+ }
160
+
154
161
  public getArchivedTxByHash(): Promise<Tx | undefined> {
155
162
  return Promise.resolve(undefined);
156
163
  }
@@ -248,9 +255,9 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
248
255
  * @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.
249
256
  * @returns The number of transactions permanently deleted.
250
257
  */
251
- public cleanupDeletedMinedTxs(blockNumber: number): Promise<number> {
258
+ public cleanupDeletedMinedTxs(blockNumber: BlockNumber): Promise<number> {
252
259
  let deletedCount = 0;
253
- const blocksToDelete: number[] = [];
260
+ const blocksToDelete: BlockNumber[] = [];
254
261
 
255
262
  // Find all blocks up to the specified block number
256
263
  for (const [block, txHashes] of this.blockToDeletedMinedTxHash.entries()) {
@@ -1,3 +1,4 @@
1
+ import type { BlockNumber } from '@aztec/foundation/branded-types';
1
2
  import type { TypedEventEmitter } from '@aztec/foundation/types';
2
3
  import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
3
4
 
@@ -43,6 +44,13 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
43
44
  */
44
45
  hasTxs(txHashes: TxHash[]): Promise<boolean[]>;
45
46
 
47
+ /**
48
+ * Checks if a transaction exists in the pool
49
+ * @param txHash - The hash of the transaction to check for
50
+ * @returns True if the transaction exists, false otherwise
51
+ */
52
+ hasTx(txHash: TxHash): Promise<boolean>;
53
+
46
54
  /**
47
55
  * Checks if an archived transaction exists in the pool and returns it.
48
56
  * @param txHash - The hash of the transaction, used as an ID.
@@ -95,7 +103,7 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
95
103
  * Gets the hashes of mined transactions currently in the tx pool.
96
104
  * @returns An array of mined transaction hashes found in the tx pool.
97
105
  */
98
- getMinedTxHashes(): Promise<[tx: TxHash, blockNumber: number][]>;
106
+ getMinedTxHashes(): Promise<[tx: TxHash, blockNumber: BlockNumber][]>;
99
107
 
100
108
  /**
101
109
  * Returns whether the given tx hash is flagged as pending, mined, or deleted.
@@ -124,5 +132,5 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
124
132
  * @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.
125
133
  * @returns The number of transactions permanently deleted.
126
134
  */
127
- cleanupDeletedMinedTxs(blockNumber: number): Promise<number>;
135
+ cleanupDeletedMinedTxs(blockNumber: BlockNumber): Promise<number>;
128
136
  }
@@ -1,3 +1,4 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
1
2
  import { unfreeze } from '@aztec/foundation/types';
2
3
  import { GasFees } from '@aztec/stdlib/gas';
3
4
  import { mockTx } from '@aztec/stdlib/testing';
@@ -13,7 +14,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
13
14
  let pool: TxPool;
14
15
 
15
16
  const minedBlockHeader = BlockHeader.empty({
16
- globalVariables: GlobalVariables.empty({ blockNumber: 1, timestamp: 0n }),
17
+ globalVariables: GlobalVariables.empty({ blockNumber: BlockNumber(1), timestamp: 0n }),
17
18
  });
18
19
 
19
20
  beforeEach(() => {
@@ -261,7 +262,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
261
262
  await pool.deleteTxs([txs[0].getTxHash(), txs[1].getTxHash()]);
262
263
 
263
264
  // Clean up deleted mined txs from block 1 and earlier
264
- const deletedCount = await pool.cleanupDeletedMinedTxs(1);
265
+ const deletedCount = await pool.cleanupDeletedMinedTxs(BlockNumber(1));
265
266
 
266
267
  // Verify old transactions are permanently deleted
267
268
  expect(deletedCount).toBe(2);
@@ -276,7 +277,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
276
277
 
277
278
  // Mark as mined in block 2
278
279
  const laterBlockHeader = BlockHeader.empty({
279
- globalVariables: GlobalVariables.empty({ blockNumber: 2, timestamp: 0n }),
280
+ globalVariables: GlobalVariables.empty({ blockNumber: BlockNumber(2), timestamp: 0n }),
280
281
  });
281
282
  await pool.markAsMined([txs[0].getTxHash()], laterBlockHeader);
282
283
 
@@ -284,7 +285,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
284
285
  await pool.deleteTxs([txs[0].getTxHash()]);
285
286
 
286
287
  // Try to clean up with block 1 (before the mined block)
287
- const deletedCount = await pool.cleanupDeletedMinedTxs(1);
288
+ const deletedCount = await pool.cleanupDeletedMinedTxs(BlockNumber(1));
288
289
 
289
290
  // Verify no transactions were cleaned up
290
291
  expect(deletedCount).toBe(0);
@@ -1,11 +1,11 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
- import { NoCommitteeError } from '@aztec/ethereum';
2
+ import { NoCommitteeError } from '@aztec/ethereum/contracts';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { type BlockAttestation, type P2PValidator, PeerErrorSeverity } from '@aztec/stdlib/p2p';
5
5
 
6
6
  export class AttestationValidator implements P2PValidator<BlockAttestation> {
7
- private epochCache: EpochCacheInterface;
8
- private logger: Logger;
7
+ protected epochCache: EpochCacheInterface;
8
+ protected logger: Logger;
9
9
 
10
10
  constructor(epochCache: EpochCacheInterface) {
11
11
  this.epochCache = epochCache;
@@ -13,47 +13,45 @@ export class AttestationValidator implements P2PValidator<BlockAttestation> {
13
13
  }
14
14
 
15
15
  async validate(message: BlockAttestation): Promise<PeerErrorSeverity | undefined> {
16
- const slotNumberBigInt = message.payload.header.slotNumber.toBigInt();
16
+ const slotNumber = message.payload.header.slotNumber;
17
17
 
18
18
  try {
19
19
  const { currentProposer, nextProposer, currentSlot, nextSlot } =
20
20
  await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
21
21
 
22
- if (slotNumberBigInt !== currentSlot && slotNumberBigInt !== nextSlot) {
23
- this.logger.warn(
24
- `Attestation slot ${slotNumberBigInt} is not current (${currentSlot}) or next (${nextSlot}) slot`,
25
- );
22
+ if (slotNumber !== currentSlot && slotNumber !== nextSlot) {
23
+ this.logger.warn(`Attestation slot ${slotNumber} is not current (${currentSlot}) or next (${nextSlot}) slot`);
26
24
  return PeerErrorSeverity.HighToleranceError;
27
25
  }
28
26
 
29
27
  // Verify the signature is valid
30
28
  const attester = message.getSender();
31
29
  if (attester === undefined) {
32
- this.logger.warn(`Invalid signature in attestation for slot ${slotNumberBigInt}`);
30
+ this.logger.warn(`Invalid signature in attestation for slot ${slotNumber}`);
33
31
  return PeerErrorSeverity.LowToleranceError;
34
32
  }
35
33
 
36
34
  // Verify the attester is in the committee for this slot
37
- if (!(await this.epochCache.isInCommittee(slotNumberBigInt, attester))) {
38
- this.logger.warn(`Attester ${attester.toString()} is not in committee for slot ${slotNumberBigInt}`);
35
+ if (!(await this.epochCache.isInCommittee(slotNumber, attester))) {
36
+ this.logger.warn(`Attester ${attester.toString()} is not in committee for slot ${slotNumber}`);
39
37
  return PeerErrorSeverity.HighToleranceError;
40
38
  }
41
39
 
42
40
  // Verify the proposer signature matches the expected proposer for this slot
43
41
  const proposer = message.getProposer();
44
- const expectedProposer = slotNumberBigInt === currentSlot ? currentProposer : nextProposer;
42
+ const expectedProposer = slotNumber === currentSlot ? currentProposer : nextProposer;
45
43
  if (!expectedProposer) {
46
- this.logger.warn(`No proposer defined for slot ${slotNumberBigInt}`);
44
+ this.logger.warn(`No proposer defined for slot ${slotNumber}`);
47
45
  return PeerErrorSeverity.HighToleranceError;
48
46
  }
49
47
  if (!proposer) {
50
- this.logger.warn(`Invalid proposer signature in attestation for slot ${slotNumberBigInt}`);
48
+ this.logger.warn(`Invalid proposer signature in attestation for slot ${slotNumber}`);
51
49
  return PeerErrorSeverity.LowToleranceError;
52
50
  }
53
51
  if (!proposer.equals(expectedProposer)) {
54
52
  this.logger.warn(
55
53
  `Proposer signature mismatch in attestation. ` +
56
- `Expected ${expectedProposer?.toString() ?? 'none'} but got ${proposer.toString()} for slot ${slotNumberBigInt}`,
54
+ `Expected ${expectedProposer?.toString() ?? 'none'} but got ${proposer.toString()} for slot ${slotNumber}`,
57
55
  );
58
56
  return PeerErrorSeverity.HighToleranceError;
59
57
  }
@@ -62,7 +60,7 @@ export class AttestationValidator implements P2PValidator<BlockAttestation> {
62
60
  } catch (e) {
63
61
  // People shouldn't be sending us attestations if the committee doesn't exist
64
62
  if (e instanceof NoCommitteeError) {
65
- this.logger.warn(`No committee exists for attestation for slot ${slotNumberBigInt}`);
63
+ this.logger.warn(`No committee exists for attestation for slot ${slotNumber}`);
66
64
  return PeerErrorSeverity.LowToleranceError;
67
65
  }
68
66
  throw e;
@@ -0,0 +1,91 @@
1
+ import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
+ import { type BlockAttestation, PeerErrorSeverity } from '@aztec/stdlib/p2p';
3
+ import { Attributes, Metrics, type TelemetryClient, ValueType } from '@aztec/telemetry-client';
4
+
5
+ import type { AttestationPool } from '../../mem_pools/attestation_pool/attestation_pool.js';
6
+ import { AttestationValidator } from './attestation_validator.js';
7
+
8
+ /**
9
+ * FishermanAttestationValidator extends the base AttestationValidator to add
10
+ * additional validation for fisherman nodes: verifying that attestations sign
11
+ * the same payload as the original proposal.
12
+ * Invalid attestations are rejected (not propagated), but peer penalization is
13
+ * handled by LibP2PService based on the fishermanMode config to ensure a better
14
+ * view of the network.
15
+ */
16
+ export class FishermanAttestationValidator extends AttestationValidator {
17
+ private invalidAttestationCounter;
18
+
19
+ constructor(
20
+ epochCache: EpochCacheInterface,
21
+ private attestationPool: AttestationPool,
22
+ telemetryClient: TelemetryClient,
23
+ ) {
24
+ super(epochCache);
25
+ this.logger = this.logger.createChild('[FISHERMAN]');
26
+
27
+ const meter = telemetryClient.getMeter('FishermanAttestationValidator');
28
+ this.invalidAttestationCounter = meter.createUpDownCounter(Metrics.VALIDATOR_INVALID_ATTESTATION_RECEIVED_COUNT, {
29
+ description: 'The number of invalid attestations received',
30
+ valueType: ValueType.INT,
31
+ });
32
+ }
33
+
34
+ override async validate(message: BlockAttestation): Promise<PeerErrorSeverity | undefined> {
35
+ // First run the standard validation
36
+ const baseValidationResult = await super.validate(message);
37
+ if (baseValidationResult !== undefined) {
38
+ // Track base validation failures (invalid signature, wrong committee, etc.)
39
+ this.invalidAttestationCounter.add(1, {
40
+ [Attributes.ERROR_TYPE]: 'base_validation_failed',
41
+ });
42
+ return baseValidationResult;
43
+ }
44
+
45
+ // fisherman validation: verify attestation payload matches proposal payload
46
+ const slotNumberBigInt = message.payload.header.slotNumber;
47
+ const attester = message.getSender();
48
+ const proposer = message.getProposer();
49
+
50
+ if (!attester || !proposer) {
51
+ return undefined;
52
+ }
53
+
54
+ const proposalId = message.archive.toString();
55
+ const proposal = await this.attestationPool.getBlockProposal(proposalId);
56
+
57
+ if (proposal) {
58
+ // Compare the attestation payload with the proposal payload
59
+ if (!message.payload.equals(proposal.payload)) {
60
+ this.logger.error(
61
+ `Attestation payload mismatch for slot ${slotNumberBigInt}! ` +
62
+ `Attester ${attester.toString()} signed different data than the proposal.`,
63
+ {
64
+ slot: slotNumberBigInt.toString(),
65
+ attester: attester.toString(),
66
+ proposer: proposer.toString(),
67
+ proposalArchive: proposal.archive.toString(),
68
+ attestationArchive: message.archive.toString(),
69
+ proposalHeader: proposal.payload.header.hash().toString(),
70
+ attestationHeader: message.payload.header.hash().toString(),
71
+ },
72
+ );
73
+
74
+ // Track invalid attestation metric
75
+ this.invalidAttestationCounter.add(1, {
76
+ [Attributes.ERROR_TYPE]: 'payload_mismatch',
77
+ });
78
+
79
+ // Return error to reject the message, but LibP2PService won't penalize in fisherman mode
80
+ return PeerErrorSeverity.LowToleranceError;
81
+ }
82
+ } else {
83
+ // We might receive attestations before proposals in some cases
84
+ this.logger.debug(
85
+ `Received attestation for slot ${slotNumberBigInt} but proposal not found yet. ` + `Proposal ID: ${proposalId}`,
86
+ );
87
+ }
88
+
89
+ return undefined;
90
+ }
91
+ }
@@ -1 +1,2 @@
1
1
  export * from './attestation_validator.js';
2
+ export * from './fisherman_attestation_validator.js';