@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,14 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
- import { randomInt } from '@aztec/foundation/crypto';
3
- import { Fr } from '@aztec/foundation/fields';
2
+ import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { randomInt } from '@aztec/foundation/crypto/random';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
5
  import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
5
- import { SerialQueue } from '@aztec/foundation/queue';
6
6
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
7
  import { Timer } from '@aztec/foundation/timer';
8
8
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
9
9
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
10
  import { protocolContractsHash } from '@aztec/protocol-contracts';
11
- import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
11
+ import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
12
12
  import type { ContractDataSource } from '@aztec/stdlib/contract';
13
13
  import { GasFees } from '@aztec/stdlib/gas';
14
14
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
@@ -50,8 +50,13 @@ import { ENR } from '@nethermindeth/enr';
50
50
  import { createLibp2p } from 'libp2p';
51
51
 
52
52
  import type { P2PConfig } from '../../config.js';
53
+ import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
53
54
  import type { MemPools } from '../../mem_pools/interface.js';
54
- import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
55
+ import {
56
+ AttestationValidator,
57
+ BlockProposalValidator,
58
+ FishermanAttestationValidator,
59
+ } from '../../msg_validators/index.js';
55
60
  import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
56
61
  import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
57
62
  import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
@@ -107,11 +112,15 @@ interface ValidationResult {
107
112
 
108
113
  type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
109
114
 
115
+ // REFACTOR: Unify with the type above
116
+ type ReceivedMessageValidationResult<T> =
117
+ | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject> }
118
+ | { obj?: undefined; result: TopicValidatorResult.Reject };
119
+
110
120
  /**
111
121
  * Lib P2P implementation of the P2PService interface.
112
122
  */
113
123
  export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
114
- private jobQueue: SerialQueue = new SerialQueue();
115
124
  private discoveryRunningPromise?: RunningPromise;
116
125
  private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
117
126
 
@@ -122,7 +131,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
122
131
  private protocolVersion = '';
123
132
  private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
124
133
 
125
- private feesCache: { blockNumber: number; gasFees: GasFees } | undefined;
134
+ private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
126
135
 
