@aztec/p2p 0.0.0-test.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +7 -0
  2. package/dest/bootstrap/bootstrap.d.ts +38 -0
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -0
  4. package/dest/bootstrap/bootstrap.js +123 -0
  5. package/dest/client/factory.d.ts +21 -0
  6. package/dest/client/factory.d.ts.map +1 -0
  7. package/dest/client/factory.js +37 -0
  8. package/dest/client/index.d.ts +3 -0
  9. package/dest/client/index.d.ts.map +1 -0
  10. package/dest/client/index.js +2 -0
  11. package/dest/client/p2p_client.d.ts +314 -0
  12. package/dest/client/p2p_client.d.ts.map +1 -0
  13. package/dest/client/p2p_client.js +505 -0
  14. package/dest/config.d.ts +180 -0
  15. package/dest/config.d.ts.map +1 -0
  16. package/dest/config.js +193 -0
  17. package/dest/enr/generate-enr.d.ts +9 -0
  18. package/dest/enr/generate-enr.d.ts.map +1 -0
  19. package/dest/enr/generate-enr.js +30 -0
  20. package/dest/enr/index.d.ts +2 -0
  21. package/dest/enr/index.d.ts.map +1 -0
  22. package/dest/enr/index.js +1 -0
  23. package/dest/errors/reqresp.error.d.ts +28 -0
  24. package/dest/errors/reqresp.error.d.ts.map +1 -0
  25. package/dest/errors/reqresp.error.js +30 -0
  26. package/dest/index.d.ts +8 -0
  27. package/dest/index.d.ts.map +1 -0
  28. package/dest/index.js +7 -0
  29. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +57 -0
  30. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -0
  31. package/dest/mem_pools/attestation_pool/attestation_pool.js +6 -0
  32. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +3 -0
  33. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -0
  34. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +195 -0
  35. package/dest/mem_pools/attestation_pool/index.d.ts +3 -0
  36. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -0
  37. package/dest/mem_pools/attestation_pool/index.js +2 -0
  38. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +22 -0
  39. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -0
  40. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +112 -0
  41. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +17 -0
  42. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -0
  43. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +129 -0
  44. package/dest/mem_pools/attestation_pool/mocks.d.ts +19 -0
  45. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -0
  46. package/dest/mem_pools/attestation_pool/mocks.js +33 -0
  47. package/dest/mem_pools/index.d.ts +4 -0
  48. package/dest/mem_pools/index.d.ts.map +1 -0
  49. package/dest/mem_pools/index.js +1 -0
  50. package/dest/mem_pools/instrumentation.d.ts +30 -0
  51. package/dest/mem_pools/instrumentation.d.ts.map +1 -0
  52. package/dest/mem_pools/instrumentation.js +84 -0
  53. package/dest/mem_pools/interface.d.ts +11 -0
  54. package/dest/mem_pools/interface.d.ts.map +1 -0
  55. package/dest/mem_pools/interface.js +3 -0
  56. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +66 -0
  57. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -0
  58. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +245 -0
  59. package/dest/mem_pools/tx_pool/index.d.ts +4 -0
  60. package/dest/mem_pools/tx_pool/index.d.ts.map +1 -0
  61. package/dest/mem_pools/tx_pool/index.js +3 -0
  62. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +56 -0
  63. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -0
  64. package/dest/mem_pools/tx_pool/memory_tx_pool.js +141 -0
  65. package/dest/mem_pools/tx_pool/priority.d.ts +8 -0
  66. package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -0
  67. package/dest/mem_pools/tx_pool/priority.js +10 -0
  68. package/dest/mem_pools/tx_pool/tx_pool.d.ts +66 -0
  69. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -0
  70. package/dest/mem_pools/tx_pool/tx_pool.js +3 -0
  71. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +7 -0
  72. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -0
  73. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +169 -0
  74. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +8 -0
  75. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -0
  76. package/dest/msg_validators/attestation_validator/attestation_validator.js +19 -0
  77. package/dest/msg_validators/attestation_validator/index.d.ts +2 -0
  78. package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -0
  79. package/dest/msg_validators/attestation_validator/index.js +1 -0
  80. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +8 -0
  81. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -0
  82. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +21 -0
  83. package/dest/msg_validators/block_proposal_validator/index.d.ts +2 -0
  84. package/dest/msg_validators/block_proposal_validator/index.d.ts.map +1 -0
  85. package/dest/msg_validators/block_proposal_validator/index.js +1 -0
  86. package/dest/msg_validators/index.d.ts +4 -0
  87. package/dest/msg_validators/index.d.ts.map +1 -0
  88. package/dest/msg_validators/index.js +3 -0
  89. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +7 -0
  90. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -0
  91. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +31 -0
  92. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +11 -0
  93. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -0
  94. package/dest/msg_validators/tx_validator/block_header_validator.js +26 -0
  95. package/dest/msg_validators/tx_validator/data_validator.d.ts +6 -0
  96. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -0
  97. package/dest/msg_validators/tx_validator/data_validator.js +107 -0
  98. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +12 -0
  99. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -0
  100. package/dest/msg_validators/tx_validator/double_spend_validator.js +41 -0
  101. package/dest/msg_validators/tx_validator/index.d.ts +7 -0
  102. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -0
  103. package/dest/msg_validators/tx_validator/index.js +6 -0
  104. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +10 -0
  105. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -0
  106. package/dest/msg_validators/tx_validator/metadata_validator.js +44 -0
  107. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +9 -0
  108. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -0
  109. package/dest/msg_validators/tx_validator/tx_proof_validator.js +25 -0
  110. package/dest/services/data_store.d.ts +27 -0
  111. package/dest/services/data_store.d.ts.map +1 -0
  112. package/dest/services/data_store.js +188 -0
  113. package/dest/services/discv5/discV5_service.d.ts +42 -0
  114. package/dest/services/discv5/discV5_service.d.ts.map +1 -0
  115. package/dest/services/discv5/discV5_service.js +214 -0
  116. package/dest/services/dummy_service.d.ts +85 -0
  117. package/dest/services/dummy_service.d.ts.map +1 -0
  118. package/dest/services/dummy_service.js +92 -0
  119. package/dest/services/encoding.d.ts +31 -0
  120. package/dest/services/encoding.d.ts.map +1 -0
  121. package/dest/services/encoding.js +66 -0
  122. package/dest/services/gossipsub/scoring.d.ts +7 -0
  123. package/dest/services/gossipsub/scoring.d.ts.map +1 -0
  124. package/dest/services/gossipsub/scoring.js +10 -0
  125. package/dest/services/index.d.ts +3 -0
  126. package/dest/services/index.d.ts.map +1 -0
  127. package/dest/services/index.js +2 -0
  128. package/dest/services/libp2p/libp2p_service.d.ts +186 -0
  129. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -0
  130. package/dest/services/libp2p/libp2p_service.js +712 -0
  131. package/dest/services/peer-manager/metrics.d.ts +12 -0
  132. package/dest/services/peer-manager/metrics.d.ts.map +1 -0
  133. package/dest/services/peer-manager/metrics.js +33 -0
  134. package/dest/services/peer-manager/peer_manager.d.ts +94 -0
  135. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -0
  136. package/dest/services/peer-manager/peer_manager.js +445 -0
  137. package/dest/services/peer-manager/peer_scoring.d.ts +28 -0
  138. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -0
  139. package/dest/services/peer-manager/peer_scoring.js +86 -0
  140. package/dest/services/reqresp/config.d.ts +16 -0
  141. package/dest/services/reqresp/config.d.ts.map +1 -0
  142. package/dest/services/reqresp/config.js +20 -0
  143. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +45 -0
  144. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -0
  145. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +88 -0
  146. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +61 -0
  147. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -0
  148. package/dest/services/reqresp/connection-sampler/connection_sampler.js +181 -0
  149. package/dest/services/reqresp/index.d.ts +6 -0
  150. package/dest/services/reqresp/index.d.ts.map +1 -0
  151. package/dest/services/reqresp/index.js +4 -0
  152. package/dest/services/reqresp/interface.d.ts +116 -0
  153. package/dest/services/reqresp/interface.d.ts.map +1 -0
  154. package/dest/services/reqresp/interface.js +84 -0
  155. package/dest/services/reqresp/metrics.d.ts +15 -0
  156. package/dest/services/reqresp/metrics.d.ts.map +1 -0
  157. package/dest/services/reqresp/metrics.js +55 -0
  158. package/dest/services/reqresp/protocols/block.d.ts +4 -0
  159. package/dest/services/reqresp/protocols/block.d.ts.map +1 -0
  160. package/dest/services/reqresp/protocols/block.js +8 -0
  161. package/dest/services/reqresp/protocols/goodbye.d.ts +51 -0
  162. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -0
  163. package/dest/services/reqresp/protocols/goodbye.js +87 -0
  164. package/dest/services/reqresp/protocols/index.d.ts +9 -0
  165. package/dest/services/reqresp/protocols/index.d.ts.map +1 -0
  166. package/dest/services/reqresp/protocols/index.js +7 -0
  167. package/dest/services/reqresp/protocols/ping.d.ts +9 -0
  168. package/dest/services/reqresp/protocols/ping.d.ts.map +1 -0
  169. package/dest/services/reqresp/protocols/ping.js +7 -0
  170. package/dest/services/reqresp/protocols/status.d.ts +9 -0
  171. package/dest/services/reqresp/protocols/status.d.ts.map +1 -0
  172. package/dest/services/reqresp/protocols/status.js +7 -0
  173. package/dest/services/reqresp/protocols/tx.d.ts +13 -0
  174. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -0
  175. package/dest/services/reqresp/protocols/tx.js +20 -0
  176. package/dest/services/reqresp/rate-limiter/index.d.ts +2 -0
  177. package/dest/services/reqresp/rate-limiter/index.d.ts.map +1 -0
  178. package/dest/services/reqresp/rate-limiter/index.js +1 -0
  179. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +102 -0
  180. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -0
  181. package/dest/services/reqresp/rate-limiter/rate_limiter.js +184 -0
  182. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +3 -0
  183. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -0
  184. package/dest/services/reqresp/rate-limiter/rate_limits.js +54 -0
  185. package/dest/services/reqresp/reqresp.d.ts +166 -0
  186. package/dest/services/reqresp/reqresp.d.ts.map +1 -0
  187. package/dest/services/reqresp/reqresp.js +516 -0
  188. package/dest/services/reqresp/status.d.ts +31 -0
  189. package/dest/services/reqresp/status.d.ts.map +1 -0
  190. package/dest/services/reqresp/status.js +51 -0
  191. package/dest/services/service.d.ts +87 -0
  192. package/dest/services/service.d.ts.map +1 -0
  193. package/dest/services/service.js +5 -0
  194. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +7 -0
  195. package/dest/test-helpers/generate-peer-id-private-keys.d.ts.map +1 -0
  196. package/dest/test-helpers/generate-peer-id-private-keys.js +13 -0
  197. package/dest/test-helpers/get-ports.d.ts +7 -0
  198. package/dest/test-helpers/get-ports.d.ts.map +1 -0
  199. package/dest/test-helpers/get-ports.js +8 -0
  200. package/dest/test-helpers/index.d.ts +6 -0
  201. package/dest/test-helpers/index.d.ts.map +1 -0
  202. package/dest/test-helpers/index.js +5 -0
  203. package/dest/test-helpers/make-enrs.d.ts +16 -0
  204. package/dest/test-helpers/make-enrs.d.ts.map +1 -0
  205. package/dest/test-helpers/make-enrs.js +32 -0
  206. package/dest/test-helpers/make-test-p2p-clients.d.ts +36 -0
  207. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -0
  208. package/dest/test-helpers/make-test-p2p-clients.js +68 -0
  209. package/dest/test-helpers/reqresp-nodes.d.ts +66 -0
  210. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -0
  211. package/dest/test-helpers/reqresp-nodes.js +207 -0
  212. package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -0
  213. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -0
  214. package/dest/testbench/p2p_client_testbench_worker.js +132 -0
  215. package/dest/testbench/parse_log_file.d.ts +2 -0
  216. package/dest/testbench/parse_log_file.d.ts.map +1 -0
  217. package/dest/testbench/parse_log_file.js +131 -0
  218. package/dest/testbench/testbench.d.ts +2 -0
  219. package/dest/testbench/testbench.d.ts.map +1 -0
  220. package/dest/testbench/testbench.js +61 -0
  221. package/dest/testbench/worker_client_manager.d.ts +56 -0
  222. package/dest/testbench/worker_client_manager.d.ts.map +1 -0
  223. package/dest/testbench/worker_client_manager.js +266 -0
  224. package/dest/types/index.d.ts +32 -0
  225. package/dest/types/index.d.ts.map +1 -0
  226. package/dest/types/index.js +28 -0
  227. package/dest/util.d.ts +53 -0
  228. package/dest/util.d.ts.map +1 -0
  229. package/dest/util.js +140 -0
  230. package/dest/versioning.d.ts +12 -0
  231. package/dest/versioning.d.ts.map +1 -0
  232. package/dest/versioning.js +33 -0
  233. package/package.json +127 -0
  234. package/src/bootstrap/bootstrap.ts +146 -0
  235. package/src/client/factory.ts +89 -0
  236. package/src/client/index.ts +2 -0
  237. package/src/client/p2p_client.ts +754 -0
  238. package/src/config.ts +371 -0
  239. package/src/enr/generate-enr.ts +39 -0
  240. package/src/enr/index.ts +1 -0
  241. package/src/errors/reqresp.error.ts +35 -0
  242. package/src/index.ts +7 -0
  243. package/src/mem_pools/attestation_pool/attestation_pool.ts +62 -0
  244. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +230 -0
  245. package/src/mem_pools/attestation_pool/index.ts +2 -0
  246. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +159 -0
  247. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +161 -0
  248. package/src/mem_pools/attestation_pool/mocks.ts +44 -0
  249. package/src/mem_pools/index.ts +3 -0
  250. package/src/mem_pools/instrumentation.ts +126 -0
  251. package/src/mem_pools/interface.ts +12 -0
  252. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +309 -0
  253. package/src/mem_pools/tx_pool/index.ts +3 -0
  254. package/src/mem_pools/tx_pool/memory_tx_pool.ts +174 -0
  255. package/src/mem_pools/tx_pool/priority.ts +13 -0
  256. package/src/mem_pools/tx_pool/tx_pool.ts +76 -0
  257. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +130 -0
  258. package/src/msg_validators/attestation_validator/attestation_validator.ts +26 -0
  259. package/src/msg_validators/attestation_validator/index.ts +1 -0
  260. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +29 -0
  261. package/src/msg_validators/block_proposal_validator/index.ts +1 -0
  262. package/src/msg_validators/index.ts +3 -0
  263. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +32 -0
  264. package/src/msg_validators/tx_validator/block_header_validator.ts +25 -0
  265. package/src/msg_validators/tx_validator/data_validator.ts +106 -0
  266. package/src/msg_validators/tx_validator/double_spend_validator.ts +38 -0
  267. package/src/msg_validators/tx_validator/index.ts +6 -0
  268. package/src/msg_validators/tx_validator/metadata_validator.ts +48 -0
  269. package/src/msg_validators/tx_validator/tx_proof_validator.ts +18 -0
  270. package/src/services/data_store.ts +235 -0
  271. package/src/services/discv5/discV5_service.ts +256 -0
  272. package/src/services/dummy_service.ts +134 -0
  273. package/src/services/encoding.ts +79 -0
  274. package/src/services/gossipsub/scoring.ts +13 -0
  275. package/src/services/index.ts +2 -0
  276. package/src/services/libp2p/libp2p_service.ts +871 -0
  277. package/src/services/peer-manager/metrics.ts +41 -0
  278. package/src/services/peer-manager/peer_manager.ts +530 -0
  279. package/src/services/peer-manager/peer_scoring.ts +105 -0
  280. package/src/services/reqresp/config.ts +35 -0
  281. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +94 -0
  282. package/src/services/reqresp/connection-sampler/connection_sampler.ts +217 -0
  283. package/src/services/reqresp/index.ts +4 -0
  284. package/src/services/reqresp/interface.ts +185 -0
  285. package/src/services/reqresp/metrics.ts +57 -0
  286. package/src/services/reqresp/protocols/block.ts +15 -0
  287. package/src/services/reqresp/protocols/goodbye.ts +101 -0
  288. package/src/services/reqresp/protocols/index.ts +8 -0
  289. package/src/services/reqresp/protocols/ping.ts +8 -0
  290. package/src/services/reqresp/protocols/status.ts +8 -0
  291. package/src/services/reqresp/protocols/tx.ts +29 -0
  292. package/src/services/reqresp/rate-limiter/index.ts +1 -0
  293. package/src/services/reqresp/rate-limiter/rate_limiter.ts +228 -0
  294. package/src/services/reqresp/rate-limiter/rate_limits.ts +55 -0
  295. package/src/services/reqresp/reqresp.ts +661 -0
  296. package/src/services/reqresp/status.ts +59 -0
  297. package/src/services/service.ts +112 -0
  298. package/src/test-helpers/generate-peer-id-private-keys.ts +15 -0
  299. package/src/test-helpers/get-ports.ts +8 -0
  300. package/src/test-helpers/index.ts +5 -0
  301. package/src/test-helpers/make-enrs.ts +44 -0
  302. package/src/test-helpers/make-test-p2p-clients.ts +122 -0
  303. package/src/test-helpers/reqresp-nodes.ts +289 -0
  304. package/src/testbench/README.md +20 -0
  305. package/src/testbench/p2p_client_testbench_worker.ts +152 -0
  306. package/src/testbench/parse_log_file.ts +175 -0
  307. package/src/testbench/testbench.ts +66 -0
  308. package/src/testbench/worker_client_manager.ts +318 -0
  309. package/src/types/index.ts +36 -0
  310. package/src/util.ts +196 -0
  311. package/src/versioning.ts +50 -0
