@optimystic/db-p2p 0.14.1 → 0.16.3

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 (275) hide show
  1. package/README.md +616 -609
  2. package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
  3. package/dist/src/cluster/block-transfer-service.js +4 -3
  4. package/dist/src/cluster/block-transfer-service.js.map +1 -1
  5. package/dist/src/cluster/block-transfer.d.ts +52 -3
  6. package/dist/src/cluster/block-transfer.d.ts.map +1 -1
  7. package/dist/src/cluster/block-transfer.js +129 -6
  8. package/dist/src/cluster/block-transfer.js.map +1 -1
  9. package/dist/src/cluster/client.d.ts.map +1 -1
  10. package/dist/src/cluster/client.js +3 -1
  11. package/dist/src/cluster/client.js.map +1 -1
  12. package/dist/src/cluster/cluster-repo.d.ts +182 -11
  13. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  14. package/dist/src/cluster/cluster-repo.js +429 -69
  15. package/dist/src/cluster/cluster-repo.js.map +1 -1
  16. package/dist/src/cluster/peer-key-binding.d.ts +25 -0
  17. package/dist/src/cluster/peer-key-binding.d.ts.map +1 -0
  18. package/dist/src/cluster/peer-key-binding.js +39 -0
  19. package/dist/src/cluster/peer-key-binding.js.map +1 -0
  20. package/dist/src/cluster/quorum-restore.d.ts +72 -0
  21. package/dist/src/cluster/quorum-restore.d.ts.map +1 -0
  22. package/dist/src/cluster/quorum-restore.js +0 -0
  23. package/dist/src/cluster/quorum-restore.js.map +1 -0
  24. package/dist/src/cluster/rebalance-monitor.d.ts +23 -1
  25. package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
  26. package/dist/src/cluster/rebalance-monitor.js +17 -2
  27. package/dist/src/cluster/rebalance-monitor.js.map +1 -1
  28. package/dist/src/cluster/service.d.ts.map +1 -1
  29. package/dist/src/cluster/service.js +7 -1
  30. package/dist/src/cluster/service.js.map +1 -1
  31. package/dist/src/cluster/spread-on-churn.d.ts +7 -0
  32. package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
  33. package/dist/src/cluster/spread-on-churn.js +4 -1
  34. package/dist/src/cluster/spread-on-churn.js.map +1 -1
  35. package/dist/src/cluster/supermajority-coupling.d.ts +22 -0
  36. package/dist/src/cluster/supermajority-coupling.d.ts.map +1 -0
  37. package/dist/src/cluster/supermajority-coupling.js +27 -0
  38. package/dist/src/cluster/supermajority-coupling.js.map +1 -0
  39. package/dist/src/cohort-topic/cohort-gossip-driver.d.ts +46 -4
  40. package/dist/src/cohort-topic/cohort-gossip-driver.d.ts.map +1 -1
  41. package/dist/src/cohort-topic/cohort-gossip-driver.js +62 -7
  42. package/dist/src/cohort-topic/cohort-gossip-driver.js.map +1 -1
  43. package/dist/src/cohort-topic/cohort-gossip-transport.d.ts +7 -0
  44. package/dist/src/cohort-topic/cohort-gossip-transport.d.ts.map +1 -1
  45. package/dist/src/cohort-topic/cohort-gossip-transport.js +9 -0
  46. package/dist/src/cohort-topic/cohort-gossip-transport.js.map +1 -1
  47. package/dist/src/cohort-topic/host.d.ts +197 -27
  48. package/dist/src/cohort-topic/host.d.ts.map +1 -1
  49. package/dist/src/cohort-topic/host.js +643 -87
  50. package/dist/src/cohort-topic/host.js.map +1 -1
  51. package/dist/src/dispute/arbitrator-selection.d.ts +68 -7
  52. package/dist/src/dispute/arbitrator-selection.d.ts.map +1 -1
  53. package/dist/src/dispute/arbitrator-selection.js +105 -16
  54. package/dist/src/dispute/arbitrator-selection.js.map +1 -1
  55. package/dist/src/dispute/cascade.d.ts +7 -0
  56. package/dist/src/dispute/cascade.d.ts.map +1 -1
  57. package/dist/src/dispute/cascade.js +1 -1
  58. package/dist/src/dispute/cascade.js.map +1 -1
  59. package/dist/src/dispute/client.d.ts.map +1 -1
  60. package/dist/src/dispute/client.js +3 -2
  61. package/dist/src/dispute/client.js.map +1 -1
  62. package/dist/src/dispute/dispute-service.d.ts +32 -3
  63. package/dist/src/dispute/dispute-service.d.ts.map +1 -1
  64. package/dist/src/dispute/dispute-service.js +82 -9
  65. package/dist/src/dispute/dispute-service.js.map +1 -1
  66. package/dist/src/dispute/index.d.ts +1 -1
  67. package/dist/src/dispute/index.d.ts.map +1 -1
  68. package/dist/src/dispute/index.js +1 -1
  69. package/dist/src/dispute/index.js.map +1 -1
  70. package/dist/src/dispute/invalidation.d.ts +25 -0
  71. package/dist/src/dispute/invalidation.d.ts.map +1 -1
  72. package/dist/src/dispute/invalidation.js +14 -2
  73. package/dist/src/dispute/invalidation.js.map +1 -1
  74. package/dist/src/dispute/service.d.ts.map +1 -1
  75. package/dist/src/dispute/service.js +7 -1
  76. package/dist/src/dispute/service.js.map +1 -1
  77. package/dist/src/index.d.ts +4 -1
  78. package/dist/src/index.d.ts.map +1 -1
  79. package/dist/src/index.js +4 -1
  80. package/dist/src/index.js.map +1 -1
  81. package/dist/src/it-utility.d.ts +1 -1
  82. package/dist/src/it-utility.d.ts.map +1 -1
  83. package/dist/src/it-utility.js +4 -13
  84. package/dist/src/it-utility.js.map +1 -1
  85. package/dist/src/libp2p-key-network.d.ts +98 -1
  86. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  87. package/dist/src/libp2p-key-network.js +272 -17
  88. package/dist/src/libp2p-key-network.js.map +1 -1
  89. package/dist/src/libp2p-node-base.d.ts +12 -0
  90. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  91. package/dist/src/libp2p-node-base.js +441 -89
  92. package/dist/src/libp2p-node-base.js.map +1 -1
  93. package/dist/src/matchmaking/query-transport.d.ts +2 -0
  94. package/dist/src/matchmaking/query-transport.d.ts.map +1 -1
  95. package/dist/src/matchmaking/query-transport.js +6 -0
  96. package/dist/src/matchmaking/query-transport.js.map +1 -1
  97. package/dist/src/matchmaking/traffic-validation.js +1 -0
  98. package/dist/src/matchmaking/traffic-validation.js.map +1 -1
  99. package/dist/src/network/network-manager-service.d.ts +2 -2
  100. package/dist/src/network/network-manager-service.d.ts.map +1 -1
  101. package/dist/src/network/network-manager-service.js +8 -4
  102. package/dist/src/network/network-manager-service.js.map +1 -1
  103. package/dist/src/owned-block-seed.d.ts +27 -0
  104. package/dist/src/owned-block-seed.d.ts.map +1 -0
  105. package/dist/src/owned-block-seed.js +37 -0
  106. package/dist/src/owned-block-seed.js.map +1 -0
  107. package/dist/src/protocol-client.d.ts +1 -0
  108. package/dist/src/protocol-client.d.ts.map +1 -1
  109. package/dist/src/protocol-client.js +9 -2
  110. package/dist/src/protocol-client.js.map +1 -1
  111. package/dist/src/protocol-limits.d.ts +32 -0
  112. package/dist/src/protocol-limits.d.ts.map +1 -0
  113. package/dist/src/protocol-limits.js +32 -0
  114. package/dist/src/protocol-limits.js.map +1 -0
  115. package/dist/src/repo/client.d.ts.map +1 -1
  116. package/dist/src/repo/client.js +3 -0
  117. package/dist/src/repo/client.js.map +1 -1
  118. package/dist/src/repo/cluster-coordinator.d.ts +41 -4
  119. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  120. package/dist/src/repo/cluster-coordinator.js +128 -80
  121. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  122. package/dist/src/repo/coordinator-repo.d.ts +38 -1
  123. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  124. package/dist/src/repo/coordinator-repo.js +92 -13
  125. package/dist/src/repo/coordinator-repo.js.map +1 -1
  126. package/dist/src/repo/service.d.ts.map +1 -1
  127. package/dist/src/repo/service.js +7 -1
  128. package/dist/src/repo/service.js.map +1 -1
  129. package/dist/src/reputation/peer-reputation.js +1 -1
  130. package/dist/src/reputation/peer-reputation.js.map +1 -1
  131. package/dist/src/reputation/types.d.ts +3 -1
  132. package/dist/src/reputation/types.d.ts.map +1 -1
  133. package/dist/src/reputation/types.js +3 -0
  134. package/dist/src/reputation/types.js.map +1 -1
  135. package/dist/src/rn.d.ts +5 -1
  136. package/dist/src/rn.d.ts.map +1 -1
  137. package/dist/src/rn.js +7 -1
  138. package/dist/src/rn.js.map +1 -1
  139. package/dist/src/storage/arachnode-fret-adapter.d.ts +30 -1
  140. package/dist/src/storage/arachnode-fret-adapter.d.ts.map +1 -1
  141. package/dist/src/storage/arachnode-fret-adapter.js +15 -2
  142. package/dist/src/storage/arachnode-fret-adapter.js.map +1 -1
  143. package/dist/src/storage/arachnode-partition.d.ts +49 -0
  144. package/dist/src/storage/arachnode-partition.d.ts.map +1 -0
  145. package/dist/src/storage/arachnode-partition.js +61 -0
  146. package/dist/src/storage/arachnode-partition.js.map +1 -0
  147. package/dist/src/storage/block-storage.d.ts +40 -1
  148. package/dist/src/storage/block-storage.d.ts.map +1 -1
  149. package/dist/src/storage/block-storage.js +150 -57
  150. package/dist/src/storage/block-storage.js.map +1 -1
  151. package/dist/src/storage/i-block-storage.d.ts +8 -0
  152. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  153. package/dist/src/storage/i-raw-storage.d.ts +13 -0
  154. package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
  155. package/dist/src/storage/kv-raw-storage.d.ts +45 -0
  156. package/dist/src/storage/kv-raw-storage.d.ts.map +1 -0
  157. package/dist/src/storage/kv-raw-storage.js +113 -0
  158. package/dist/src/storage/kv-raw-storage.js.map +1 -0
  159. package/dist/src/storage/memory-storage.d.ts +20 -58
  160. package/dist/src/storage/memory-storage.d.ts.map +1 -1
  161. package/dist/src/storage/memory-storage.js +22 -148
  162. package/dist/src/storage/memory-storage.js.map +1 -1
  163. package/dist/src/storage/memory-store-driver.d.ts +39 -0
  164. package/dist/src/storage/memory-store-driver.d.ts.map +1 -0
  165. package/dist/src/storage/memory-store-driver.js +129 -0
  166. package/dist/src/storage/memory-store-driver.js.map +1 -0
  167. package/dist/src/storage/raw-store-codec.d.ts +10 -0
  168. package/dist/src/storage/raw-store-codec.d.ts.map +1 -0
  169. package/dist/src/storage/raw-store-codec.js +36 -0
  170. package/dist/src/storage/raw-store-codec.js.map +1 -0
  171. package/dist/src/storage/raw-store-driver.d.ts +69 -0
  172. package/dist/src/storage/raw-store-driver.d.ts.map +1 -0
  173. package/dist/src/storage/raw-store-driver.js +2 -0
  174. package/dist/src/storage/raw-store-driver.js.map +1 -0
  175. package/dist/src/storage/restoration-coordinator-v2.d.ts.map +1 -1
  176. package/dist/src/storage/restoration-coordinator-v2.js +1 -1
  177. package/dist/src/storage/restoration-coordinator-v2.js.map +1 -1
  178. package/dist/src/storage/restoration-coordinator.d.ts +73 -0
  179. package/dist/src/storage/restoration-coordinator.d.ts.map +1 -0
  180. package/dist/src/storage/restoration-coordinator.js +181 -0
  181. package/dist/src/storage/restoration-coordinator.js.map +1 -0
  182. package/dist/src/storage/ring-selector.d.ts +92 -5
  183. package/dist/src/storage/ring-selector.d.ts.map +1 -1
  184. package/dist/src/storage/ring-selector.js +153 -51
  185. package/dist/src/storage/ring-selector.js.map +1 -1
  186. package/dist/src/storage/ring-shift-coordinator.d.ts +128 -0
  187. package/dist/src/storage/ring-shift-coordinator.d.ts.map +1 -0
  188. package/dist/src/storage/ring-shift-coordinator.js +196 -0
  189. package/dist/src/storage/ring-shift-coordinator.js.map +1 -0
  190. package/dist/src/storage/storage-monitor.d.ts +28 -0
  191. package/dist/src/storage/storage-monitor.d.ts.map +1 -1
  192. package/dist/src/storage/storage-monitor.js +42 -2
  193. package/dist/src/storage/storage-monitor.js.map +1 -1
  194. package/dist/src/storage/storage-repo.d.ts +19 -0
  195. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  196. package/dist/src/storage/storage-repo.js +172 -22
  197. package/dist/src/storage/storage-repo.js.map +1 -1
  198. package/dist/src/sync/client.d.ts.map +1 -1
  199. package/dist/src/sync/client.js +5 -1
  200. package/dist/src/sync/client.js.map +1 -1
  201. package/dist/src/sync/service.d.ts.map +1 -1
  202. package/dist/src/sync/service.js +2 -1
  203. package/dist/src/sync/service.js.map +1 -1
  204. package/dist/src/testing/cohort-topic-mesh-harness.d.ts +28 -4
  205. package/dist/src/testing/cohort-topic-mesh-harness.d.ts.map +1 -1
  206. package/dist/src/testing/cohort-topic-mesh-harness.js +51 -10
  207. package/dist/src/testing/cohort-topic-mesh-harness.js.map +1 -1
  208. package/dist/src/testing/index.d.ts +1 -0
  209. package/dist/src/testing/index.d.ts.map +1 -1
  210. package/dist/src/testing/index.js +1 -0
  211. package/dist/src/testing/index.js.map +1 -1
  212. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  213. package/dist/src/testing/mesh-harness.js +15 -7
  214. package/dist/src/testing/mesh-harness.js.map +1 -1
  215. package/dist/src/testing/raw-storage-conformance.d.ts +24 -0
  216. package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -0
  217. package/dist/src/testing/raw-storage-conformance.js +313 -0
  218. package/dist/src/testing/raw-storage-conformance.js.map +1 -0
  219. package/package.json +4 -4
  220. package/src/cluster/block-transfer-service.ts +4 -3
  221. package/src/cluster/block-transfer.ts +424 -265
  222. package/src/cluster/client.ts +3 -1
  223. package/src/cluster/cluster-repo.ts +481 -71
  224. package/src/cluster/peer-key-binding.ts +37 -0
  225. package/src/cluster/quorum-restore.ts +0 -0
  226. package/src/cluster/rebalance-monitor.ts +34 -3
  227. package/src/cluster/service.ts +7 -1
  228. package/src/cluster/spread-on-churn.ts +11 -1
  229. package/src/cluster/supermajority-coupling.ts +37 -0
  230. package/src/cohort-topic/cohort-gossip-driver.ts +96 -9
  231. package/src/cohort-topic/cohort-gossip-transport.ts +10 -0
  232. package/src/cohort-topic/host.ts +831 -105
  233. package/src/dispute/arbitrator-selection.ts +138 -28
  234. package/src/dispute/cascade.ts +8 -1
  235. package/src/dispute/client.ts +3 -2
  236. package/src/dispute/dispute-service.ts +94 -9
  237. package/src/dispute/index.ts +7 -1
  238. package/src/dispute/invalidation.ts +40 -2
  239. package/src/dispute/service.ts +7 -1
  240. package/src/index.ts +46 -43
  241. package/src/it-utility.ts +5 -13
  242. package/src/libp2p-key-network.ts +281 -18
  243. package/src/libp2p-node-base.ts +511 -81
  244. package/src/matchmaking/query-transport.ts +9 -0
  245. package/src/network/network-manager-service.ts +8 -2
  246. package/src/owned-block-seed.ts +40 -0
  247. package/src/protocol-client.ts +10 -3
  248. package/src/protocol-limits.ts +33 -0
  249. package/src/repo/client.ts +3 -0
  250. package/src/repo/cluster-coordinator.ts +145 -82
  251. package/src/repo/coordinator-repo.ts +96 -16
  252. package/src/repo/service.ts +7 -1
  253. package/src/reputation/types.ts +3 -0
  254. package/src/rn.ts +7 -1
  255. package/src/storage/arachnode-fret-adapter.ts +33 -2
  256. package/src/storage/arachnode-partition.ts +74 -0
  257. package/src/storage/block-storage.ts +169 -61
  258. package/src/storage/i-block-storage.ts +9 -0
  259. package/src/storage/i-raw-storage.ts +14 -0
  260. package/src/storage/kv-raw-storage.ts +135 -0
  261. package/src/storage/memory-storage.ts +25 -173
  262. package/src/storage/memory-store-driver.ts +157 -0
  263. package/src/storage/raw-store-codec.ts +42 -0
  264. package/src/storage/raw-store-driver.ts +80 -0
  265. package/src/storage/{restoration-coordinator-v2.ts → restoration-coordinator.ts} +43 -37
  266. package/src/storage/ring-selector.ts +317 -176
  267. package/src/storage/ring-shift-coordinator.ts +271 -0
  268. package/src/storage/storage-monitor.ts +70 -3
  269. package/src/storage/storage-repo.ts +710 -558
  270. package/src/sync/client.ts +5 -1
  271. package/src/sync/service.ts +2 -1
  272. package/src/testing/cohort-topic-mesh-harness.ts +61 -11
  273. package/src/testing/index.ts +1 -0
  274. package/src/testing/mesh-harness.ts +14 -6
  275. package/src/testing/raw-storage-conformance.ts +397 -0