127
136
  /**
128
137
  * Callback for when a block is received from a peer.
@@ -135,6 +144,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
135
144
 
136
145
  private instrumentation: P2PInstrumentation;
137
146
 
147
+ protected logger: Logger;
148
+
138
149
  constructor(
139
150
  private clientType: T,
140
151
  private config: P2PConfig,
@@ -148,10 +159,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
148
159
  private proofVerifier: ClientProtocolCircuitVerifier,
149
160
  private worldStateSynchronizer: WorldStateSynchronizer,
150
161
  telemetry: TelemetryClient,
151
- protected logger = createLogger('p2p:libp2p_service'),
162
+ logger: Logger = createLogger('p2p:libp2p_service'),
152
163
  ) {
153
164
  super(telemetry, 'LibP2PService');
154
165
 
166
+ // Create child logger with fisherman prefix if in fisherman mode
167
+ this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
168
+
155
169
  this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
156
170
 
157
171
  this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
@@ -169,15 +183,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
169
183
  this.protocolVersion,
170
184
  );
171
185
 
172
- this.attestationValidator = new AttestationValidator(epochCache);
186
+ // Use FishermanAttestationValidator in fisherman mode to validate attestation payloads against proposals
187
+ this.attestationValidator = config.fishermanMode
188
+ ? new FishermanAttestationValidator(epochCache, mempools.attestationPool!, telemetry)
189
+ : new AttestationValidator(epochCache);
173
190
  this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
174
191
 
175
192
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
176
193
 
177
194
  this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
178
195
  this.logger.debug(
179
- `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`,
180
- { p2pMessageIdentifier: await block.p2pMessageIdentifier() },
196
+ `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber} from peer.`,
197
+ { p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
181
198
  );
182
199
  return undefined;
183
200
  };
@@ -390,7 +407,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
390
407
  logger: createLibp2pComponentLogger(logger.module),
391
408
  });
392
409
 
393
- const peerScoring = new PeerScoring(config);
410
+ const peerScoring = new PeerScoring(config, telemetry);
394
411
  const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
395
412
 
396
413
  const peerManager = new PeerManager(
@@ -445,9 +462,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
445
462
  }
446
463
  const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
447
464
 
448
- // Start job queue, peer discovery service and libp2p node
449
- this.jobQueue.start();
450
-
451
465
  await this.peerManager.initializePeers();
452
466
  if (!this.config.p2pDiscoveryDisabled) {
453
467
  await this.peerDiscoveryService.start();
@@ -473,7 +487,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
473
487
  };
474
488
 
475
489
  // Only handle block transactions request if attestation pool is available to the client
476
- if (this.mempools.attestationPool) {
490
+ if (this.mempools.attestationPool && !this.config.disableTransactions) {
477
491
  const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.mempools.txPool);
478
492
  requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
479
493
  }
@@ -485,9 +499,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
485
499
  // add GossipSub listener
486
500
  this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
487
501
 
488
- // Start running promise for peer discovery
502
+ // Start running promise for peer discovery and metrics collection
489
503
  this.discoveryRunningPromise = new RunningPromise(
490
- () => this.peerManager.heartbeat(),
504
+ async () => {
505
+ await this.peerManager.heartbeat();
506
+ },
491
507
  this.logger,
492
508
  this.config.peerCheckIntervalMS,
493
509
  );
@@ -496,9 +512,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
496
512
  // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
497
513
  const reqrespSubProtocolValidators = {
498
514
  ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
499
- // TODO(#11336): A request validator for blocks
500
515
  [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
501
516
  [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
517
+ [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
502
518
  };
503
519
  await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
504
520
  this.logger.info(`Started P2P service`, {
@@ -520,9 +536,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
520
536
  // Stop peer manager
521
537
  this.logger.debug('Stopping peer manager...');
522
538
  await this.peerManager.stop();
523
-
524
- this.logger.debug('Stopping job queue...');
525
- await this.jobQueue.end();
526
539
  this.logger.debug('Stopping running promise...');
527
540
  await this.discoveryRunningPromise?.stop();
528
541
  this.logger.debug('Stopping peer discovery service...');
@@ -610,11 +623,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
610
623
  if (!this.node.services.pubsub) {
611
624
  throw new Error('Pubsub service not available.');
612
625
  }
613
- const p2pMessage = P2PMessage.fromGossipable(message);
626
+ const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
614
627
  const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
615
628
  return result.recipients.length;
616
629
  }
617
630
 
631
+ /**
632
+ * Checks if this message has already been seen, based on its msgId computed from hashing the message data.
633
+ * Note that we do not rely on the seenCache from gossipsub since we want to keep a longer history of seen
634
+ * messages to avoid tx echoes across the network.
635
+ */
618
636
  protected preValidateReceivedMessage(
619
637
  msg: Message,
620
638
  msgId: string,
@@ -650,13 +668,39 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
650
668
  return { result: true, topicType };
651
669
  }
652
670
 
671
+ /**
672
+ * Safely deserializes a P2PMessage from raw message data.
673
+ * @param msgId - The message ID.
674
+ * @param source - The peer ID of the message source.
675
+ * @param data - The raw message data.
676
+ * @returns The deserialized P2PMessage or undefined if deserialization fails.
677
+ */
678
+ private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
679
+ try {
680
+ return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
681
+ } catch (err) {
682
+ this.logger.error(`Error deserializing P2PMessage`, err, {
683
+ msgId,
684
+ source: source.toString(),
685
+ });
686
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
687
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
688
+ return undefined;
689
+ }
690
+ }
691
+
653
692
  /**
654
693
  * Handles a new gossip message that was received by the client.
655
694
  * @param topic - The message's topic.
656
695
  * @param data - The message data
657
696
  */
658
697
  protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
659
- const p2pMessage = P2PMessage.fromMessageData(Buffer.from(msg.data));
698
+ const msgReceivedTime = Date.now();
699
+ let topicType: TopicType | undefined;
700
+ const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
701
+ if (!p2pMessage) {
702
+ return;
703
+ }
660
704
 
661
705
  const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
662
706
 
@@ -665,55 +709,81 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
665
709
  }
666
710
 
667
711
  if (msg.topic === this.topicStrings[TopicType.tx]) {
712
+ topicType = TopicType.tx;
668
713
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
669
714
  }