@@ -0,0 +1,871 @@
1
+ import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
+ import { Fr } from '@aztec/foundation/fields';
3
+ import { createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
4
+ import { SerialQueue } from '@aztec/foundation/queue';
5
+ import { RunningPromise } from '@aztec/foundation/running-promise';
6
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
7
+ import type { L2BlockSource } from '@aztec/stdlib/block';
8
+ import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
9
+ import {
10
+ BlockAttestation,
11
+ BlockProposal,
12
+ type Gossipable,
13
+ P2PClientType,
14
+ PeerErrorSeverity,
15
+ TopicTypeMap,
16
+ getTopicTypeForClientType,
17
+ metricsTopicStrToLabels,
18
+ } from '@aztec/stdlib/p2p';
19
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
20
+ import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx';
21
+ import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
22
+
23
+ import type { ENR } from '@chainsafe/enr';
24
+ import {
25
+ type GossipSub,
26
+ type GossipSubComponents,
27
+ type GossipsubMessage,
28
+ gossipsub,
29
+ } from '@chainsafe/libp2p-gossipsub';
30
+ import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
31
+ import { SignaturePolicy } from '@chainsafe/libp2p-gossipsub/types';
32
+ import { noise } from '@chainsafe/libp2p-noise';
33
+ import { yamux } from '@chainsafe/libp2p-yamux';
34
+ import { bootstrap } from '@libp2p/bootstrap';
35
+ import { identify } from '@libp2p/identify';
36
+ import { type Message, type PeerId, TopicValidatorResult } from '@libp2p/interface';
37
+ import type { ConnectionManager } from '@libp2p/interface-internal';
38
+ import '@libp2p/kad-dht';
39
+ import { mplex } from '@libp2p/mplex';
40
+ import { tcp } from '@libp2p/tcp';
41
+ import { createLibp2p } from 'libp2p';
42
+
43
+ import type { P2PConfig } from '../../config.js';
44
+ import type { MemPools } from '../../mem_pools/interface.js';
45
+ import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
46
+ import {
47
+ DataTxValidator,
48
+ DoubleSpendTxValidator,
49
+ MetadataTxValidator,
50
+ TxProofValidator,
51
+ } from '../../msg_validators/tx_validator/index.js';
52
+ import { GossipSubEvent } from '../../types/index.js';
53
+ import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
54
+ import { AztecDatastore } from '../data_store.js';
55
+ import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
56
+ import { gossipScoreThresholds } from '../gossipsub/scoring.js';
57
+ import { PeerManager } from '../peer-manager/peer_manager.js';
58
+ import { PeerScoring } from '../peer-manager/peer_scoring.js';
59
+ import { DEFAULT_SUB_PROTOCOL_VALIDATORS, ReqRespSubProtocol, type SubProtocolMap } from '../reqresp/interface.js';
60
+ import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
61
+ import { pingHandler, reqRespBlockHandler, reqRespTxHandler, statusHandler } from '../reqresp/protocols/index.js';
62
+ import { ReqResp } from '../reqresp/reqresp.js';
63
+ import type { P2PService, PeerDiscoveryService } from '../service.js';
64
+
65
+ interface MessageValidator {
66
+ validator: {
67
+ validateTx(tx: Tx): Promise<TxValidationResult>;
68
+ };
69
+ severity: PeerErrorSeverity;
70
+ }
71
+
72
+ interface ValidationResult {
73
+ name: string;
74
+ isValid: TxValidationResult;
75
+ severity: PeerErrorSeverity;
76
+ }
77
+
78
+ type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
79
+
80
+ /**
81
+ * Lib P2P implementation of the P2PService interface.
82
+ */
83
+ export class LibP2PService<T extends P2PClientType> extends WithTracer implements P2PService {
84
+ private jobQueue: SerialQueue = new SerialQueue();
85
+ private peerManager: PeerManager;
86
+ private discoveryRunningPromise?: RunningPromise;
87
+
88
+ // Message validators
89
+ private attestationValidator: AttestationValidator;
90
+ private blockProposalValidator: BlockProposalValidator;
91
+
92
+ // Request and response sub service
93
+ public reqresp: ReqResp;
94
+
95
+ /**
96
+ * Callback for when a block is received from a peer.
97
+ * @param block - The block received from the peer.
98
+ * @returns The attestation for the block, if any.
99
+ */
100
+ private blockReceivedCallback: (block: BlockProposal) => Promise<BlockAttestation | undefined>;
101
+
102
+ constructor(
103
+ private clientType: T,
104
+ private config: P2PConfig,
105
+ private node: PubSubLibp2p,
106
+ private peerDiscoveryService: PeerDiscoveryService,
107
+ private mempools: MemPools<T>,
108
+ private l2BlockSource: L2BlockSource,
109
+ epochCache: EpochCacheInterface,
110
+ private proofVerifier: ClientProtocolCircuitVerifier,
111
+ private worldStateSynchronizer: WorldStateSynchronizer,
112
+ telemetry: TelemetryClient,
113
+ private logger = createLogger('p2p:libp2p_service'),
114
+ ) {
115
+ super(telemetry, 'LibP2PService');
116
+
117
+ const peerScoring = new PeerScoring(config);
118
+ this.reqresp = new ReqResp(config, node, peerScoring);
119
+
120
+ this.peerManager = new PeerManager(
121
+ node,
122
+ peerDiscoveryService,
123
+ config,
124
+ telemetry,
125
+ createLogger(`${logger.module}:peer_manager`),
126
+ peerScoring,
127
+ this.reqresp,
128
+ );
129
+
130
+ // Update gossipsub score params
131
+ this.node.services.pubsub.score.params.appSpecificScore = (peerId: string) => {
132
+ return this.peerManager.getPeerScore(peerId);
133
+ };
134
+ this.node.services.pubsub.score.params.appSpecificWeight = 10;
135
+
136
+ this.attestationValidator = new AttestationValidator(epochCache);
137
+ this.blockProposalValidator = new BlockProposalValidator(epochCache);
138
+
139
+ this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation | undefined> => {
140
+ this.logger.debug(
141
+ `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`,
142
+ { p2pMessageIdentifier: await block.p2pMessageIdentifier() },
143
+ );
144
+ return undefined;
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Creates an instance of the LibP2P service.
150
+ * @param config - The configuration to use when creating the service.
151
+ * @param txPool - The transaction pool to be accessed by the service.
152
+ * @returns The new service.
153
+ */
154
+ public static async new<T extends P2PClientType>(
155
+ clientType: T,
156
+ config: P2PConfig,
157
+ peerDiscoveryService: PeerDiscoveryService,
158
+ peerId: PeerId,
159
+ mempools: MemPools<T>,
160
+ l2BlockSource: L2BlockSource,
161
+ epochCache: EpochCacheInterface,
162
+ proofVerifier: ClientProtocolCircuitVerifier,
163
+ worldStateSynchronizer: WorldStateSynchronizer,
164
+ store: AztecAsyncKVStore,
165
+ telemetry: TelemetryClient,
166
+ logger = createLogger('p2p:libp2p_service'),
167
+ ) {
168
+ const { tcpListenAddress, tcpAnnounceAddress, maxPeerCount } = config;
169
+ const bindAddrTcp = convertToMultiaddr(tcpListenAddress, 'tcp');
170
+ // We know tcpAnnounceAddress cannot be null here because we set it or throw when setting up the service.
171
+ const announceAddrTcp = convertToMultiaddr(tcpAnnounceAddress!, 'tcp');
172
+
173
+ const datastore = new AztecDatastore(store);
174
+
175
+ const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
176
+
177
+ // If bootstrap nodes are provided, also provide them to the p2p service
178
+ const peerDiscovery = [];
179
+ if (peerDiscoveryService.bootstrapNodes.length > 0) {
180
+ peerDiscovery.push(bootstrap({ list: peerDiscoveryService.bootstrapNodes }));
181
+ }
182
+
183
+ const node = await createLibp2p({
184
+ start: false,
185
+ peerId,
186
+ addresses: {
187
+ listen: [bindAddrTcp],
188
+ announce: [announceAddrTcp],
189
+ },
190
+ transports: [
191
+ tcp({
192
+ maxConnections: config.maxPeerCount,
193
+ // socket option: the maximum length of the queue of pending connections
194
+ // https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
195
+ // it's not safe if we increase this number
196
+ backlog: 5,
197
+ closeServerOnMaxConnections: {
198
+ closeAbove: maxPeerCount ?? Infinity,
199
+ listenBelow: maxPeerCount ?? Infinity,
200
+ },
201
+ }),
202
+ ],
203
+ datastore,
204
+ peerDiscovery,
205
+ streamMuxers: [mplex(), yamux()],
206
+ connectionEncryption: [noise()],
207
+ connectionManager: {
208
+ minConnections: 0,
209
+
210
+ maxParallelDials: 100,
211
+ dialTimeout: 30_000,
212
+ maxPeerAddrsToDial: 5,
213
+ maxIncomingPendingConnections: 5,
214
+ },
215
+ services: {
216
+ identify: identify({
217
+ protocolPrefix: 'aztec',
218
+ }),
219
+ pubsub: gossipsub({
220
+ debugName: 'gossipsub',
221
+ globalSignaturePolicy: SignaturePolicy.StrictNoSign,
222
+ allowPublishToZeroTopicPeers: true,
223
+ floodPublish: config.gossipsubFloodPublish,
224
+ D: config.gossipsubD,
225
+ Dlo: config.gossipsubDlo,
226
+ Dhi: config.gossipsubDhi,
227
+ Dlazy: config.gossipsubDLazy,
228
+ heartbeatInterval: config.gossipsubInterval,
229
+ mcacheLength: config.gossipsubMcacheLength,
230
+ mcacheGossip: config.gossipsubMcacheGossip,
231
+ msgIdFn: getMsgIdFn,
232
+ msgIdToStrFn: msgIdToStrFn,
233
+ fastMsgIdFn: fastMsgIdFn,
234
+ dataTransform: new SnappyTransform(),
235
+ metricsRegister: otelMetricsAdapter,
236
+ metricsTopicStrToLabel: metricsTopicStrToLabels(),
237
+ asyncValidation: true,
238
+ scoreThresholds: gossipScoreThresholds,
239
+ scoreParams: createPeerScoreParams({
240
+ // IPColocation factor can be disabled for local testing - default to -5
241
+ IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
242
+ topics: {
243
+ [Tx.p2pTopic]: createTopicScoreParams({
244
+ topicWeight: 1,
245
+ invalidMessageDeliveriesWeight: -20,
246
+ invalidMessageDeliveriesDecay: 0.5,
247
+ }),
248
+ [BlockAttestation.p2pTopic]: createTopicScoreParams({
249
+ topicWeight: 1,
250
+ invalidMessageDeliveriesWeight: -20,
251
+ invalidMessageDeliveriesDecay: 0.5,
252
+ }),
253
+ [BlockProposal.p2pTopic]: createTopicScoreParams({
254
+ topicWeight: 1,
255
+ invalidMessageDeliveriesWeight: -20,
256
+ invalidMessageDeliveriesDecay: 0.5,
257
+ }),
258
+ },
259
+ }),
260
+ }) as (components: GossipSubComponents) => GossipSub,
261
+ components: (components: { connectionManager: ConnectionManager }) => ({
262
+ connectionManager: components.connectionManager,
263
+ }),
264
+ },
265
+ logger: createLibp2pComponentLogger(logger.module),
266
+ });
267
+
268
+ return new LibP2PService(
269
+ clientType,
270
+ config,
271
+ node,
272
+ peerDiscoveryService,
273
+ mempools,
274
+ l2BlockSource,
275
+ epochCache,
276
+ proofVerifier,
277
+ worldStateSynchronizer,
278
+ telemetry,
279
+ logger,
280
+ );
281
+ }
282
+
283
+ /**
284
+ * Starts the LibP2P service.
285
+ * @returns An empty promise.
286
+ */
287
+ public async start() {
288
+ // Check if service is already started
289
+ if (this.node.status === 'started') {
290
+ throw new Error('P2P service already started');
291
+ }
292
+
293
+ // Get listen & announce addresses for logging
294
+ const { tcpListenAddress, tcpAnnounceAddress } = this.config;
295
+ if (!tcpAnnounceAddress) {
296
+ throw new Error('Announce address not provided.');
297
+ }
298
+ const announceTcpMultiaddr = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
299
+
300
+ // Start job queue, peer discovery service and libp2p node
301
+ this.jobQueue.start();
302
+ await this.peerDiscoveryService.start();
303
+ await this.node.start();
304
+
305
+ // Subscribe to standard GossipSub topics by default
306
+ for (const topic of getTopicTypeForClientType(this.clientType)) {
307
+ this.subscribeToTopic(TopicTypeMap[topic].p2pTopic);
308
+ }
309
+
310
+ // Create request response protocol handlers
311
+ const txHandler = reqRespTxHandler(this.mempools);
312
+ const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
313
+ const blockHandler = reqRespBlockHandler(this.l2BlockSource);
314
+
315
+ const requestResponseHandlers = {
316
+ [ReqRespSubProtocol.PING]: pingHandler,
317
+ [ReqRespSubProtocol.STATUS]: statusHandler,
318
+ [ReqRespSubProtocol.TX]: txHandler.bind(this),
319
+ [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
320
+ [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
321
+ };
322
+
323
+ // add GossipSub listener
324
+ this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
325
+
326
+ // Start running promise for peer discovery
327
+ this.discoveryRunningPromise = new RunningPromise(
328
+ () => this.peerManager.heartbeat(),
329
+ this.logger,
330
+ this.config.peerCheckIntervalMS,
331
+ );
332
+ this.discoveryRunningPromise.start();
333
+
334
+ // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
335
+ const reqrespSubProtocolValidators = {
336
+ ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
337
+ // TODO(#11336): A request validator for blocks
338
+ [ReqRespSubProtocol.TX]: this.validateRequestedTx.bind(this),
339
+ };
340
+ await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
341
+ this.logger.info(`Started P2P service`, {
342
+ listen: tcpListenAddress,
343
+ announce: announceTcpMultiaddr,
344
+ peerId: this.node.peerId.toString(),
345
+ });
346
+ }
347
+
348
+ /**
349
+ * Stops the LibP2P service.
350
+ * @returns An empty promise.
351
+ */
352
+ public async stop() {
353
+ // Remove gossip sub listener
354
+ this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
355
+
356
+ // Stop peer manager
357
+ this.logger.debug('Stopping peer manager...');
358
+ await this.peerManager.stop();
359
+
360
+ this.logger.debug('Stopping job queue...');
361
+ await this.jobQueue.end();
362
+ this.logger.debug('Stopping running promise...');
363
+ await this.discoveryRunningPromise?.stop();
364
+ this.logger.debug('Stopping peer discovery service...');
365
+ await this.peerDiscoveryService.stop();
366
+ this.logger.debug('Request response service stopped...');
367
+ await this.reqresp.stop();
368
+ this.logger.debug('Stopping LibP2P...');
369
+ await this.stopLibP2P();
370
+ this.logger.info('LibP2P service stopped');
371
+ }
372
+
373
+ public getPeers(includePending?: boolean): PeerInfo[] {
374
+ return this.peerManager.getPeers(includePending);
375
+ }
376
+
377
+ private handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
378
+ this.logger.trace(`Received PUBSUB message.`);
379
+
380
+ const safeJob = async () => {
381
+ try {
382
+ await this.handleNewGossipMessage(e.detail.msg, e.detail.msgId, e.detail.propagationSource);
383
+ } catch (err) {
384
+ this.logger.error(`Error handling gossipsub message: ${err}`);
385
+ }
386
+ };
387
+ setImmediate(() => void safeJob());
388
+ }
389
+
390
+ /**
391
+ * Send Request via the ReqResp service
392
+ * The subprotocol defined will determine the request and response types
393
+ *
394
+ * See the subProtocolMap for the mapping of subprotocols to request/response types in `interface.ts`
395
+ *
396
+ * @param protocol The request response protocol to use
397
+ * @param request The request type to send
398
+ * @returns
399
+ */
400
+ sendRequest<SubProtocol extends ReqRespSubProtocol>(
401
+ protocol: SubProtocol,
402
+ request: InstanceType<SubProtocolMap[SubProtocol]['request']>,
403
+ ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined> {
404
+ return this.reqresp.sendRequest(protocol, request);
405
+ }
406
+
407
+ /**
408
+ * Send a batch of requests to peers, and return the responses
409
+ * @param protocol - The request response protocol to use
410
+ * @param requests - The requests to send to the peers
411
+ * @returns The responses to the requests
412
+ */
413
+ sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
414
+ protocol: SubProtocol,
415
+ requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
416
+ ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[] | undefined> {
417
+ return this.reqresp.sendBatchRequest(protocol, requests);
418
+ }
419
+
420
+ /**
421
+ * Get the ENR of the node
422
+ * @returns The ENR of the node
423
+ */
424
+ public getEnr(): ENR | undefined {
425
+ return this.peerDiscoveryService.getEnr();
426
+ }
427
+
428
+ public registerBlockReceivedCallback(callback: (block: BlockProposal) => Promise<BlockAttestation | undefined>) {
429
+ this.blockReceivedCallback = callback;
430
+ this.logger.verbose('Block received callback registered');
431
+ }
432
+
433
+ /**
434
+ * Subscribes to a topic.
435
+ * @param topic - The topic to subscribe to.
436
+ */
437
+ private subscribeToTopic(topic: string) {
438
+ if (!this.node.services.pubsub) {
439
+ throw new Error('Pubsub service not available.');
440
+ }
441
+ void this.node.services.pubsub.subscribe(topic);
442
+ }
443
+
444
+ /**
445
+ * Publishes data to a topic.
446
+ * @param topic - The topic to publish to.
447
+ * @param data - The data to publish.
448
+ * @returns The number of recipients the data was sent to.
449
+ */
450
+ private async publishToTopic(topic: string, data: Uint8Array) {
451
+ if (!this.node.services.pubsub) {
452
+ throw new Error('Pubsub service not available.');
453
+ }
454
+ const result = await this.node.services.pubsub.publish(topic, data);
455
+
456
+ return result.recipients.length;
457
+ }
458
+
459
+ /**
460
+ * Handles a new gossip message that was received by the client.
461
+ * @param topic - The message's topic.
462
+ * @param data - The message data
463
+ */
464
+ private async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
465
+ if (msg.topic === Tx.p2pTopic) {
466
+ await this.handleGossipedTx(msg, msgId, source);
467
+ }
468
+ if (msg.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) {
469
+ await this.processAttestationFromPeer(msg, msgId, source);
470
+ }
471
+ if (msg.topic == BlockProposal.p2pTopic) {
472
+ await this.processBlockFromPeer(msg, msgId, source);
473
+ }
474
+
475
+ return;
476
+ }
477
+
478
+ private async validateReceivedMessage<T>(
479
+ validationFunc: () => Promise<{ result: boolean; obj: T }>,
480
+ msgId: string,
481
+ source: PeerId,
482
+ ): Promise<{ result: boolean; obj: T | undefined }> {
483
+ let resultAndObj: { result: boolean; obj: T | undefined } = { result: false, obj: undefined };
484
+ try {
485
+ resultAndObj = await validationFunc();
486
+ } catch (err) {
487
+ this.logger.error(`Error deserialising and validating message `, err);
488
+ }
489
+
490
+ this.node.services.pubsub.reportMessageValidationResult(
491
+ msgId,
492
+ source.toString(),
493
+ resultAndObj.result && resultAndObj.obj ? TopicValidatorResult.Accept : TopicValidatorResult.Reject,
494
+ );
495
+ return resultAndObj;
496
+ }
497
+
498
+ private async handleGossipedTx(msg: Message, msgId: string, source: PeerId) {
499
+ const validationFunc = async () => {
500
+ const tx = Tx.fromBuffer(Buffer.from(msg.data));
501
+ const result = await this.validatePropagatedTx(tx, source);
502
+ return { result, obj: tx };
503
+ };
504
+
505
+ const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source);
506
+ if (!result || !tx) {
507
+ return;
508
+ }
509
+ const txHash = await tx.getTxHash();
510
+ const txHashString = txHash.toString();
511
+ this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()}.`);
512
+ await this.mempools.txPool.addTxs([tx]);
513
+ }
514
+
515
+ /**Process Attestation From Peer
516
+ * When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
517
+ *
518
+ * @param attestation - The attestation to process.
519
+ */
520
+ private async processAttestationFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
521
+ const validationFunc = async () => {
522
+ const attestation = BlockAttestation.fromBuffer(Buffer.from(msg.data));
523
+ const result = await this.validateAttestation(source, attestation);
524
+ this.logger.trace(`validatePropagatedAttestation: ${result}`, {
525
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString(),
526
+ [Attributes.P2P_ID]: source.toString(),
527
+ });
528
+ return { result, obj: attestation };
529
+ };
530
+
531
+ const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
532
+ validationFunc,
533
+ msgId,
534
+ source,
535
+ );
536
+ if (!result || !attestation) {
537
+ return;
538
+ }
539
+ this.logger.debug(
540
+ `Received attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()} from external peer.`,
541
+ {
542
+ p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
543
+ slot: attestation.slotNumber.toNumber(),
544
+ archive: attestation.archive.toString(),
545
+ block: attestation.blockNumber.toNumber(),
546
+ },
547
+ );
548
+ await this.mempools.attestationPool!.addAttestations([attestation]);
549
+ }
550
+
551
+ private async processBlockFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
552
+ const validationFunc = async () => {
553
+ const block = BlockProposal.fromBuffer(Buffer.from(msg.data));
554
+ const result = await this.validateBlockProposal(source, block);
555
+ this.logger.trace(`validatePropagatedBlock: ${result}`, {
556
+ [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
557
+ [Attributes.P2P_ID]: source.toString(),
558
+ });
559
+ return { result, obj: block };
560
+ };
561
+
562
+ const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(validationFunc, msgId, source);
563
+ if (!result || !block) {
564
+ return;
565
+ }
566
+ await this.processValidBlockProposal(block);
567
+ }
568
+
569
+ // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
570
+ @trackSpan('Libp2pService.processValidBlockProposal', async block => ({
571
+ [Attributes.BLOCK_NUMBER]: block.blockNumber.toNumber(),
572
+ [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
573
+ [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
574
+ [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then(i => i.toString()),
575
+ }))
576
+ private async processValidBlockProposal(block: BlockProposal) {
577
+ this.logger.verbose(
578
+ `Received block ${block.blockNumber.toNumber()} for slot ${block.slotNumber.toNumber()} from external peer.`,
579
+ {
580
+ p2pMessageIdentifier: await block.p2pMessageIdentifier(),
581
+ slot: block.slotNumber.toNumber(),
582
+ archive: block.archive.toString(),
583
+ block: block.blockNumber.toNumber(),
584
+ },
585
+ );
586
+ const attestation = await this.blockReceivedCallback(block);
587
+
588
+ // TODO: fix up this pattern - the abstraction is not nice
589
+ // The attestation can be undefined if no handler is registered / the validator deems the block invalid
590
+ if (attestation != undefined) {
591
+ this.logger.verbose(
592
+ `Broadcasting attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()}`,
593
+ {
594
+ p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
595
+ slot: attestation.slotNumber.toNumber(),
596
+ archive: attestation.archive.toString(),
597
+ block: attestation.blockNumber.toNumber(),
598
+ },
599
+ );
600
+ await this.broadcastAttestation(attestation);
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Broadcast an attestation to all peers.
606
+ * @param attestation - The attestation to broadcast.
607
+ */
608
+ @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
609
+ [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
610
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
611
+ [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
612
+ [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
613
+ }))
614
+ private async broadcastAttestation(attestation: BlockAttestation) {
615
+ await this.propagate(attestation);
616
+ }
617
+
618
+ /**
619
+ * Propagates provided message to peers.
620
+ * @param message - The message to propagate.
621
+ */
622
+ public async propagate<T extends Gossipable>(message: T) {
623
+ const p2pMessageIdentifier = await message.p2pMessageIdentifier();
624
+ this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
625
+ void this.jobQueue
626
+ .put(async () => {
627
+ await this.sendToPeers(message);
628
+ })
629
+ .catch(error => {
630
+ this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
631
+ });
632
+ }
633
+
634
+ /**
635
+ * Validate a tx that has been requested from a peer.
636
+ *
637
+ * The core component of this validator is that the tx hash MUST match the requested tx hash,
638
+ * In order to perform this check, the tx proof must be verified.
639
+ *
640
+ * Note: This function is called from within `ReqResp.sendRequest` as part of the
641
+ * ReqRespSubProtocol.TX subprotocol validation.
642
+ *
643
+ * @param requestedTxHash - The hash of the tx that was requested.
644
+ * @param responseTx - The tx that was received as a response to the request.
645
+ * @param peerId - The peer ID of the peer that sent the tx.
646
+ * @returns True if the tx is valid, false otherwise.
647
+ */
648
+ @trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
649
+ [Attributes.TX_HASH]: requestedTxHash.toString(),
650
+ }))
651
+ private async validateRequestedTx(requestedTxHash: TxHash, responseTx: Tx, peerId: PeerId): Promise<boolean> {
652
+ const proofValidator = new TxProofValidator(this.proofVerifier);
653
+ const validProof = await proofValidator.validateTx(responseTx);
654
+
655
+ // If the node returns the wrong data, we penalize it
656
+ if (!requestedTxHash.equals(await responseTx.getTxHash())) {
657
+ // Returning the wrong data is a low tolerance error
658
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
659
+ return false;
660
+ }
661
+
662
+ if (validProof.result === 'invalid') {
663
+ // If the proof is invalid, but the txHash is correct, then this is an active attack and we severly punish
664
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
665
+ return false;
666
+ }
667
+
668
+ return true;
669
+ }
670
+
671
+ @trackSpan('Libp2pService.validatePropagatedTx', async tx => ({
672
+ [Attributes.TX_HASH]: (await tx.getTxHash()).toString(),
673
+ }))
674
+ private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
675
+ const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1;
676
+ const messageValidators = this.createMessageValidators(blockNumber);
677
+ const outcome = await this.runValidations(tx, messageValidators);
678
+
679
+ if (outcome.allPassed) {
680
+ return true;
681
+ }
682
+ const { name } = outcome.failure;
683
+ let { severity } = outcome.failure;
684
+
685
+ // Double spend validator has a special case handler
686
+ if (name === 'doubleSpendValidator') {
687
+ severity = await this.handleDoubleSpendFailure(tx, blockNumber);
688
+ }
689
+
690
+ this.peerManager.penalizePeer(peerId, severity);
691
+ return false;
692
+ }
693
+
694
+ /**
695
+ * Create message validators for the given block number.
696
+ *
697
+ * Each validator is a pair of a validator and a severity.
698
+ * If a validator fails, the peer is penalized with the severity of the validator.
699
+ *
700
+ * @param blockNumber - The block number to create validators for.
701
+ * @returns The message validators.
702
+ */
703
+ private createMessageValidators(blockNumber: number): Record<string, MessageValidator> {
704
+ return {
705
+ dataValidator: {
706
+ validator: new DataTxValidator(),
707
+ severity: PeerErrorSeverity.HighToleranceError,
708
+ },
709
+ metadataValidator: {
710
+ validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)),
711
+ severity: PeerErrorSeverity.HighToleranceError,
712
+ },
713
+ proofValidator: {
714
+ validator: new TxProofValidator(this.proofVerifier),
715
+ severity: PeerErrorSeverity.MidToleranceError,
716
+ },
717
+ doubleSpendValidator: {
718
+ validator: new DoubleSpendTxValidator({
719
+ nullifiersExist: async (nullifiers: Buffer[]) => {
720
+ const merkleTree = this.worldStateSynchronizer.getCommitted();
721
+ const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
722
+ return indices.map(index => index !== undefined);
723
+ },
724
+ }),
725
+ severity: PeerErrorSeverity.HighToleranceError,
726
+ },
727
+ };
728
+ }
729
+
730
+ /**
731
+ * Run validations on a tx.
732
+ * @param tx - The tx to validate.
733
+ * @param messageValidators - The message validators to run.
734
+ * @returns The validation outcome.
735
+ */
736
+ private async runValidations(
737
+ tx: Tx,
738
+ messageValidators: Record<string, MessageValidator>,
739
+ ): Promise<ValidationOutcome> {
740
+ const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
741
+ const { result } = await validator.validateTx(tx);
742
+ return { name, isValid: result === 'valid', severity };
743
+ });
744
+
745
+ // A promise that resolves when all validations have been run
746
+ const allValidations = Promise.all(validationPromises);
747
+
748
+ // A promise that resolves when the first validation fails
749
+ const firstFailure = Promise.race(
750
+ validationPromises.map(async promise => {
751
+ const result = await promise;
752
+ return result.isValid ? new Promise(() => {}) : result;
753
+ }),
754
+ );
755
+
756
+ // Wait for the first validation to fail or all validations to pass
757
+ const result = await Promise.race([
758
+ allValidations.then(() => ({ allPassed: true as const })),
759
+ firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })),
760
+ ]);
761
+
762
+ // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
763
+ return result;
764
+ }
765
+
766
+ /**
767
+ * Handle a double spend failure.
768
+ *
769
+ * Double spend failures are managed on their own because they are a special case.
770
+ * We must check if the double spend is recent or old, if it is past a threshold, then we heavily penalize the peer.
771
+ *
772
+ * @param tx - The tx that failed the double spend validator.
773
+ * @param blockNumber - The block number of the tx.
774
+ * @param peerId - The peer ID of the peer that sent the tx.
775
+ * @returns Severity
776
+ */
777
+ private async handleDoubleSpendFailure(tx: Tx, blockNumber: number): Promise<PeerErrorSeverity> {
778
+ if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
779
+ return PeerErrorSeverity.HighToleranceError;
780
+ }
781
+
782
+ const snapshotValidator = new DoubleSpendTxValidator({
783
+ nullifiersExist: async (nullifiers: Buffer[]) => {
784
+ const merkleTree = this.worldStateSynchronizer.getSnapshot(
785
+ blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
786
+ );
787
+ const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
788
+ return indices.map(index => index !== undefined);
789
+ },
790
+ });
791
+
792
+ const validSnapshot = await snapshotValidator.validateTx(tx);
793
+ if (validSnapshot.result !== 'valid') {
794
+ return PeerErrorSeverity.LowToleranceError;
795
+ }
796
+
797
+ return PeerErrorSeverity.HighToleranceError;
798
+ }
799
+
800
+ /**
801
+ * Validate an attestation.
802
+ *
803
+ * @param attestation - The attestation to validate.
804
+ * @returns True if the attestation is valid, false otherwise.
805
+ */
806
+ @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
807
+ [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
808
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
809
+ [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
810
+ [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
811
+ }))
812
+ public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
813
+ const severity = await this.attestationValidator.validate(attestation);
814
+ if (severity) {
815
+ this.peerManager.penalizePeer(peerId, severity);
816
+ return false;
817
+ }
818
+
819
+ return true;
820
+ }
821
+
822
+ /**
823
+ * Validate a block proposal.
824
+ *
825
+ * @param block - The block proposal to validate.
826
+ * @returns True if the block proposal is valid, false otherwise.
827
+ */
828
+ @trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
829
+ [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
830
+ }))
831
+ public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
832
+ const severity = await this.blockProposalValidator.validate(block);
833
+ if (severity) {
834
+ this.peerManager.penalizePeer(peerId, severity);
835
+ return false;
836
+ }
837
+
838
+ return true;
839
+ }
840
+
841
+ public getPeerScore(peerId: PeerId): number {
842
+ return this.node.services.pubsub.score.score(peerId.toString());
843
+ }
844
+
845
+ private async sendToPeers<T extends Gossipable>(message: T) {
846
+ const parent = message.constructor as typeof Gossipable;
847
+
848
+ const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
849
+ this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
850
+
851
+ const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
852
+ this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
853
+ p2pMessageIdentifier: identifier,
854
+ sourcePeer: this.node.peerId.toString(),
855
+ });
856
+ }
857
+
858
+ // Libp2p seems to hang sometimes if new peers are initiating connections.
859
+ private async stopLibP2P() {
860
+ const TIMEOUT_MS = 5000; // 5 seconds timeout
861
+ const timeout = new Promise((_resolve, reject) => {
862
+ setTimeout(() => reject(new Error('Timeout during libp2p.stop()')), TIMEOUT_MS);
863
+ });
864
+ try {
865
+ await Promise.race([this.node.stop(), timeout]);
866
+ this.logger.debug('LibP2P stopped');
867
+ } catch (error) {
868
+ this.logger.error('Error during stop or timeout:', error);
869
+ }
870
+ }
871
+ }