package/dist/src/index.js CHANGED
@@ -12,12 +12,15 @@ export * from "./repo/cluster-coordinator.js";
12
12
  export * from "./repo/coordinator-repo.js";
13
13
  export * from "./repo/service.js";
14
14
  export * from "./storage/block-storage.js";
15
+ export * from "./storage/raw-store-driver.js";
16
+ export * from "./storage/kv-raw-storage.js";
17
+ export * from "./storage/memory-store-driver.js";
15
18
  export * from "./storage/memory-storage.js";
16
19
  export * from "./storage/i-block-storage.js";
17
20
  export * from "./storage/i-raw-storage.js";
18
21
  export * from "./storage/struct.js";
19
22
  export * from "./storage/storage-repo.js";
20
- export * from "./storage/restoration-coordinator-v2.js";
23
+ export * from "./storage/restoration-coordinator.js";
21
24
  export * from "./storage/ring-selector.js";
22
25
  export * from "./storage/storage-monitor.js";
23
26
  export * from "./storage/arachnode-fret-adapter.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qCAAqC,CAAC;AACpD,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yCAAyC,CAAC;AACxD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qCAAqC,CAAC;AACpD,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,iDAAiD,CAAC;AAChE,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qCAAqC,CAAC;AACpD,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qCAAqC,CAAC;AACpD,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,iDAAiD,CAAC;AAChE,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC"}