670
- if (msg.topic === this.topicStrings[TopicType.block_attestation] && this.clientType === P2PClientType.Full) {
671
- await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
715
+ if (msg.topic === this.topicStrings[TopicType.block_attestation]) {
716
+ topicType = TopicType.block_attestation;
717
+ if (this.clientType === P2PClientType.Full) {
718
+ await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
719
+ }
672
720
  }
673
721
  if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
722
+ topicType = TopicType.block_proposal;
674
723
  await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
675
724
  }
676
725
 
726
+ if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
727
+ const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
728
+ this.instrumentation.recordMessageLatency(topicType, latency);
729
+ }
730
+
677
731
  return;
678
732
  }
679
733
 
680
734
  protected async validateReceivedMessage<T>(
681
- validationFunc: () => Promise<{ result: boolean; obj: T }>,
735
+ validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
682
736
  msgId: string,
683
737
  source: PeerId,
684
738
  topicType: TopicType,
685
- ): Promise<{ result: boolean; obj: T | undefined }> {
686
- let resultAndObj: { result: boolean; obj: T | undefined } = { result: false, obj: undefined };
739
+ ): Promise<ReceivedMessageValidationResult<T>> {
740
+ let resultAndObj: ReceivedMessageValidationResult<T> = { result: TopicValidatorResult.Reject };
687
741
  const timer = new Timer();
688
742
  try {
689
743
  resultAndObj = await validationFunc();
690
744
  } catch (err) {
691
- this.logger.error(`Error deserializing and validating message `, err);
745
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
746
+ this.logger.error(`Error deserializing and validating gossipsub message`, err, {
747
+ msgId,
748
+ source: source.toString(),
749
+ topicType,
750
+ });
692
751
  }
693
752
 
694
- if (resultAndObj.result) {
753
+ if (resultAndObj.result === TopicValidatorResult.Accept) {
695
754
  this.instrumentation.recordMessageValidation(topicType, timer);
696
755
  }
697
756
 
698
- this.node.services.pubsub.reportMessageValidationResult(
699
- msgId,
700
- source.toString(),
701
- resultAndObj.result && resultAndObj.obj ? TopicValidatorResult.Accept : TopicValidatorResult.Reject,
702
- );
757
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
703
758
  return resultAndObj;
704
759
  }
705
760
 
706
761
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
707
- const validationFunc = async () => {
762
+ const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
708
763
  const tx = Tx.fromBuffer(payloadData);
709
- const result = await this.validatePropagatedTx(tx, source);
710
- return { result, obj: tx };
764
+ const isValid = await this.validatePropagatedTx(tx, source);
765
+ const exists = isValid && (await this.mempools.txPool.hasTx(tx.getTxHash()));
766
+
767
+ this.logger.trace(`Validate propagated tx`, {
768
+ isValid,
769
+ exists,
770
+ [Attributes.P2P_ID]: source.toString(),
771
+ });
772
+
773
+ if (!isValid) {
774
+ return { result: TopicValidatorResult.Reject };
775
+ } else if (exists) {
776
+ return { result: TopicValidatorResult.Ignore, obj: tx };
777
+ } else {
778
+ return { result: TopicValidatorResult.Accept, obj: tx };
779
+ }
711
780
  };
712
781
 
713
782
  const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source, TopicType.tx);
714
- if (!result || !tx) {
783
+ if (result !== TopicValidatorResult.Accept || !tx) {
715
784
  return;
716
785
  }
786
+
717
787
  const txHash = tx.getTxHash();
718
788
  const txHashString = txHash.toString();
719
789
  this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
@@ -722,10 +792,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
722
792
  });
723
793
 
724
794
  if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
725
- this.logger.debug(`Intentionally dropping tx ${txHashString} (probability rule)`);
795
+ this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
726
796
  return;
727
797
  }
728
798
 
799
+ this.instrumentation.incrementTxReceived(1);
729
800
  await this.mempools.txPool.addTxs([tx]);
730
801
  }
731
802
 
@@ -736,14 +807,42 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
736
807
  * @param attestation - The attestation to process.
737
808
  */
738
809
  private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
