@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,661 @@
1
+ // @attribution: lodestar impl for inspiration
2
+ import { type Logger, createLogger } from '@aztec/foundation/log';
3
+ import { executeTimeout } from '@aztec/foundation/timer';
4
+ import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
5
+ import { Attributes, type TelemetryClient, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
6
+
7
+ import type { IncomingStreamData, PeerId, Stream } from '@libp2p/interface';
8
+ import { pipe } from 'it-pipe';
9
+ import type { Libp2p } from 'libp2p';
10
+ import type { Uint8ArrayList } from 'uint8arraylist';
11
+
12
+ import {
13
+ CollectiveReqRespTimeoutError,
14
+ IndividualReqRespTimeoutError,
15
+ InvalidResponseError,
16
+ } from '../../errors/reqresp.error.js';
17
+ import { SnappyTransform } from '../encoding.js';
18
+ import type { PeerScoring } from '../peer-manager/peer_scoring.js';
19
+ import type { P2PReqRespConfig } from './config.js';
20
+ import { BatchConnectionSampler } from './connection-sampler/batch_connection_sampler.js';
21
+ import { ConnectionSampler } from './connection-sampler/connection_sampler.js';
22
+ import {
23
+ DEFAULT_SUB_PROTOCOL_HANDLERS,
24
+ DEFAULT_SUB_PROTOCOL_VALIDATORS,
25
+ type ReqRespResponse,
26
+ ReqRespSubProtocol,
27
+ type ReqRespSubProtocolHandlers,
28
+ type ReqRespSubProtocolValidators,
29
+ type SubProtocolMap,
30
+ subProtocolMap,
31
+ } from './interface.js';
32
+ import { ReqRespMetrics } from './metrics.js';
33
+ import {
34
+ RateLimitStatus,
35
+ RequestResponseRateLimiter,
36
+ prettyPrintRateLimitStatus,
37
+ } from './rate-limiter/rate_limiter.js';
38
+ import { ReqRespStatus, ReqRespStatusError, parseStatusChunk, prettyPrintReqRespStatus } from './status.js';
39
+
40
+ /**
41
+ * The Request Response Service
42
+ *
43
+ * It allows nodes to request specific information from their peers, its use case covers recovering
44
+ * information that was missed during a syncronisation or a gossip event.
45
+ *
46
+ * This service implements the request response sub protocol, it is heavily inspired from
47
+ * ethereum implementations of the same name.
48
+ *
49
+ * Note, responses get compressed in streamHandler
50
+ * so they get decompressed in readMessage
51
+ *
52
+ * see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md#the-reqresp-domain
53
+ */
54
+ export class ReqResp {
55
+ protected readonly logger: Logger;
56
+
57
+ private overallRequestTimeoutMs: number;
58
+ private individualRequestTimeoutMs: number;
59
+
60
+ // Warning, if the `start` function is not called as the parent class constructor, then the default sub protocol handlers will be used ( not good )
61
+ private subProtocolHandlers: ReqRespSubProtocolHandlers = DEFAULT_SUB_PROTOCOL_HANDLERS;
62
+ private subProtocolValidators: ReqRespSubProtocolValidators = DEFAULT_SUB_PROTOCOL_VALIDATORS;
63
+
64
+ private connectionSampler: ConnectionSampler;
65
+ private rateLimiter: RequestResponseRateLimiter;
66
+
67
+ private snappyTransform: SnappyTransform;
68
+
69
+ private metrics: ReqRespMetrics;
70
+
71
+ constructor(
72
+ config: P2PReqRespConfig,
73
+ private libp2p: Libp2p,
74
+ private peerScoring: PeerScoring,
75
+ telemetryClient: TelemetryClient = getTelemetryClient(),
76
+ ) {
77
+ this.logger = createLogger('p2p:reqresp');
78
+
79
+ this.overallRequestTimeoutMs = config.overallRequestTimeoutMs;
80
+ this.individualRequestTimeoutMs = config.individualRequestTimeoutMs;
81
+
82
+ this.rateLimiter = new RequestResponseRateLimiter(peerScoring);
83
+
84
+ // Connection sampler is used to sample our connected peers
85
+ this.connectionSampler = new ConnectionSampler(libp2p);
86
+
87
+ this.snappyTransform = new SnappyTransform();
88
+ this.metrics = new ReqRespMetrics(telemetryClient);
89
+ }
90
+
91
+ get tracer() {
92
+ return this.metrics.tracer;
93
+ }
94
+
95
+ /**
96
+ * Start the reqresp service
97
+ */
98
+ async start(subProtocolHandlers: ReqRespSubProtocolHandlers, subProtocolValidators: ReqRespSubProtocolValidators) {
99
+ this.subProtocolHandlers = subProtocolHandlers;
100
+ this.subProtocolValidators = subProtocolValidators;
101
+
102
+ // Register all protocol handlers
103
+ for (const subProtocol of Object.keys(this.subProtocolHandlers)) {
104
+ await this.libp2p.handle(
105
+ subProtocol,
106
+ (data: IncomingStreamData) =>
107
+ void this.streamHandler(subProtocol as ReqRespSubProtocol, data).catch(err =>
108
+ this.logger.error(`Error on libp2p subprotocol ${subProtocol} handler`, err),
109
+ ),
110
+ );
111
+ }
112
+ this.rateLimiter.start();
113
+ }
114
+
115
+ /**
116
+ * Stop the reqresp service
117
+ */
118
+ async stop() {
119
+ // Unregister handlers in parallel
120
+ const unregisterPromises = Object.keys(this.subProtocolHandlers).map(protocol => this.libp2p.unhandle(protocol));
121
+ await Promise.all(unregisterPromises);
122
+
123
+ // Close connection sampler
124
+ await this.connectionSampler.stop();
125
+ this.logger.debug('ReqResp: Connection sampler stopped');
126
+
127
+ // Close streams in parallel
128
+ const closeStreamPromises = this.libp2p.getConnections().map(connection => connection.close());
129
+ await Promise.all(closeStreamPromises);
130
+ this.logger.debug('ReqResp: All active streams closed');
131
+
132
+ this.rateLimiter.stop();
133
+ this.logger.debug('ReqResp: Rate limiter stopped');
134
+
135
+ // NOTE: We assume libp2p instance is managed by the caller
136
+ }
137
+
138
+ /**
139
+ * Send a request to peers, returns the first response
140
+ *
141
+ * @param subProtocol - The protocol being requested
142
+ * @param request - The request to send
143
+ * @returns - The response from the peer, otherwise undefined
144
+ *
145
+ * @description
146
+ * This method attempts to send a request to all active peers using the specified sub-protocol.
147
+ * It opens a stream with each peer, sends the request, and awaits a response.
148
+ * If a valid response is received, it returns the response; otherwise, it continues to the next peer.
149
+ * If no response is received from any peer, it returns undefined.
150
+ *
151
+ * The method performs the following steps:
152
+ * - Sample a peer to send the request to.
153
+ * - Opens a stream with the peer using the specified sub-protocol.
154
+ *
155
+ * When a response is received, it is validated using the given sub protocols response validator.
156
+ * To see the interface for the response validator - see `interface.ts`
157
+ *
158
+ * Failing a response validation requests in a severe peer penalty, and will
159
+ * prompt the node to continue to search to the next peer.
160
+ * For example, a transaction request validator will check that the payload returned does in fact
161
+ * match the txHash that was requested. A peer that fails this check an only be an extremely naughty peer.
162
+ *
163
+ * This entire operation is wrapped in an overall timeout, that is independent of the
164
+ * peer it is requesting data from.
165
+ *
166
+ */
167
+ async sendRequest<SubProtocol extends ReqRespSubProtocol>(
168
+ subProtocol: SubProtocol,
169
+ request: InstanceType<SubProtocolMap[SubProtocol]['request']>,
170
+ ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined> {
171
+ const responseValidator = this.subProtocolValidators[subProtocol];
172
+ const requestBuffer = request.toBuffer();
173
+
174
+ const requestFunction = async () => {
175
+ // Attempt to ask all of our peers, but sampled in a random order
176
+ // This function is wrapped in a timeout, so we will exit the loop if we have not received a response
177
+ const numberOfPeers = this.libp2p.getPeers().length;
178
+
179
+ if (numberOfPeers === 0) {
180
+ this.logger.debug('No active peers to send requests to');
181
+ return undefined;
182
+ }
183
+
184
+ const attemptedPeers: Map<string, boolean> = new Map();
185
+ for (let i = 0; i < numberOfPeers; i++) {
186
+ // Sample a peer to make a request to
187
+ const peer = this.connectionSampler.getPeer(attemptedPeers);
188
+ this.logger.trace(`Attempting to send request to peer: ${peer?.toString()}`);
189
+ if (!peer) {
190
+ this.logger.debug('No peers available to send requests to');
191
+ return undefined;
192
+ }
193
+
194
+ attemptedPeers.set(peer.toString(), true);
195
+
196
+ this.logger.trace(`Sending request to peer: ${peer.toString()}`);
197
+ const response = await this.sendRequestToPeer(peer, subProtocol, requestBuffer);
198
+
199
+ if (response && response.status !== ReqRespStatus.SUCCESS) {
200
+ this.logger.debug(
201
+ `Request to peer ${peer.toString()} failed with status ${prettyPrintReqRespStatus(response.status)}`,
202
+ );
203
+ continue;
204
+ }
205
+
206
+ // If we get a response, return it, otherwise we iterate onto the next peer
207
+ // We do not consider it a success if we have an empty buffer
208
+ if (response && response.data.length > 0) {
209
+ const object = subProtocolMap[subProtocol].response.fromBuffer(response.data);
210
+ // The response validator handles peer punishment within
211
+ const isValid = await responseValidator(request, object, peer);
212
+ if (!isValid) {
213
+ throw new InvalidResponseError();
214
+ }
215
+ return object;
216
+ }
217
+ }
218
+ };
219
+
220
+ try {
221
+ return await executeTimeout<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined>(
222
+ requestFunction,
223
+ this.overallRequestTimeoutMs,
224
+ () => new CollectiveReqRespTimeoutError(),
225
+ );
226
+ } catch (e: any) {
227
+ this.logger.debug(`${e.message} | subProtocol: ${subProtocol}`);
228
+ return undefined;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Request multiple messages over the same sub protocol, balancing the requests across peers.
234
+ *
235
+ * @devnote
236
+ * - The function prioritizes sending requests to free peers using a batch sampling strategy.
237
+ * - If a peer fails to respond or returns an invalid response, it is removed from the sampling pool and replaced.
238
+ * - The function stops retrying once all requests are processed, no active peers remain, or the maximum retry attempts are reached.
239
+ * - Responses are validated using a custom validator for the sub-protocol.*
240
+ *
241
+ * Requests are sent in parallel to each peer, but multiple requests are sent to the same peer in series
242
+ * - If a peer fails to respond or returns an invalid response, it is removed from the sampling pool and replaced.
243
+ * - The function stops retrying once all requests are processed, no active peers remain, or the maximum retry attempts are reached.
244
+ * - Responses are validated using a custom validator for the sub-protocol.*
245
+ *
246
+ * @param subProtocol
247
+ * @param requests
248
+ * @param timeoutMs
249
+ * @param maxPeers
250
+ * @returns
251
+ *
252
+ * @throws {CollectiveReqRespTimeoutError} - If the request batch exceeds the specified timeout (`timeoutMs`).
253
+ */
254
+ @trackSpan(
255
+ 'ReqResp.sendBatchRequest',
256
+ (subProtocol: ReqRespSubProtocol, requests: InstanceType<SubProtocolMap[ReqRespSubProtocol]['request']>[]) => ({
257
+ [Attributes.P2P_REQ_RESP_PROTOCOL]: subProtocol,
258
+ [Attributes.P2P_REQ_RESP_BATCH_REQUESTS_COUNT]: requests.length,
259
+ }),
260
+ )
261
+ async sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
262
+ subProtocol: SubProtocol,
263
+ requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
264
+ timeoutMs = 10000,
265
+ maxPeers = Math.min(10, requests.length),
266
+ maxRetryAttempts = 3,
267
+ ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
268
+ const responseValidator = this.subProtocolValidators[subProtocol];
269
+ const responses: InstanceType<SubProtocolMap[SubProtocol]['response']>[] = new Array(requests.length);
270
+ const requestBuffers = requests.map(req => req.toBuffer());
271
+
272
+ const requestFunction = async () => {
273
+ // Track which requests still need to be processed
274
+ const pendingRequestIndices = new Set(requestBuffers.map((_, i) => i));
275
+
276
+ // Create batch sampler with the total number of requests and max peers
277
+ const batchSampler = new BatchConnectionSampler(this.connectionSampler, requests.length, maxPeers);
278
+
279
+ if (batchSampler.activePeerCount === 0) {
280
+ this.logger.debug('No active peers to send requests to');
281
+ return [];
282
+ }
283
+
284
+ // This is where it gets fun
285
+ // The outer loop is the retry loop, we will continue to retry until we process all indices we have
286
+ // not received a response for, or we have reached the max retry attempts
287
+
288
+ // The inner loop is the batch loop, we will process all requests for each peer in parallel
289
+ // We will then process the results of the requests, and resample any peers that failed to respond
290
+ // We will continue to retry until we have processed all indices, or we have reached the max retry attempts
291
+
292
+ let retryAttempts = 0;
293
+ while (pendingRequestIndices.size > 0 && batchSampler.activePeerCount > 0 && retryAttempts < maxRetryAttempts) {
294
+ // Process requests in parallel for each available peer
295
+ const requestBatches = new Map<PeerId, number[]>();
296
+
297
+ // Group requests by peer
298
+ for (const requestIndex of pendingRequestIndices) {
299
+ const peer = batchSampler.getPeerForRequest(requestIndex);
300
+ if (!peer) {
301
+ break;
302
+ }
303
+
304
+ if (!requestBatches.has(peer)) {
305
+ requestBatches.set(peer, []);
306
+ }
307
+ requestBatches.get(peer)!.push(requestIndex);
308
+ }
309
+
310
+ // Make parallel requests for each peer's batch
311
+ // A batch entry will look something like this:
312
+ // PeerId0: [0, 1, 2, 3]
313
+ // PeerId1: [4, 5, 6, 7]
314
+
315
+ // Peer Id 0 will send requests 0, 1, 2, 3 in serial
316
+ // while simultaneously Peer Id 1 will send requests 4, 5, 6, 7 in serial
317
+
318
+ const batchResults = await Promise.all(
319
+ Array.from(requestBatches.entries()).map(async ([peer, indices]) => {
320
+ try {
321
+ // Requests all going to the same peer are sent synchronously
322
+ const peerResults: { index: number; response: InstanceType<SubProtocolMap[SubProtocol]['response']> }[] =
323
+ [];
324
+ for (const index of indices) {
325
+ const response = await this.sendRequestToPeer(peer, subProtocol, requestBuffers[index]);
326
+
327
+ // Check the status of the response buffer
328
+ if (response && response.status !== ReqRespStatus.SUCCESS) {
329
+ this.logger.debug(
330
+ `Request to peer ${peer.toString()} failed with status ${prettyPrintReqRespStatus(
331
+ response.status,
332
+ )}`,
333
+ );
334
+
335
+ // If we hit a rate limit or some failure, we remove the peer and return the results,
336
+ // they will be split among remaining peers and the new sampled peer
337
+ batchSampler.removePeerAndReplace(peer);
338
+ return { peer, results: peerResults };
339
+ }
340
+
341
+ if (response && response.data.length > 0) {
342
+ const object = subProtocolMap[subProtocol].response.fromBuffer(response.data);
343
+ const isValid = await responseValidator(requests[index], object, peer);
344
+
345
+ if (isValid) {
346
+ peerResults.push({ index, response: object });
347
+ }
348
+ }
349
+ }
350
+
351
+ return { peer, results: peerResults };
352
+ } catch (error) {
353
+ this.logger.debug(`Failed batch request to peer ${peer.toString()}:`, error);
354
+ batchSampler.removePeerAndReplace(peer);
355
+ return { peer, results: [] };
356
+ }
357
+ }),
358
+ );
359
+
360
+ // Process results
361
+ for (const { results } of batchResults) {
362
+ for (const { index, response } of results) {
363
+ if (response) {
364
+ responses[index] = response;
365
+ pendingRequestIndices.delete(index);
366
+ }
367
+ }
368
+ }
369
+
370
+ retryAttempts++;
371
+ }
372
+
373
+ if (retryAttempts >= maxRetryAttempts) {
374
+ this.logger.debug(`Max retry attempts ${maxRetryAttempts} reached for batch request`);
375
+ }
376
+
377
+ return responses;
378
+ };
379
+
380
+ try {
381
+ return await executeTimeout<InstanceType<SubProtocolMap[SubProtocol]['response']>[]>(
382
+ requestFunction,
383
+ timeoutMs,
384
+ () => new CollectiveReqRespTimeoutError(),
385
+ );
386
+ } catch (e: any) {
387
+ this.logger.debug(`${e.message} | subProtocol: ${subProtocol}`);
388
+ return [];
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Sends a request to a specific peer
394
+ *
395
+ * We first dial a particular protocol for the peer, this ensures that the peer knows
396
+ * what to respond with
397
+ *
398
+ *
399
+ * @param peerId - The peer to send the request to
400
+ * @param subProtocol - The protocol to use to request
401
+ * @param payload - The payload to send
402
+ * @returns If the request is successful, the response is returned, otherwise undefined
403
+ *
404
+ * @description
405
+ * This method attempts to open a stream with the specified peer, send the payload,
406
+ * and await a response.
407
+ * If an error occurs, it penalizes the peer and returns undefined.
408
+ *
409
+ * The method performs the following steps:
410
+ * - Opens a stream with the peer using the specified sub-protocol.
411
+ * - Sends the payload and awaits a response with a timeout.
412
+ *
413
+ * If the stream is not closed by the dialled peer, and a timeout occurs, then
414
+ * the stream is closed on the requester's end and sender (us) updates its peer score
415
+ */
416
+ @trackSpan('ReqResp.sendRequestToPeer', (peerId: PeerId, subProtocol: ReqRespSubProtocol, _: Buffer) => ({
417
+ [Attributes.P2P_ID]: peerId.toString(),
418
+ [Attributes.P2P_REQ_RESP_PROTOCOL]: subProtocol,
419
+ }))
420
+ public async sendRequestToPeer(
421
+ peerId: PeerId,
422
+ subProtocol: ReqRespSubProtocol,
423
+ payload: Buffer,
424
+ ): Promise<ReqRespResponse | undefined> {
425
+ let stream: Stream | undefined;
426
+ try {
427
+ this.metrics.recordRequestSent(subProtocol);
428
+
429
+ stream = await this.connectionSampler.dialProtocol(peerId, subProtocol);
430
+
431
+ // Open the stream with a timeout
432
+ const result = await executeTimeout<ReqRespResponse>(
433
+ (): Promise<ReqRespResponse> => pipe([payload], stream!, this.readMessage.bind(this)),
434
+ this.individualRequestTimeoutMs,
435
+ () => new IndividualReqRespTimeoutError(),
436
+ );
437
+
438
+ return result;
439
+ } catch (e: any) {
440
+ this.metrics.recordRequestError(subProtocol);
441
+ this.handleResponseError(e, peerId, subProtocol);
442
+ } finally {
443
+ // Only close the stream if we created it
444
+ if (stream) {
445
+ try {
446
+ await this.connectionSampler.close(stream.id);
447
+ } catch (closeError) {
448
+ this.logger.error(
449
+ `Error closing stream: ${closeError instanceof Error ? closeError.message : 'Unknown error'}`,
450
+ );
451
+ }
452
+ }
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Handle a response error
458
+ *
459
+ * ReqResp errors are punished differently depending on the severity of the offense
460
+ *
461
+ * @param e - The error
462
+ * @param peerId - The peer id
463
+ * @param subProtocol - The sub protocol
464
+ * @returns If the error is non pubishable, then undefined is returned, otherwise the peer is penalized
465
+ */
466
+ private handleResponseError(e: any, peerId: PeerId, subProtocol: ReqRespSubProtocol): void {
467
+ const severity = this.categorizeError(e, peerId, subProtocol);
468
+ if (severity) {
469
+ this.peerScoring.penalizePeer(peerId, severity);
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Categorize the error and log it.
475
+ */
476
+ private categorizeError(e: any, peerId: PeerId, subProtocol: ReqRespSubProtocol): PeerErrorSeverity | undefined {
477
+ // Non punishable errors - we do not expect a response for goodbye messages
478
+ if (subProtocol === ReqRespSubProtocol.GOODBYE) {
479
+ this.logger.debug('Error encountered on goodbye sub protocol, no penalty', {
480
+ peerId: peerId.toString(),
481
+ subProtocol,
482
+ });
483
+ return undefined;
484
+ }
485
+
486
+ // We do not punish a collective timeout, as the node triggers this interupt, independent of the peer's behaviour
487
+ const logTags = {
488
+ peerId: peerId.toString(),
489
+ subProtocol,
490
+ };
491
+ if (e instanceof CollectiveReqRespTimeoutError || e instanceof InvalidResponseError) {
492
+ this.logger.debug(
493
+ `Non-punishable error: ${e.message} | peerId: ${peerId.toString()} | subProtocol: ${subProtocol}`,
494
+ logTags,
495
+ );
496
+ return undefined;
497
+ }
498
+
499
+ // Pubishable errors
500
+ // Connection reset errors in the networking stack are punished with high severity
501
+ // it just signals an unreliable peer
502
+ // We assume that the requesting node has a functioning networking stack.
503
+ if (e?.code === 'ECONNRESET' || e?.code === 'EPIPE') {
504
+ this.logger.debug(`Connection reset: ${peerId.toString()}`, logTags);
505
+ return PeerErrorSeverity.HighToleranceError;
506
+ }
507
+
508
+ if (e?.code === 'ECONNREFUSED') {
509
+ this.logger.debug(`Connection refused: ${peerId.toString()}`, logTags);
510
+ return PeerErrorSeverity.HighToleranceError;
511
+ }
512
+
513
+ // Timeout errors are punished with high tolerance, they can be due to a geogrpahically far away peer or an
514
+ // overloaded peer
515
+ if (e instanceof IndividualReqRespTimeoutError) {
516
+ this.logger.debug(
517
+ `Timeout error: ${e.message} | peerId: ${peerId.toString()} | subProtocol: ${subProtocol}`,
518
+ logTags,
519
+ );
520
+ return PeerErrorSeverity.HighToleranceError;
521
+ }
522
+
523
+ // Catch all error
524
+ this.logger.error(`Unexpected error sending request to peer`, e, logTags);
525
+ return PeerErrorSeverity.HighToleranceError;
526
+ }
527
+
528
+ /**
529
+ * Read a message returned from a stream into a single buffer
530
+ *
531
+ * The message is split into two components
532
+ * - The first chunk should contain a control byte, indicating the status of the response see `ReqRespStatus`
533
+ * - The second chunk should contain the response data
534
+ */
535
+ private async readMessage(source: AsyncIterable<Uint8ArrayList>): Promise<ReqRespResponse> {
536
+ let statusBuffer: ReqRespStatus | undefined;
537
+ const chunks: Uint8Array[] = [];
538
+
539
+ try {
540
+ for await (const chunk of source) {
541
+ if (statusBuffer === undefined) {
542
+ const firstChunkBuffer = chunk.subarray();
543
+ statusBuffer = parseStatusChunk(firstChunkBuffer);
544
+ } else {
545
+ chunks.push(chunk.subarray());
546
+ }
547
+ }
548
+
549
+ const messageData = Buffer.concat(chunks);
550
+ const message: Buffer = this.snappyTransform.inboundTransformNoTopic(messageData);
551
+
552
+ return {
553
+ status: statusBuffer ?? ReqRespStatus.UNKNOWN,
554
+ data: message,
555
+ };
556
+ } catch (e: any) {
557
+ this.logger.debug(`Reading message failed: ${e.message}`);
558
+
559
+ let status = ReqRespStatus.UNKNOWN;
560
+ if (e instanceof ReqRespStatusError) {
561
+ status = e.status;
562
+ }
563
+
564
+ return {
565
+ status,
566
+ data: Buffer.from([]),
567
+ };
568
+ }
569
+ }
570
+
571
+ /**
572
+ * Stream Handler
573
+ * Reads the incoming stream, determines the protocol, then triggers the appropriate handler
574
+ *
575
+ * @param param0 - The incoming stream data
576
+ *
577
+ * @description
578
+ * An individual stream handler will be bound to each sub protocol, and handles returning data back
579
+ * to the requesting peer.
580
+ *
581
+ * The sub protocol handler interface is defined within `interface.ts` and will be assigned to the
582
+ * req resp service on start up.
583
+ *
584
+ * We check rate limits for each peer, note the peer will be penalised within the rate limiter implementation
585
+ * if they exceed their peer specific limits.
586
+ */
587
+ @trackSpan('ReqResp.streamHandler', (protocol: ReqRespSubProtocol, { connection }: IncomingStreamData) => ({
588
+ [Attributes.P2P_REQ_RESP_PROTOCOL]: protocol,
589
+ [Attributes.P2P_ID]: connection.remotePeer.toString(),
590
+ }))
591
+ private async streamHandler(protocol: ReqRespSubProtocol, { stream, connection }: IncomingStreamData) {
592
+ this.metrics.recordRequestReceived(protocol);
593
+
594
+ try {
595
+ // Store a reference to from this for the async generator
596
+ const rateLimitStatus = this.rateLimiter.allow(protocol, connection.remotePeer);
597
+ if (rateLimitStatus != RateLimitStatus.Allowed) {
598
+ this.logger.warn(
599
+ `Rate limit exceeded ${prettyPrintRateLimitStatus(rateLimitStatus)} for ${protocol} from ${
600
+ connection.remotePeer
601
+ }`,
602
+ );
603
+
604
+ throw new ReqRespStatusError(ReqRespStatus.RATE_LIMIT_EXCEEDED);
605
+ }
606
+
607
+ const handler = this.subProtocolHandlers[protocol];
608
+ const transform = this.snappyTransform;
609
+
610
+ await pipe(
611
+ stream,
612
+ async function* (source: any) {
613
+ for await (const chunkList of source) {
614
+ const msg = Buffer.from(chunkList.subarray());
615
+ const response = await handler(connection.remotePeer, msg);
616
+
617
+ if (protocol === ReqRespSubProtocol.GOODBYE) {
618
+ // Don't respond
619
+ await stream.close();
620
+ return;
621
+ }
622
+
623
+ // Send success code first, then the response
624
+ const successChunk = Buffer.from([ReqRespStatus.SUCCESS]);
625
+ yield new Uint8Array(successChunk);
626
+
627
+ yield new Uint8Array(transform.outboundTransformNoTopic(response));
628
+ }
629
+ },
630
+ stream,
631
+ );
632
+ } catch (e: any) {
633
+ this.logger.warn('Reqresp Response error: ', e);
634
+ this.metrics.recordResponseError(protocol);
635
+
636
+ // If we receive a known error, we use the error status in the response chunk, otherwise we categorize as unknown
637
+ let errorStatus = ReqRespStatus.UNKNOWN;
638
+ if (e instanceof ReqRespStatusError) {
639
+ errorStatus = e.status;
640
+ }
641
+
642
+ const sendErrorChunk = this.sendErrorChunk(errorStatus);
643
+
644
+ // Return and yield the response chunk
645
+ await pipe(
646
+ stream,
647
+ async function* (_source: any) {
648
+ yield* sendErrorChunk;
649
+ },
650
+ stream,
651
+ );
652
+ } finally {
653
+ await stream.close();
654
+ }
655
+ }
656
+
657
+ private async *sendErrorChunk(error: ReqRespStatus): AsyncIterable<Uint8Array> {
658
+ const errorChunk = Buffer.from([error]);
659
+ yield new Uint8Array(errorChunk);
660
+ }
661
+ }