@@ -1,4 +1,4 @@
1
- export declare function first<T>(createIterable: (signal: AbortSignal) => AsyncIterable<T>, onEmpty?: () => T, timeoutMs?: number): Promise<T>;
1
+ export declare function first<T>(createIterable: () => AsyncIterable<T>, onEmpty?: () => T): Promise<T>;
2
2
  export declare function asyncIteratorToArray<T>(iterator: AsyncIterable<T>): Promise<T[]>;
3
3
  export declare function reduce<TP, TC>(iter: IterableIterator<TC>, each: (prior: TP, current: TC, index: number) => TP, start: TP): TP;
4
4
  //# sourceMappingURL=it-utility.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"it-utility.d.ts","sourceRoot":"","sources":["../../src/it-utility.ts"],"names":[],"mappings":"AAAA,wBAAsB,KAAK,CAAC,CAAC,EAC5B,cAAc,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,aAAa,CAAC,CAAC,CAAC,EACzD,OAAO,GAAE,MAAM,CAA+C,EAC9D,SAAS,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,CAAC,CAAC,CAYZ;AAED,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAMtF;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAQxH"}
1
+ {"version":3,"file":"it-utility.d.ts","sourceRoot":"","sources":["../../src/it-utility.ts"],"names":[],"mappings":"AAAA,wBAAsB,KAAK,CAAC,CAAC,EAC5B,cAAc,EAAE,MAAM,aAAa,CAAC,CAAC,CAAC,EACtC,OAAO,GAAE,MAAM,CAA+C,GAC5D,OAAO,CAAC,CAAC,CAAC,CAKZ;AAED,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAMtF;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,MAQxH"}
@@ -1,17 +1,8 @@
1
- export async function first(createIterable, onEmpty = () => { throw new Error('No items found'); }, timeoutMs) {
2
- const controller = new AbortController();
3
- const timer = typeof timeoutMs === 'number' ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
4
- try {
5
- for await (const item of createIterable(controller.signal)) {
6
- return item;
7
- }
8
- return onEmpty();
9
- }
10
- finally {
11
- if (timer !== undefined)
12
- clearTimeout(timer);
13
- controller.abort();
1
+ export async function first(createIterable, onEmpty = () => { throw new Error('No items found'); }) {
2
+ for await (const item of createIterable()) {
3
+ return item;
14
4
  }
5
+ return onEmpty();
15
6
  }
16
7
  export async function asyncIteratorToArray(iterator) {
17
8
  const result = [];
@@ -1 +1 @@
1
- {"version":3,"file":"it-utility.js","sourceRoot":"","sources":["../../src/it-utility.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,KAAK,CAC1B,cAAyD,EACzD,UAAmB,GAAG,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAA,CAAC,CAAC,EAC9D,SAAkB;IAElB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1G,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5D,OAAO,IAAI,CAAC;QACb,CAAC;QACD,OAAO,OAAO,EAAE,CAAC;IAClB,CAAC;YAAS,CAAC;QACV,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC7C,UAAU,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAI,QAA0B;IACvE,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,MAAM,CAAS,IAA0B,EAAE,IAAmD,EAAE,KAAS;IACxH,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QAC1B,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAChC,EAAE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"it-utility.js","sourceRoot":"","sources":["../../src/it-utility.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,KAAK,CAC1B,cAAsC,EACtC,UAAmB,GAAG,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAA,CAAC,CAAC;IAE9D,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,cAAc,EAAE,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,OAAO,EAAE,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAI,QAA0B;IACvE,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,MAAM,CAAS,IAA0B,EAAE,IAAmD,EAAE,KAAS;IACxH,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QAC1B,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAChC,EAAE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
@@ -23,8 +23,28 @@ export declare const FIND_COORDINATOR_ERROR_CODES: {
23
23
  readonly SELF_COORDINATION_EXHAUSTED: "SELF_COORDINATION_EXHAUSTED";
24
24
  /** No peer (including self) is an eligible coordinator. */
25
25
  readonly NO_COORDINATOR_AVAILABLE: "NO_COORDINATOR_AVAILABLE";
26
+ /**
27
+ * The candidate set was non-empty but every non-self candidate serves a
28
+ * DIFFERENT network's protocol (or none of this network's). Distinct from
29
+ * NO_COORDINATOR_AVAILABLE so a Sereus-style trace points at the real cause —
30
+ * "peer(s) do not serve this network's protocol" — instead of a generic
31
+ * "all candidates excluded" / super-majority failure.
32
+ */
33
+ readonly NO_NETWORK_COORDINATOR: "NO_NETWORK_COORDINATOR";
26
34
  };
27
35
  export type FindCoordinatorErrorCode = typeof FIND_COORDINATOR_ERROR_CODES[keyof typeof FIND_COORDINATOR_ERROR_CODES];
36
+ /**
37
+ * Network-membership classification of a peer relative to THIS node's network,
38
+ * derived from the peer's libp2p peerStore protocol list:
39
+ * - `serves` — advertises this network's namespaced `cluster`/`repo` protocol.
40
+ * - `foreign` — has a non-empty protocol list but none for this network → another network.
41
+ * - `unknown` — protocol list empty / peer absent → identify not yet completed. This is
42
+ * both a fresh same-network peer (will flip to `serves`) AND a cross-network
43
+ * peer (whose network-namespaced identify can NEVER complete, so it stays
44
+ * `unknown` forever) — indistinguishable at a single instant, separated over
45
+ * the retry/stabilization window.
46
+ */
47
+ export type NetworkMembership = 'serves' | 'foreign' | 'unknown';
28
48
  export declare class FindCoordinatorError extends Error {
29
49
  readonly code: FindCoordinatorErrorCode;
30
50
  constructor(code: FindCoordinatorErrorCode, message: string);
@@ -63,13 +83,31 @@ export declare class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
63
83
  private readonly libp2p;
64
84
  private readonly clusterSize;
65
85
  private readonly reputation?;
86
+ /**
87
+ * Network-namespaced protocol prefix (`/optimystic/<networkName>`). When
88
+ * provided, coordinator/cohort selection is scoped to peers that serve THIS
89
+ * network's `cluster`/`repo` protocol, so a peer that only belongs to another
90
+ * network sharing the same physical nodes/bootstraps is never chosen. When
91
+ * ABSENT, the membership filter is disabled (today's exact behavior) — required
92
+ * for backward compatibility because most call sites don't know the network name.
93
+ */
94
+ private readonly protocolPrefix?;
66
95
  private readonly selfCoordinationConfig;
67
96
  private networkHighWaterMark;
68
97
  private lastConnectedTime;
69
98
  private consecutiveIsolatedSessions;
70
99
  private readonly networkMode;
71
100
  private readonly persistence?;
72
- constructor(libp2p: Libp2p, clusterSize?: number, selfCoordinationConfig?: SelfCoordinationConfig, networkMode?: NetworkMode, persistence?: NetworkStatePersistence, reputation?: IPeerReputation | undefined);
101
+ constructor(libp2p: Libp2p, clusterSize?: number, selfCoordinationConfig?: SelfCoordinationConfig, networkMode?: NetworkMode, persistence?: NetworkStatePersistence, reputation?: IPeerReputation | undefined,
102
+ /**
103
+ * Network-namespaced protocol prefix (`/optimystic/<networkName>`). When
104
+ * provided, coordinator/cohort selection is scoped to peers that serve THIS
105
+ * network's `cluster`/`repo` protocol, so a peer that only belongs to another
106
+ * network sharing the same physical nodes/bootstraps is never chosen. When
107
+ * ABSENT, the membership filter is disabled (today's exact behavior) — required
108
+ * for backward compatibility because most call sites don't know the network name.
109
+ */
110
+ protocolPrefix?: string | undefined);
73
111
  private readonly coordinatorCache;
74
112
  private static readonly MAX_CACHE_ENTRIES;
75
113
  private readonly log;
@@ -95,6 +133,13 @@ export declare class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
95
133
  shouldAllowSelfCoordination(): SelfCoordinationDecision;
96
134
  recordCoordinator(key: Uint8Array, peerId: PeerId, ttlMs?: number): void;
97
135
  private getCachedCoordinator;
136
+ /**
137
+ * True for a circuit-relay ("limited") connection. libp2p stamps a relayed
138
+ * connection with `limits` (per-circuit data/duration caps); we additionally
139
+ * sniff the multiaddr for `/p2p-circuit` as a fallback for transports/versions
140
+ * that don't populate `limits`.
141
+ */
142
+ private isLimitedConnection;
98
143
  connect(peerId: PeerId, protocol: string, options?: AbortOptions): Promise<Stream>;
99
144
  private getFret;
100
145
  private getNeighborIdsForKey;
@@ -109,5 +154,57 @@ export declare class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
109
154
  * back to the defense-in-depth drop than throw out of findCluster.
110
155
  */
111
156
  private getPeerStoreAddrsByPeer;
157
+ /**
158
+ * Single-pass peerStore read returning BOTH protocols and addresses per peer from one
159
+ * `store.get` call. Used on the membership-scoped `findCluster` hot path, where the
160
+ * cohort needs protocols (to classify membership) AND addresses (to backfill dial
161
+ * targets) for the same peers — reading them together avoids a second `store.get` per
162
+ * finally-selected member. Same error handling as {@link getPeerStoreProtocolsByPeer}
163
+ * and {@link getPeerStoreAddrsByPeer}: a missing peer or peerStore failure is left
164
+ * absent from the map (caller treats absent protocols as 'unknown', absent addrs as none).
165
+ */
166
+ private getPeerStoreRecordsByPeer;
167
+ /**
168
+ * Prefetch each peer's advertised protocol list from the libp2p peerStore.
169
+ * Returns a map from peer-id string to its protocols (empty array when the peer
170
+ * is absent or has not yet been identified). Mirrors {@link getPeerStoreAddrsByPeer};
171
+ * errors are swallowed so a peerStore hiccup degrades to "unknown" rather than throwing.
172
+ */
173
+ private getPeerStoreProtocolsByPeer;
174
+ /**
175
+ * Over-fetch width for network-membership scoping. A cross-network peer can sit
176
+ * NEARER the key than a legitimate same-network peer and displace it from the
177
+ * nearest-`clusterSize` window, so when scoping is active we ask FRET for a wider
178
+ * proximity band and then keep the nearest peers that actually serve this network.
179
+ * (A ring polluted by more cross-network peers than this band is the domain of the
180
+ * separate FRET-side eviction follow-up; this band covers realistic co-location.)
181
+ */
182
+ private membershipOverfetch;
183
+ /**
184
+ * Classify a peer's network membership from its advertised protocols. Self always
185
+ * `serves` (it trivially serves its own network). When no `protocolPrefix` is
186
+ * configured the filter is disabled and EVERY peer is reported `serves`, so all
187
+ * callers behave exactly as before this scoping was added.
188
+ */
189
+ private membershipOf;
190
+ /**
191
+ * Scope a reputation-ordered candidate id list to this network for COORDINATOR
192
+ * selection: keep ONLY peers confirmed to serve this network (`serves`, which always
193
+ * includes self), dropping both `foreign` peers (serving another network) and
194
+ * `unknown` peers (peerStore protocol list empty — not yet confirmed). Incoming
195
+ * (reputation) order is preserved among the surviving `serves` peers. A no-op
196
+ * (returns the input unchanged, no drops) when `protocolPrefix` is unset or the list
197
+ * is empty — the membership-disabled path is therefore untouched.
198
+ *
199
+ * `droppedUnconfirmed` reports whether any candidate was excluded because it was not
200
+ * confirmed to serve this network — `foreign` OR `unknown` under scoping — so the
201
+ * caller can surface a distinct "no network coordinator" failure rather than a generic
202
+ * one. An `unknown` peer is not gambled on as coordinator: a permanent cross-network
203
+ * contaminant and a fresh same-network peer mid-identify are indistinguishable at an
204
+ * instant, but the filter re-reads the peerStore on every retry attempt, so a genuine
205
+ * same-network peer that completes `identify` within the retry window flips to `serves`
206
+ * and is selected normally on that attempt.
207
+ */
208
+ private filterByMembership;
112
209
  }
113
210
  //# sourceMappingURL=libp2p-key-network.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"libp2p-key-network.d.ts","sourceRoot":"","sources":["../../src/libp2p-key-network.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"libp2p-key-network.d.ts","sourceRoot":"","sources":["../../src/libp2p-key-network.ts"],"names":[],"mappings":"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"}
@@ -22,7 +22,15 @@ export const FIND_COORDINATOR_ERROR_CODES = {
22
22
  */
23
23
  SELF_COORDINATION_EXHAUSTED: 'SELF_COORDINATION_EXHAUSTED',
24
24
  /** No peer (including self) is an eligible coordinator. */
25
- NO_COORDINATOR_AVAILABLE: 'NO_COORDINATOR_AVAILABLE'
25
+ NO_COORDINATOR_AVAILABLE: 'NO_COORDINATOR_AVAILABLE',
26
+ /**
27
+ * The candidate set was non-empty but every non-self candidate serves a
28
+ * DIFFERENT network's protocol (or none of this network's). Distinct from
29
+ * NO_COORDINATOR_AVAILABLE so a Sereus-style trace points at the real cause —
30
+ * "peer(s) do not serve this network's protocol" — instead of a generic
31
+ * "all candidates excluded" / super-majority failure.
32
+ */
33
+ NO_NETWORK_COORDINATOR: 'NO_NETWORK_COORDINATOR'
26
34
  };
27
35
  export class FindCoordinatorError extends Error {
28
36
  code;
@@ -36,16 +44,27 @@ export class Libp2pKeyPeerNetwork {
36
44
  libp2p;
37
45
  clusterSize;
38
46
  reputation;
47
+ protocolPrefix;
39
48
  selfCoordinationConfig;
40
49
  networkHighWaterMark = 1;
41
50
  lastConnectedTime = Date.now();
42
51
  consecutiveIsolatedSessions = 0;
43
52
  networkMode;
44
53
  persistence;
45
- constructor(libp2p, clusterSize = 16, selfCoordinationConfig, networkMode, persistence, reputation) {
54
+ constructor(libp2p, clusterSize = 16, selfCoordinationConfig, networkMode, persistence, reputation,
55
+ /**
56
+ * Network-namespaced protocol prefix (`/optimystic/<networkName>`). When
57
+ * provided, coordinator/cohort selection is scoped to peers that serve THIS
58
+ * network's `cluster`/`repo` protocol, so a peer that only belongs to another
59
+ * network sharing the same physical nodes/bootstraps is never chosen. When
60
+ * ABSENT, the membership filter is disabled (today's exact behavior) — required
61
+ * for backward compatibility because most call sites don't know the network name.
62
+ */
63
+ protocolPrefix) {
46
64
  this.libp2p = libp2p;
47
65
  this.clusterSize = clusterSize;
48
66
  this.reputation = reputation;
67
+ this.protocolPrefix = protocolPrefix;
49
68
  this.selfCoordinationConfig = {
50
69
  gracePeriodMs: selfCoordinationConfig?.gracePeriodMs ?? 30_000,
51
70
  shrinkageThreshold: selfCoordinationConfig?.shrinkageThreshold ?? 0.5,
@@ -233,17 +252,39 @@ export class Libp2pKeyPeerNetwork {
233
252
  this.coordinatorCache.delete(k);
234
253
  return undefined;
235
254
  }
255
+ /**
256
+ * True for a circuit-relay ("limited") connection. libp2p stamps a relayed
257
+ * connection with `limits` (per-circuit data/duration caps); we additionally
258
+ * sniff the multiaddr for `/p2p-circuit` as a fallback for transports/versions
259
+ * that don't populate `limits`.
260
+ */
261
+ isLimitedConnection(c) {
262
+ if (c.limits != null)
263
+ return true;
264
+ const addr = c.remoteAddr?.toString?.();
265
+ return addr != null && addr.includes('/p2p-circuit');
266
+ }
236
267
  connect(peerId, protocol, options) {
237
268
  const conns = this.libp2p.getConnections?.(peerId) ?? [];
238
269
  // Filter to only-open connections so a closing/closed entry that libp2p
239
270
  // hasn't yet evicted from its index doesn't get picked up here.
240
- const open = conns.find(c => c?.status === 'open' && typeof c?.newStream === 'function');
241
- if (open) {
271
+ const open = conns.filter(c => c?.status === 'open' && typeof c?.newStream === 'function');
272
+ // Prefer a DIRECT connection over a limited (circuit-relay) one for the RPC.
273
+ // A relayed/limited connection can be reset by the relay once a per-circuit
274
+ // cap or reservation lapses (@libp2p/circuit-relay-v2), surfacing to the
275
+ // coordinator as a StreamResetError that fails consensus. After DCUtR upgrades
276
+ // a relayed link to direct, both connections briefly coexist — picking the
277
+ // direct one avoids riding the soon-to-be-reset circuit. We only fall back to
278
+ // the limited connection (with runOnLimitedConnection) when it is the only open
279
+ // path — the steady state for browsers and NATed peers before any upgrade.
280
+ const chosen = open.find(c => !this.isLimitedConnection(c)) ?? open[0];
281
+ if (chosen) {
242
282
  // runOnLimitedConnection: true is required to open a stream over a
243
283
  // circuit-relay (limited) connection — the steady-state path for
244
284
  // browsers and NATed peers. Without it, the warm relay connection
245
- // from a prior dialProtocol cannot be reused on subsequent RPCs.
246
- return open.newStream([protocol], {
285
+ // from a prior dialProtocol cannot be reused on subsequent RPCs. It is
286
+ // a harmless no-op on the preferred direct connection.
287
+ return chosen.newStream([protocol], {
247
288
  signal: options?.signal,
248
289
  runOnLimitedConnection: true,
249
290
  negotiateFully: false
@@ -272,6 +313,12 @@ export class Libp2pKeyPeerNetwork {
272
313
  const t0 = Date.now();
273
314
  const excludedSet = new Set((_options?.excludedPeers ?? []).map(p => p.toString()));
274
315
  const keyStr = this.toCacheKey(key).substring(0, 12);
316
+ // Tracks whether the network-membership filter excluded an UNCONFIRMED candidate
317
+ // — `foreign` (another network) OR `unknown` (not yet confirmed to serve this
318
+ // network) — during any attempt. If selection ultimately fails with self
319
+ // unavailable, this lets us surface NO_NETWORK_COORDINATOR (the real cause)
320
+ // instead of the generic NO_COORDINATOR_AVAILABLE.
321
+ let droppedUnconfirmedAnyAttempt = false;
275
322
  this.log('findCoordinator:start key=%s excluded=%o', keyStr, Array.from(excludedSet).map(s => s.substring(0, 12)));
276
323
  // honor cache if not excluded
277
324
  const cached = this.getCachedCoordinator(key);
@@ -301,7 +348,18 @@ export class Libp2pKeyPeerNetwork {
301
348
  && !(this.reputation?.isBanned(id)))
302
349
  .sort((a, b) => (this.reputation?.getScore(a) ?? 0) - (this.reputation?.getScore(b) ?? 0));
303
350
  this.log('findCoordinator:fret-connected key=%s count=%d peers=%o', keyStr, connectedFretIds.length, connectedFretIds.map(s => s.substring(0, 12)));
304
- const pick = connectedFretIds[0];
351
+ // Network-membership scoping (no-op when protocolPrefix is unset): only a peer
352
+ // CONFIRMED to serve this network ('serves') is eligible — both `foreign`
353
+ // (another network) and `unknown` (not yet identified) peers are excluded
354
+ // from selection. A cross-network peer is permanently 'unknown' (its
355
+ // namespaced identify never completes), so it is never gambled on; over the
356
+ // 3×500ms retry window a genuine same-network peer flips to 'serves' on a
357
+ // re-read of the peerStore and is selected normally on that attempt. Self
358
+ // always classifies as 'serves' and stays eligible.
359
+ const { ranked, droppedUnconfirmed } = await this.filterByMembership(connectedFretIds);
360
+ if (droppedUnconfirmed)
361
+ droppedUnconfirmedAnyAttempt = true;
362
+ const pick = ranked[0];
305
363
  if (pick) {
306
364
  const pid = peerIdFromString(pick);
307
365
  this.recordCoordinator(key, pid);
@@ -312,14 +370,24 @@ export class Libp2pKeyPeerNetwork {
312
370
  catch (err) {
313
371
  this.log('findCoordinator getNeighborIdsForKey failed - %o', err);
314
372
  }
315
- // fallback: prefer any existing connected peer that's not excluded or banned
316
- const connectedPick = connected
373
+ // fallback: prefer any existing connected peer that's not excluded or banned,
374
+ // scoped to this network's serving peers (a `foreign` or not-yet-confirmed
375
+ // `unknown` peer is never picked). Note this candidate set is built from
376
+ // connected REMOTE peers and never includes self, so when no serving peer is
377
+ // present selection falls through to the last-resort self-coordination block.
378
+ const connectedCandidates = connected
317
379
  .filter(p => !excludedSet.has(p.toString()) && !(this.reputation?.isBanned(p.toString())))
318
- .sort((a, b) => (this.reputation?.getScore(a.toString()) ?? 0) - (this.reputation?.getScore(b.toString()) ?? 0))[0];
380
+ .sort((a, b) => (this.reputation?.getScore(a.toString()) ?? 0) - (this.reputation?.getScore(b.toString()) ?? 0))
381
+ .map(p => p.toString());
382
+ const { ranked: connRanked, droppedUnconfirmed: connDroppedUnconfirmed } = await this.filterByMembership(connectedCandidates);
383
+ if (connDroppedUnconfirmed)
384
+ droppedUnconfirmedAnyAttempt = true;
385
+ const connectedPick = connRanked[0];
319
386
  if (connectedPick) {
320
- this.recordCoordinator(key, connectedPick);
387
+ const pid = peerIdFromString(connectedPick);
388
+ this.recordCoordinator(key, pid);
321
389
  this.log('findCoordinator:done key=%s ms=%d source=%s', keyStr, Date.now() - t0, 'connected-fallback');
322
- return connectedPick;
390
+ return pid;
323
391
  }
324
392
  // If no connections and not the last attempt, wait and retry
325
393
  if (connected.length === 0 && attempt < maxRetries - 1) {
@@ -349,6 +417,15 @@ export class Libp2pKeyPeerNetwork {
349
417
  this.log('findCoordinator:done key=%s ms=%d source=%s', keyStr, Date.now() - t0, 'self');
350
418
  return self;
351
419
  }
420
+ // Self is excluded and selection found no eligible peer. If the membership filter is
421
+ // the reason the candidate set emptied (the only other peers are `foreign` — serving
422
+ // a DIFFERENT network — or `unknown` — not yet confirmed to serve this network),
423
+ // surface a distinct, accurate cause instead of the generic codes below.
424
+ if (droppedUnconfirmedAnyAttempt) {
425
+ this.log('findCoordinator:no-network-coordinator key=%s prefix=%s self=%s', keyStr, this.protocolPrefix ?? '?', self.toString().substring(0, 12));
426
+ throw new FindCoordinatorError(FIND_COORDINATOR_ERROR_CODES.NO_NETWORK_COORDINATOR, `No coordinator available for key on network ${this.protocolPrefix ?? '?'}: ` +
427
+ `the remaining candidate peer(s) are foreign or not-yet-confirmed to serve this network's cluster/repo protocol.`);
428
+ }
352
429
  // Self is excluded. On a solo/bootstrap node (HWM<=1 and no other connected/FRET peers),
353
430
  // this means the caller already tried self and the retry has nowhere to go — surface a
354
431
  // distinct error so retry logic stops and the original first-attempt cause is preserved.
@@ -380,7 +457,7 @@ export class Libp2pKeyPeerNetwork {
380
457
  out.push(a);
381
458
  }
382
459
  catch (err) {
383
- console.warn('invalid multiaddr from connection', a, err);
460
+ this.log('WARN: invalid multiaddr from connection %s %o', a, err);
384
461
  }
385
462
  }
386
463
  return out;
@@ -389,19 +466,84 @@ export class Libp2pKeyPeerNetwork {
389
466
  const t0 = Date.now();
390
467
  const fret = this.getFret();
391
468
  const coord = await hashKey(key);
392
- const cohort = fret.assembleCohort(coord, this.clusterSize);
469
+ // When membership scoping is active, over-fetch a wider proximity band so the
470
+ // nearest peers that SERVE this network are in the candidate pool even if cross-
471
+ // network peers sit nearer the key (see membershipOverfetch).
472
+ const wants = this.protocolPrefix != null ? this.membershipOverfetch() : this.clusterSize;
473
+ const cohort = fret.assembleCohort(coord, wants);
393
474
  const keyStr = this.toCacheKey(key).substring(0, 12);
394
475
  this.log('findCluster:start key=%s', keyStr);
395
476
  // Include self in the cohort
396
477
  const selfId = this.libp2p.peerId.toString();
397
- const ids = Array.from(new Set([...cohort, selfId]));
478
+ let ids = Array.from(new Set([...cohort, selfId]));
479
+ // Network-membership scoping (no-op when protocolPrefix is unset): a cohort
480
+ // member that serves a DIFFERENT network's protocol can never negotiate THIS
481
+ // network's cluster/repo dial, so it guarantees a super-majority failure rather
482
+ // than contributing a promise. Drop such 'foreign' members; build the cohort from
483
+ // positively-'serves' members only and NEVER admit a not-yet-identified ('unknown')
484
+ // member. A permanently cross-network peer and a freshly-discovered same-network
485
+ // peer mid-identify are indistinguishable while 'unknown' (both have an empty
486
+ // peerStore protocol list), so admitting an 'unknown' on the strength of a viability
487
+ // floor risks pulling a cross-network contaminant into the cohort — its repo dial
488
+ // then negotiates a different network's protocol and the whole write fails. A fresh
489
+ // same-network peer is not starved: it flips to 'serves' once identify completes and
490
+ // is re-included on the caller's retry, and in the meantime a self-only cohort still
491
+ // completes the write under allowClusterDownsize (the default).
492
+ // Scoped path only: one peerStore read per cohort member yields both protocols
493
+ // (for membership classification here) and addresses (reused at backfill below),
494
+ // so a finally-selected member isn't fetched from the peerStore twice. Left
495
+ // undefined on the unscoped path, which never classifies membership.
496
+ let peerStoreRecords;
497
+ if (this.protocolPrefix != null) {
498
+ // `cohort` is the over-fetched nearest-first band. Classify each non-self
499
+ // member, preserving proximity order within each tier.
500
+ const nonSelf = cohort.filter(id => id !== selfId);
501
+ peerStoreRecords = await this.getPeerStoreRecordsByPeer(nonSelf);
502
+ const serves = [];
503
+ const unknown = [];
504
+ let foreignDropped = 0;
505
+ for (const id of nonSelf) {
506
+ const m = this.membershipOf(id, peerStoreRecords[id]?.protocols);
507
+ if (m === 'serves')
508
+ serves.push(id);
509
+ else if (m === 'unknown')
510
+ unknown.push(id);
511
+ else
512
+ foreignDropped++;
513
+ }
514
+ // Take the nearest `clusterSize - 1` SERVING peers. Self is ALWAYS added below and
515
+ // counts toward `clusterSize` (matching the unscoped path, where `assembleCohort`
516
+ // returns the nearest `clusterSize` peers INCLUDING self when self is near the key —
517
+ // the coordinator case), so reserving a slot for self keeps a healthy same-network
518
+ // cohort at exactly `clusterSize` members rather than `clusterSize + 1`. Over-sizing
519
+ // would inflate the super-majority promise count (ceil(peerCount * threshold)) above
520
+ // what the configured `clusterSize` intends and hurt write availability. 'unknown'
521
+ // members are never backfilled: an 'unknown' peer may be a permanently cross-network
522
+ // contaminant whose repo dial cannot negotiate this network's protocol, and a fresh
523
+ // same-network peer mid-identify is indistinguishable from it. We therefore admit
524
+ // only positively-'serves' peers; when self is the sole serving member the cohort is
525
+ // self-only, which completes the write under allowClusterDownsize (the default) and
526
+ // re-includes any legitimate peer as 'serves' on the caller's retry once identify
527
+ // completes. `unknown.length` is still computed above for the diagnostic log line.
528
+ const nonSelfTarget = Math.max(0, this.clusterSize - 1);
529
+ const others = serves.slice(0, nonSelfTarget);
530
+ ids = Array.from(new Set([selfId, ...others]));
531
+ this.log('findCluster:membership key=%s serves=%d unknown=%d foreignDropped=%d kept=%d', keyStr, serves.length, unknown.length, foreignDropped, ids.length);
532
+ }
398
533
  const connectedByPeer = this.getConnectedAddrsByPeer();
399
534
  const connectedPeerIds = Object.keys(connectedByPeer);
400
535
  // Backfill addresses from the peerStore for cohort members we don't have
401
536
  // a live connection to. The cohort is keyspace-determined and can include
402
537
  // peers we know-of but haven't dialed yet; without this backfill those
403
- // would be silently dropped.
404
- const peerStoreAddrs = await this.getPeerStoreAddrsByPeer(ids.filter(id => id !== selfId));
538
+ // would be silently dropped. On the scoped path reuse the addresses already
539
+ // read into `peerStoreRecords` above (no second store.get per member); on the
540
+ // unscoped path (no record map) do the single peerStore read as before.
541
+ const backfillIds = ids.filter(id => id !== selfId);
542
+ const peerStoreAddrs = peerStoreRecords
543
+ ? Object.fromEntries(backfillIds
544
+ .map(id => [id, peerStoreRecords[id]?.addrs ?? []])
545
+ .filter(([, addrs]) => addrs.length > 0))
546
+ : await this.getPeerStoreAddrsByPeer(backfillIds);
405
547
  this.log('findCluster key=%s fretCohort=%d connected=%d', keyStr, cohort.length, connectedPeerIds.length);
406
548
  if (verbose)
407
549
  this.log('findCluster:detail key=%s cohortPeers=%o connectedPeers=%o', keyStr, ids, connectedPeerIds);
@@ -458,5 +600,118 @@ export class Libp2pKeyPeerNetwork {
458
600
  }));
459
601
  return out;
460
602
  }
603
+ /**
604
+ * Single-pass peerStore read returning BOTH protocols and addresses per peer from one
605
+ * `store.get` call. Used on the membership-scoped `findCluster` hot path, where the
606
+ * cohort needs protocols (to classify membership) AND addresses (to backfill dial
607
+ * targets) for the same peers — reading them together avoids a second `store.get` per
608
+ * finally-selected member. Same error handling as {@link getPeerStoreProtocolsByPeer}
609
+ * and {@link getPeerStoreAddrsByPeer}: a missing peer or peerStore failure is left
610
+ * absent from the map (caller treats absent protocols as 'unknown', absent addrs as none).
611
+ */
612
+ async getPeerStoreRecordsByPeer(ids) {
613
+ const out = {};
614
+ const store = this.libp2p.peerStore;
615
+ if (!store?.get)
616
+ return out;
617
+ await Promise.all(ids.map(async (idStr) => {
618
+ try {
619
+ const pid = peerIdFromString(idStr);
620
+ const peer = await store.get(pid);
621
+ const addrs = (peer?.addresses ?? []).map(a => a.multiaddr.toString());
622
+ out[idStr] = { protocols: peer?.protocols ?? [], addrs };
623
+ }
624
+ catch {
625
+ // Unknown peer or peerStore failure — leave out of the map.
626
+ }
627
+ }));
628
+ return out;
629
+ }
630
+ /**
631
+ * Prefetch each peer's advertised protocol list from the libp2p peerStore.
632
+ * Returns a map from peer-id string to its protocols (empty array when the peer
633
+ * is absent or has not yet been identified). Mirrors {@link getPeerStoreAddrsByPeer};
634
+ * errors are swallowed so a peerStore hiccup degrades to "unknown" rather than throwing.
635
+ */
636
+ async getPeerStoreProtocolsByPeer(ids) {
637
+ const out = {};
638
+ const store = this.libp2p.peerStore;
639
+ if (!store?.get)
640
+ return out;
641
+ await Promise.all(ids.map(async (idStr) => {
642
+ try {
643
+ const pid = peerIdFromString(idStr);
644
+ const peer = await store.get(pid);
645
+ out[idStr] = peer?.protocols ?? [];
646
+ }
647
+ catch {
648
+ // Unknown peer or peerStore failure — leave out (treated as 'unknown').
649
+ }
650
+ }));
651
+ return out;
652
+ }
653
+ /**
654
+ * Over-fetch width for network-membership scoping. A cross-network peer can sit
655
+ * NEARER the key than a legitimate same-network peer and displace it from the
656
+ * nearest-`clusterSize` window, so when scoping is active we ask FRET for a wider
657
+ * proximity band and then keep the nearest peers that actually serve this network.
658
+ * (A ring polluted by more cross-network peers than this band is the domain of the
659
+ * separate FRET-side eviction follow-up; this band covers realistic co-location.)
660
+ */
661
+ membershipOverfetch() {
662
+ return Math.max(this.clusterSize * 4, this.clusterSize + 16);
663
+ }
664
+ /**
665
+ * Classify a peer's network membership from its advertised protocols. Self always
666
+ * `serves` (it trivially serves its own network). When no `protocolPrefix` is
667
+ * configured the filter is disabled and EVERY peer is reported `serves`, so all
668
+ * callers behave exactly as before this scoping was added.
669
+ */
670
+ membershipOf(idStr, protocols) {
671
+ if (this.protocolPrefix == null)
672
+ return 'serves';
673
+ if (idStr === this.libp2p.peerId.toString())
674
+ return 'serves';
675
+ if (protocols == null || protocols.length === 0)
676
+ return 'unknown';
677
+ if (protocols.includes(`${this.protocolPrefix}/cluster/1.0.0`)
678
+ || protocols.includes(`${this.protocolPrefix}/repo/1.0.0`))
679
+ return 'serves';
680
+ return 'foreign';
681
+ }
682
+ /**
683
+ * Scope a reputation-ordered candidate id list to this network for COORDINATOR
684
+ * selection: keep ONLY peers confirmed to serve this network (`serves`, which always
685
+ * includes self), dropping both `foreign` peers (serving another network) and
686
+ * `unknown` peers (peerStore protocol list empty — not yet confirmed). Incoming
687
+ * (reputation) order is preserved among the surviving `serves` peers. A no-op
688
+ * (returns the input unchanged, no drops) when `protocolPrefix` is unset or the list
689
+ * is empty — the membership-disabled path is therefore untouched.
690
+ *
691
+ * `droppedUnconfirmed` reports whether any candidate was excluded because it was not
692
+ * confirmed to serve this network — `foreign` OR `unknown` under scoping — so the
693
+ * caller can surface a distinct "no network coordinator" failure rather than a generic
694
+ * one. An `unknown` peer is not gambled on as coordinator: a permanent cross-network
695
+ * contaminant and a fresh same-network peer mid-identify are indistinguishable at an
696
+ * instant, but the filter re-reads the peerStore on every retry attempt, so a genuine
697
+ * same-network peer that completes `identify` within the retry window flips to `serves`
698
+ * and is selected normally on that attempt.
699
+ */
700
+ async filterByMembership(ids) {
701
+ if (this.protocolPrefix == null || ids.length === 0)
702
+ return { ranked: ids, droppedUnconfirmed: false };
703
+ const selfStr = this.libp2p.peerId.toString();
704
+ const protocolsByPeer = await this.getPeerStoreProtocolsByPeer(ids.filter(id => id !== selfStr));
705
+ const serves = [];
706
+ let droppedUnconfirmed = false;
707
+ for (const id of ids) {
708
+ const m = this.membershipOf(id, protocolsByPeer[id]);
709
+ if (m === 'serves')
710
+ serves.push(id);
711
+ else
712
+ droppedUnconfirmed = true;
713
+ }
714
+ return { ranked: serves, droppedUnconfirmed };
715
+ }
461
716
  }
462
717
  //# sourceMappingURL=libp2p-key-network.js.map