739
- const validationFunc = async () => {
810
+ const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
740
811
  const attestation = BlockAttestation.fromBuffer(payloadData);
741
- const result = await this.validateAttestation(source, attestation);
742
- this.logger.trace(`validatePropagatedAttestation: ${result}`, {
812
+ const pool = this.mempools.attestationPool!;
813
+ const isValid = await this.validateAttestation(source, attestation);
814
+ const exists = isValid && (await pool.hasAttestation(attestation));
815
+
816
+ let canAdd = true;
817
+ if (isValid && !exists) {
818
+ const slot = attestation.payload.header.slotNumber;
819
+ const { committee } = await this.epochCache.getCommittee(slot);
820
+ const committeeSize = committee?.length ?? 0;
821
+ canAdd = await pool.canAddAttestation(attestation, committeeSize);
822
+ }
823
+
824
+ this.logger.trace(`Validate propagated block attestation`, {
825
+ isValid,
826
+ exists,
827
+ canAdd,
743
828
  [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
744
829
  [Attributes.P2P_ID]: source.toString(),
745
830
  });
746
- return { result, obj: attestation };
831
+
832
+ if (!isValid) {
833
+ return { result: TopicValidatorResult.Reject };
834
+ } else if (exists) {
835
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
836
+ } else if (!canAdd) {
837
+ this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
838
+ slot: attestation.payload.header.slotNumber.toString(),
839
+ archive: attestation.archive.toString(),
840
+ source: source.toString(),
841
+ });
842
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
843
+ } else {
844
+ return { result: TopicValidatorResult.Accept, obj: attestation };
845
+ }
747
846
  };
748
847
 
749
848
  const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
@@ -752,30 +851,58 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
752
851
  source,
753
852
  TopicType.block_attestation,
754
853
  );
755
- if (!result || !attestation) {
854
+
855
+ if (result !== TopicValidatorResult.Accept || !attestation) {
756
856
  return;
757
857
  }
858
+
758
859
  this.logger.debug(
759
- `Received attestation for slot ${attestation.slotNumber.toNumber()} from external peer ${source.toString()}`,
860
+ `Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
760
861
  {
761
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
762
- slot: attestation.slotNumber.toNumber(),
862
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
863
+ slot: attestation.slotNumber,
763
864
  archive: attestation.archive.toString(),
764
865
  source: source.toString(),
765
866
  },
766
867
  );
868
+
767
869
  await this.mempools.attestationPool!.addAttestations([attestation]);
768
870
  }
769
871
 
770
872
  private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
771
- const validationFunc = async () => {
873
+ const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
772
874
  const block = BlockProposal.fromBuffer(payloadData);
773
- const result = await this.validateBlockProposal(source, block);
774
- this.logger.trace(`validatePropagatedBlock: ${result}`, {
875
+ const isValid = await this.validateBlockProposal(source, block);
876
+ const pool = this.mempools.attestationPool;
877
+
878
+ // Note that we dont have an attestation pool if we're a prover node, but we still
879
+ // subscribe to block proposal topics in order to prevent their txs from being cleared.
880
+ const exists = isValid && (await pool?.hasBlockProposal(block));
881
+ const canAdd = isValid && (await pool?.canAddProposal(block));
882
+
883
+ this.logger.trace(`Validate propagated block proposal`, {
884
+ isValid,
885
+ exists,
886
+ canAdd,
775
887
  [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
776
888
  [Attributes.P2P_ID]: source.toString(),
777
889
  });
778
- return { result, obj: block };
890
+
891
+ if (!isValid) {
892
+ return { result: TopicValidatorResult.Reject };
893
+ } else if (exists) {
894
+ return { result: TopicValidatorResult.Ignore, obj: block };
895
+ } else if (!canAdd) {
896
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
897
+ this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
898
+ slot: block.slotNumber.toString(),
899
+ archive: block.archive.toString(),
900
+ source: source.toString(),
901
+ });
902
+ return { result: TopicValidatorResult.Reject };
903
+ } else {
904
+ return { result: TopicValidatorResult.Accept, obj: block };
905
+ }
779
906
  };
780
907
 
781
908
  const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
@@ -784,6 +911,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
784
911
  source,
785
912
  TopicType.block_proposal,
786
913
  );
914
+
787
915
  if (!result || !block) {
788
916
  return;
789
917
  }
@@ -793,16 +921,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
793
921
 
794
922
  // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
795
923
  @trackSpan('Libp2pService.processValidBlockProposal', async block => ({
796
- [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
924
+ [Attributes.SLOT_NUMBER]: block.slotNumber,
797
925
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
798
- [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then(i => i.toString()),
926
+ [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
799
927
  }))
800
928
  private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
801
- const slot = block.slotNumber.toBigInt();
802
- const previousSlot = slot - 1n;
929
+ const slot = block.slotNumber;
930
+ const previousSlot = SlotNumber(slot - 1);
803
931
  this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
804
- p2pMessageIdentifier: await block.p2pMessageIdentifier(),
805
- slot: block.slotNumber.toNumber(),
932
+ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
933
+ slot: block.slotNumber,
806
934
  archive: block.archive.toString(),
807
935
  source: sender.toString(),
808
936
  });
@@ -811,18 +939,31 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
811
939
  this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
812
940
  }
813
941
 
814
- // Mark the txs in this proposal as non-evictable
942
+ // Attempt to add proposal, then mark the txs in this proposal as non-evictable
943
+ try {
944
+ await this.mempools.attestationPool?.addBlockProposal(block);
945
+ } catch (err: unknown) {
946
+ // Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
947
+ if (err instanceof ProposalSlotCapExceededError) {
948
+ this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
949
+ slot: String(slot),
950
+ archive: block.archive.toString(),
951
+ error: (err as Error).message,
952
+ });
953
+ return;
954
+ }
955
+ throw err;
956
+ }
815
957
  await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
816
- await this.mempools.attestationPool?.addBlockProposal(block);
817
958
  const attestations = await this.blockReceivedCallback(block, sender);
818
959
 
819
960
  // TODO: fix up this pattern - the abstraction is not nice
820
- // The attestation can be undefined if no handler is registered / the validator deems the block invalid
961
+ // The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
821
962
  if (attestations?.length) {
822
963
  for (const attestation of attestations) {
823
- this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber.toNumber()}`, {
824
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
825
- slot: attestation.slotNumber.toNumber(),
964
+ this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
965
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
966
+ slot: attestation.slotNumber,
826
967
  archive: attestation.archive.toString(),
827
968
  });
828
969
  await this.broadcastAttestation(attestation);
@@ -835,9 +976,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
835
976
  * @param attestation - The attestation to broadcast.
836
977
  */
837
978
  @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
838
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
979
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
839
980
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
840
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
981
+ [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
841
982
  }))
842
983
  private async broadcastAttestation(attestation: BlockAttestation) {
843
984
  await this.propagate(attestation);
@@ -848,19 +989,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
848
989
  * @param message - The message to propagate.
849
990
  */
850
991
  public async propagate<T extends Gossipable>(message: T) {
851
- const p2pMessageIdentifier = await message.p2pMessageIdentifier();
992
+ const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
852
993
  this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
853
- void this.jobQueue
854
- .put(async () => {
855
- await this.sendToPeers(message);
856
- })
857
- .catch(error => {
858
- this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
859
- });
994
+ void this.sendToPeers(message).catch(error => {
995
+ this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
996
+ });
860
997
  }
861
998
 
862
999
  /**
863
- * Validate the requested block transactions.
1000
+ * Validate the requested block transactions. Allow partial returns.
864
1001
  * @param request - The block transactions request.
865
1002
  * @param response - The block transactions response.
866
1003
  * @param peerId - The ID of the peer that made the request.
@@ -870,14 +1007,71 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
870
1007
  [Attributes.BLOCK_HASH]: request.blockHash.toString(),
871
1008
  }))
872
1009
  private async validateRequestedBlockTxs(
873
- _request: BlockTxsRequest,
1010
+ request: BlockTxsRequest,
874
1011
  response: BlockTxsResponse,
875
1012
  peerId: PeerId,
876
1013
  ): Promise<boolean> {
877
1014
  const requestedTxValidator = this.createRequestedTxValidator();
878
1015
 
879
1016
  try {
880
- // TODO(palla/txs): Validate that this tx belongs to the block hash being requested
1017
+ if (!response.blockHash.equals(request.blockHash)) {
1018
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1019
+ throw new ValidationError(
1020
+ `Received block txs for unexpected block: expected ${request.blockHash.toString()}, got ${response.blockHash.toString()}`,
1021
+ );
1022
+ }
1023
+
1024
+ if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1025
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1026
+ throw new ValidationError(
1027
+ `Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
1028
+ );
1029
+ }
1030
+
1031
+ // Check no duplicates and not exceeding returnable count
1032
+ const requestedIndices = new Set(request.txIndices.getTrueIndices());
1033
+ const availableIndices = new Set(response.txIndices.getTrueIndices());
1034
+ const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
1035
+
1036
+ const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
1037
+ const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1038
+ if (uniqueReturned.size !== returnedHashes.length) {
1039
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1040
+ throw new ValidationError(`Received duplicate txs in block txs response`);
1041
+ }
1042
+ if (response.txs.length > maxReturnable) {
1043
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1044
+ throw new ValidationError(
1045
+ `Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
1046
+ );
1047
+ }
1048
+
1049
+ // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1050
+ const proposal = await this.mempools.attestationPool?.getBlockProposal(request.blockHash.toString());
1051
+ if (proposal) {
1052
+ // Build intersected indices
1053
+ const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1054
+
1055
+ // Enforce subset membership and preserve increasing order by index.
1056
+ const hashToIndexInProposal = new Map<string, number>(
1057
+ proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
1058
+ );
1059
+ const allowedIndexSet = new Set(intersectIdx);
1060
+ const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
1061
+ const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
1062
+ const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
1063
+ if (!allAllowed || !strictlyIncreasing) {
1064
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1065
+ throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1066
+ }
1067
+ } else {
1068
+ // No local proposal, cannot check the membership/order of the returned txs
1069
+ this.logger.warn(
1070
+ `Block proposal not found for block hash ${request.blockHash.toString()}; cannot validate membership/order of returned txs`,
1071
+ );
1072
+ return false;
1073
+ }
1074
+
881
1075
  await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
882
1076
  return true;
883
1077
  } catch (e: any) {
@@ -901,7 +1095,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
901
1095
  * ReqRespSubProtocol.TX subprotocol validation.
902
1096
  *
903
1097
  * @param requestedTxHash - The collection of the txs that was requested.
904
- * @param responseTx - The collectin of txs that was received as a response to the request.
1098
+ * @param responseTx - The collection of txs that was received as a response to the request.
905
1099
  * @param peerId - The peer ID of the peer that sent the tx.
906
1100
  * @returns True if the whole collection of txs is valid, false otherwise.
907
1101
  */
@@ -928,6 +1122,53 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
928
1122
  }
929
1123
  }
930
1124
 
1125
+ /**
1126
+ * Validates a BLOCK response.
1127
+ *
1128
+ * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1129
+ * Penalizes on block number mismatch or hash mismatch.
1130
+ *
1131
+ * @param requestedBlockNumber - The requested block number.
1132
+ * @param responseBlock - The block returned by the peer.
1133
+ * @param peerId - The peer that returned the block.
1134
+ * @returns True if the response is valid, false otherwise.
1135
+ */
1136
+ @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1137
+ [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1138
+ }))
1139
+ private async validateRequestedBlock(
1140
+ requestedBlockNumber: Fr,
1141
+ responseBlock: L2Block,
1142
+ peerId: PeerId,
1143
+ ): Promise<boolean> {
1144
+ try {
1145
+ const reqNum = Number(requestedBlockNumber.toString());
1146
+ if (responseBlock.number !== reqNum) {
1147
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1148
+ return false;
1149
+ }
1150
+
1151
+ const local = await this.archiver.getBlock(BlockNumber(reqNum));
1152
+ if (!local) {
1153
+ // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1154
+ // TODO: Consider extending this validator to accept an expected hash or
1155
+ // performing quorum-based checks when using P2P syncing prior to L1 sync.
1156
+ this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1157
+ return false;
1158
+ }
1159
+ const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1160
+ if (!localHash.equals(respHash)) {
1161
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1162
+ return false;
1163
+ }
1164
+
1165
+ return true;
1166
+ } catch (e) {
1167
+ this.logger.warn(`Error validating requested block`, e);
1168
+ return false;
1169
+ }
1170
+ }
1171
+
931
1172
  private createRequestedTxValidator(): TxValidator {
932
1173
  return new AggregateTxValidator(
933
1174
  new DataTxValidator(),
@@ -942,19 +1183,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
942
1183
  }
943
1184
 
944
1185
  private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
1186
+ const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1187
+
945
1188
  if (!(await tx.validateTxHash())) {
946
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1189
+ penalize(PeerErrorSeverity.MidToleranceError);
947
1190
  throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
948
1191
  }
949
1192
 
950
1193
  if (requested && !requested.has(tx.getTxHash().toString())) {
951
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1194
+ penalize(PeerErrorSeverity.MidToleranceError);
952
1195
  throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
953
1196
  }
954
1197
 
955
1198
  const { result } = await txValidator.validateTx(tx);
956
1199
  if (result === 'invalid') {
957
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1200
+ penalize(PeerErrorSeverity.LowToleranceError);
958
1201
  throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
959
1202
  }
960
1203
  }
@@ -980,7 +1223,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
980
1223
 
981
1224
  // Double spend validator has a special case handler
982
1225
  if (name === 'doubleSpendValidator') {
983
- const txBlockNumber = currentBlockNumber + 1; // tx is expected to be in the next block
1226
+ const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
984
1227
  severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
985
1228
  }
986
1229
 
@@ -990,7 +1233,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
990
1233
  return true;
991
1234
  }
992
1235
 
993
- private async getGasFees(blockNumber: number): Promise<GasFees> {
1236
+ private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
994
1237
  if (blockNumber === this.feesCache?.blockNumber) {
995
1238
  return this.feesCache.gasFees;
996
1239
  }
@@ -1031,13 +1274,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1031
1274
  * @returns The message validators.
1032
1275
  */
1033
1276
  private async createMessageValidators(
1034
- currentBlockNumber: number,
1277
+ currentBlockNumber: BlockNumber,
1035
1278
  nextSlotTimestamp: UInt64,
1036
1279
  ): Promise<Record<string, MessageValidator>[]> {
1037
1280
  const gasFees = await this.getGasFees(currentBlockNumber);
1038
1281
  const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
1039
1282
 
1040
- const blockNumberInWhichTheTxIsConsideredToBeIncluded = currentBlockNumber + 1;
1283
+ const blockNumberInWhichTheTxIsConsideredToBeIncluded = BlockNumber(currentBlockNumber + 1);
1041
1284
 
1042
1285
  return createTxMessageValidators(
1043
1286
  nextSlotTimestamp,
@@ -1099,7 +1342,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1099
1342
  * @param peerId - The peer ID of the peer that sent the tx.
1100
1343
  * @returns Severity
1101
1344
  */
1102
- private async handleDoubleSpendFailure(tx: Tx, blockNumber: number): Promise<PeerErrorSeverity> {
1345
+ private async handleDoubleSpendFailure(tx: Tx, blockNumber: BlockNumber): Promise<PeerErrorSeverity> {
1103
1346
  if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
1104
1347
  return PeerErrorSeverity.HighToleranceError;
1105
1348
  }
@@ -1107,7 +1350,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1107
1350
  const snapshotValidator = new DoubleSpendTxValidator({
1108
1351
  nullifiersExist: async (nullifiers: Buffer[]) => {
1109
1352
  const merkleTree = this.worldStateSynchronizer.getSnapshot(
1110
- blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
1353
+ BlockNumber(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow),
1111
1354
  );
1112
1355
  const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
1113
1356
  return indices.map(index => index !== undefined);
@@ -1129,9 +1372,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1129
1372
  * @returns True if the attestation is valid, false otherwise.
1130
1373
  */
1131
1374
  @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
1132
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
1375
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
1133
1376
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
1134
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
1377
+ [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
1135
1378
  }))
1136
1379
  public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
1137
1380
  const severity = await this.attestationValidator.validate(attestation);
@@ -1174,7 +1417,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1174
1417
  private async sendToPeers<T extends Gossipable>(message: T) {
1175
1418
  const parent = message.constructor as typeof Gossipable;
1176
1419
 
1177
- const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
1420
+ const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
1178
1421
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
1179
1422
 
1180
1423
  const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);