@optimystic/db-p2p 1.0.0-beta.3 → 1.0.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 (234) hide show
  1. package/dist/src/cluster/block-transfer-service.d.ts +73 -1
  2. package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
  3. package/dist/src/cluster/block-transfer-service.js +70 -3
  4. package/dist/src/cluster/block-transfer-service.js.map +1 -1
  5. package/dist/src/cluster/block-transfer.d.ts +16 -1
  6. package/dist/src/cluster/block-transfer.d.ts.map +1 -1
  7. package/dist/src/cluster/block-transfer.js +73 -72
  8. package/dist/src/cluster/block-transfer.js.map +1 -1
  9. package/dist/src/cluster/client.d.ts +4 -6
  10. package/dist/src/cluster/client.d.ts.map +1 -1
  11. package/dist/src/cluster/client.js +8 -13
  12. package/dist/src/cluster/client.js.map +1 -1
  13. package/dist/src/cluster/cluster-policy.d.ts +2 -1
  14. package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
  15. package/dist/src/cluster/cluster-policy.js.map +1 -1
  16. package/dist/src/cluster/cluster-repo.d.ts +126 -6
  17. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  18. package/dist/src/cluster/cluster-repo.js +378 -50
  19. package/dist/src/cluster/cluster-repo.js.map +1 -1
  20. package/dist/src/cluster/commit-proof.js +2 -2
  21. package/dist/src/cluster/race-resolution.d.ts +51 -7
  22. package/dist/src/cluster/race-resolution.d.ts.map +1 -1
  23. package/dist/src/cluster/race-resolution.js +71 -7
  24. package/dist/src/cluster/race-resolution.js.map +1 -1
  25. package/dist/src/cluster/rebalance-monitor.d.ts +67 -5
  26. package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
  27. package/dist/src/cluster/rebalance-monitor.js +120 -26
  28. package/dist/src/cluster/rebalance-monitor.js.map +1 -1
  29. package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
  30. package/dist/src/cluster/spread-on-churn.js +34 -47
  31. package/dist/src/cluster/spread-on-churn.js.map +1 -1
  32. package/dist/src/cohort-topic/host.d.ts.map +1 -1
  33. package/dist/src/cohort-topic/host.js +8 -6
  34. package/dist/src/cohort-topic/host.js.map +1 -1
  35. package/dist/src/cohort-topic/membership-source.js +1 -1
  36. package/dist/src/cohort-topic/membership-source.js.map +1 -1
  37. package/dist/src/cohort-topic/peer-codec.d.ts.map +1 -1
  38. package/dist/src/cohort-topic/peer-codec.js +5 -2
  39. package/dist/src/cohort-topic/peer-codec.js.map +1 -1
  40. package/dist/src/cohort-topic/stream-util.d.ts +24 -3
  41. package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
  42. package/dist/src/cohort-topic/stream-util.js +36 -3
  43. package/dist/src/cohort-topic/stream-util.js.map +1 -1
  44. package/dist/src/cohort-topic/topic-router.d.ts +9 -0
  45. package/dist/src/cohort-topic/topic-router.d.ts.map +1 -1
  46. package/dist/src/cohort-topic/topic-router.js +12 -2
  47. package/dist/src/cohort-topic/topic-router.js.map +1 -1
  48. package/dist/src/dispute/client.d.ts.map +1 -1
  49. package/dist/src/dispute/client.js +15 -3
  50. package/dist/src/dispute/client.js.map +1 -1
  51. package/dist/src/index.d.ts +5 -0
  52. package/dist/src/index.d.ts.map +1 -1
  53. package/dist/src/index.js +5 -0
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/libp2p-key-network.d.ts +78 -17
  56. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  57. package/dist/src/libp2p-key-network.js +234 -147
  58. package/dist/src/libp2p-key-network.js.map +1 -1
  59. package/dist/src/libp2p-node-base.d.ts +35 -1
  60. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  61. package/dist/src/libp2p-node-base.js +151 -26
  62. package/dist/src/libp2p-node-base.js.map +1 -1
  63. package/dist/src/libp2p-node-rn.d.ts +2 -2
  64. package/dist/src/libp2p-node-rn.d.ts.map +1 -1
  65. package/dist/src/libp2p-node-rn.js.map +1 -1
  66. package/dist/src/libp2p-node.d.ts +2 -2
  67. package/dist/src/libp2p-node.d.ts.map +1 -1
  68. package/dist/src/libp2p-node.js.map +1 -1
  69. package/dist/src/logger.d.ts.map +1 -1
  70. package/dist/src/logger.js +6 -1
  71. package/dist/src/logger.js.map +1 -1
  72. package/dist/src/matchmaking/query-transport.d.ts +3 -4
  73. package/dist/src/matchmaking/query-transport.d.ts.map +1 -1
  74. package/dist/src/matchmaking/query-transport.js +9 -9
  75. package/dist/src/matchmaking/query-transport.js.map +1 -1
  76. package/dist/src/network/network-manager-service.d.ts +10 -5
  77. package/dist/src/network/network-manager-service.d.ts.map +1 -1
  78. package/dist/src/network/network-manager-service.js +11 -2
  79. package/dist/src/network/network-manager-service.js.map +1 -1
  80. package/dist/src/network/relay-reservation.d.ts +170 -0
  81. package/dist/src/network/relay-reservation.d.ts.map +1 -0
  82. package/dist/src/network/relay-reservation.js +527 -0
  83. package/dist/src/network/relay-reservation.js.map +1 -0
  84. package/dist/src/pend-validation.d.ts +3 -2
  85. package/dist/src/pend-validation.d.ts.map +1 -1
  86. package/dist/src/pend-validation.js +3 -2
  87. package/dist/src/pend-validation.js.map +1 -1
  88. package/dist/src/protocol-client.d.ts +8 -1
  89. package/dist/src/protocol-client.d.ts.map +1 -1
  90. package/dist/src/protocol-client.js +11 -0
  91. package/dist/src/protocol-client.js.map +1 -1
  92. package/dist/src/reactivity/forwarder-host.d.ts +2 -1
  93. package/dist/src/reactivity/forwarder-host.d.ts.map +1 -1
  94. package/dist/src/reactivity/forwarder-host.js +2 -1
  95. package/dist/src/reactivity/forwarder-host.js.map +1 -1
  96. package/dist/src/reactivity/origination-manager.d.ts +3 -3
  97. package/dist/src/reactivity/origination-manager.js +3 -3
  98. package/dist/src/reactivity/recover-transport.d.ts +22 -14
  99. package/dist/src/reactivity/recover-transport.d.ts.map +1 -1
  100. package/dist/src/reactivity/recover-transport.js +32 -19
  101. package/dist/src/reactivity/recover-transport.js.map +1 -1
  102. package/dist/src/reactivity/subscription-manager.d.ts +2 -2
  103. package/dist/src/reactivity/topic-bytes.d.ts +6 -7
  104. package/dist/src/reactivity/topic-bytes.d.ts.map +1 -1
  105. package/dist/src/reactivity/topic-bytes.js +8 -9
  106. package/dist/src/reactivity/topic-bytes.js.map +1 -1
  107. package/dist/src/repo/client.d.ts +5 -2
  108. package/dist/src/repo/client.d.ts.map +1 -1
  109. package/dist/src/repo/client.js +49 -40
  110. package/dist/src/repo/client.js.map +1 -1
  111. package/dist/src/repo/cluster-coordinator.d.ts +77 -5
  112. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  113. package/dist/src/repo/cluster-coordinator.js +131 -20
  114. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  115. package/dist/src/repo/coordinator-repo.d.ts +238 -122
  116. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  117. package/dist/src/repo/coordinator-repo.js +625 -472
  118. package/dist/src/repo/coordinator-repo.js.map +1 -1
  119. package/dist/src/repo/i-under-replication-ledger.d.ts +87 -0
  120. package/dist/src/repo/i-under-replication-ledger.d.ts.map +1 -0
  121. package/dist/src/repo/i-under-replication-ledger.js +2 -0
  122. package/dist/src/repo/i-under-replication-ledger.js.map +1 -0
  123. package/dist/src/repo/kv-under-replication-ledger.d.ts +91 -0
  124. package/dist/src/repo/kv-under-replication-ledger.d.ts.map +1 -0
  125. package/dist/src/repo/kv-under-replication-ledger.js +287 -0
  126. package/dist/src/repo/kv-under-replication-ledger.js.map +1 -0
  127. package/dist/src/repo/responsibility.d.ts +37 -0
  128. package/dist/src/repo/responsibility.d.ts.map +1 -0
  129. package/dist/src/repo/responsibility.js +37 -0
  130. package/dist/src/repo/responsibility.js.map +1 -0
  131. package/dist/src/repo/service.d.ts +44 -15
  132. package/dist/src/repo/service.d.ts.map +1 -1
  133. package/dist/src/repo/service.js +64 -27
  134. package/dist/src/repo/service.js.map +1 -1
  135. package/dist/src/repo/stuck-reservation.d.ts +127 -0
  136. package/dist/src/repo/stuck-reservation.d.ts.map +1 -0
  137. package/dist/src/repo/stuck-reservation.js +179 -0
  138. package/dist/src/repo/stuck-reservation.js.map +1 -0
  139. package/dist/src/repo/under-replication-drain.d.ts +208 -0
  140. package/dist/src/repo/under-replication-drain.d.ts.map +1 -0
  141. package/dist/src/repo/under-replication-drain.js +461 -0
  142. package/dist/src/repo/under-replication-drain.js.map +1 -0
  143. package/dist/src/rn.d.ts +5 -0
  144. package/dist/src/rn.d.ts.map +1 -1
  145. package/dist/src/rn.js +5 -0
  146. package/dist/src/rn.js.map +1 -1
  147. package/dist/src/storage/block-latch.d.ts +3 -2
  148. package/dist/src/storage/block-latch.d.ts.map +1 -1
  149. package/dist/src/storage/block-latch.js +7 -4
  150. package/dist/src/storage/block-latch.js.map +1 -1
  151. package/dist/src/storage/block-storage.d.ts +45 -3
  152. package/dist/src/storage/block-storage.d.ts.map +1 -1
  153. package/dist/src/storage/block-storage.js +156 -9
  154. package/dist/src/storage/block-storage.js.map +1 -1
  155. package/dist/src/storage/i-block-storage.d.ts +57 -5
  156. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  157. package/dist/src/storage/i-block-storage.js.map +1 -1
  158. package/dist/src/storage/pending-claim.d.ts +167 -0
  159. package/dist/src/storage/pending-claim.d.ts.map +1 -0
  160. package/dist/src/storage/pending-claim.js +156 -0
  161. package/dist/src/storage/pending-claim.js.map +1 -0
  162. package/dist/src/storage/raw-store-codec.d.ts +13 -0
  163. package/dist/src/storage/raw-store-codec.d.ts.map +1 -1
  164. package/dist/src/storage/raw-store-codec.js +24 -3
  165. package/dist/src/storage/raw-store-codec.js.map +1 -1
  166. package/dist/src/storage/restoration-coordinator.d.ts.map +1 -1
  167. package/dist/src/storage/restoration-coordinator.js +2 -1
  168. package/dist/src/storage/restoration-coordinator.js.map +1 -1
  169. package/dist/src/storage/ring-shift-coordinator.d.ts.map +1 -1
  170. package/dist/src/storage/ring-shift-coordinator.js +3 -3
  171. package/dist/src/storage/ring-shift-coordinator.js.map +1 -1
  172. package/dist/src/storage/storage-repo.d.ts +122 -5
  173. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  174. package/dist/src/storage/storage-repo.js +245 -68
  175. package/dist/src/storage/storage-repo.js.map +1 -1
  176. package/dist/src/storage/struct.d.ts +58 -0
  177. package/dist/src/storage/struct.d.ts.map +1 -1
  178. package/dist/src/testing/mesh-harness.d.ts +77 -11
  179. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  180. package/dist/src/testing/mesh-harness.js +226 -100
  181. package/dist/src/testing/mesh-harness.js.map +1 -1
  182. package/package.json +2 -2
  183. package/readme.md +27 -3
  184. package/src/cluster/block-transfer-service.ts +132 -4
  185. package/src/cluster/block-transfer.ts +79 -84
  186. package/src/cluster/client.ts +115 -119
  187. package/src/cluster/cluster-policy.ts +2 -1
  188. package/src/cluster/cluster-repo.ts +422 -56
  189. package/src/cluster/commit-proof.ts +2 -2
  190. package/src/cluster/race-resolution.ts +76 -7
  191. package/src/cluster/rebalance-monitor.ts +677 -539
  192. package/src/cluster/spread-on-churn.ts +34 -54
  193. package/src/cohort-topic/host.ts +8 -6
  194. package/src/cohort-topic/membership-source.ts +1 -1
  195. package/src/cohort-topic/peer-codec.ts +34 -31
  196. package/src/cohort-topic/stream-util.ts +39 -4
  197. package/src/cohort-topic/topic-router.ts +12 -2
  198. package/src/dispute/client.ts +17 -7
  199. package/src/index.ts +64 -59
  200. package/src/libp2p-key-network.ts +1377 -1260
  201. package/src/libp2p-node-base.ts +186 -27
  202. package/src/libp2p-node-rn.ts +31 -30
  203. package/src/libp2p-node.ts +37 -36
  204. package/src/logger.ts +6 -1
  205. package/src/matchmaking/query-transport.ts +9 -9
  206. package/src/network/network-manager-service.ts +16 -5
  207. package/src/network/relay-reservation.ts +636 -0
  208. package/src/pend-validation.ts +3 -2
  209. package/src/protocol-client.ts +13 -1
  210. package/src/reactivity/forwarder-host.ts +2 -1
  211. package/src/reactivity/origination-manager.ts +3 -3
  212. package/src/reactivity/recover-transport.ts +39 -22
  213. package/src/reactivity/subscription-manager.ts +2 -2
  214. package/src/reactivity/topic-bytes.ts +35 -37
  215. package/src/repo/client.ts +172 -173
  216. package/src/repo/cluster-coordinator.ts +145 -19
  217. package/src/repo/coordinator-repo.ts +652 -505
  218. package/src/repo/i-under-replication-ledger.ts +89 -0
  219. package/src/repo/kv-under-replication-ledger.ts +302 -0
  220. package/src/repo/responsibility.ts +48 -0
  221. package/src/repo/service.ts +76 -39
  222. package/src/repo/stuck-reservation.ts +224 -0
  223. package/src/repo/under-replication-drain.ts +582 -0
  224. package/src/rn.ts +74 -69
  225. package/src/storage/block-latch.ts +7 -4
  226. package/src/storage/block-storage.ts +161 -10
  227. package/src/storage/i-block-storage.ts +60 -5
  228. package/src/storage/pending-claim.ts +200 -0
  229. package/src/storage/raw-store-codec.ts +26 -3
  230. package/src/storage/restoration-coordinator.ts +212 -211
  231. package/src/storage/ring-shift-coordinator.ts +271 -271
  232. package/src/storage/storage-repo.ts +280 -72
  233. package/src/storage/struct.ts +58 -0
  234. package/src/testing/mesh-harness.ts +293 -112
@@ -0,0 +1,527 @@
1
+ /**
2
+ * Keeps a relay-only node's circuit reservation alive.
3
+ *
4
+ * A phone or browser reaches its group only through a relay server: it listens on
5
+ * `<relay address>/p2p/<relay id>/p2p-circuit`, holds a slot ("reservation") on that relay, and
6
+ * advertises the circuit address other peers dial. libp2p asks the relay for that slot once, from
7
+ * inside `start()`, and never again. The slot is lost, and the node stays unreachable until the
8
+ * app restarts, whenever the relay restarts, the connection to it drops, or libp2p's own routine
9
+ * renewal of the slot runs — all three verified against the installed `@libp2p/circuit-relay-v2`
10
+ * 4.1.3 (see the `NOTE:` at {@link findCircuitRelayTransport} for the exact mechanics).
11
+ *
12
+ * This module gives the node its own supervisor for every such relay, in two parts:
13
+ *
14
+ * 1. {@link planRelayListenAddrs} rewrites each relay-naming circuit listen address into a bare
15
+ * `/p2p-circuit` entry and records the relay it named. libp2p treats the bare shape as a
16
+ * "search" listener: it registers a pending slot and publishes any `discovered` reservation
17
+ * that fills it, including the re-created one after a renewal. A listener on the relay-naming
18
+ * ("configured") shape publishes only from inside its own `listen()`, so nothing done after
19
+ * start can ever bring its address back — which is why the rewrite is not optional.
20
+ * 2. {@link superviseRelayReservation} runs one supervisor per recorded relay. It dials the relay,
21
+ * asks the circuit-relay transport's reservation store for a `discovered` slot on it, waits
22
+ * until the node advertises a circuit address through it, and repeats that whenever the address
23
+ * goes away. "Held" is judged per relay ({@link routesThroughRelay}), so one relay's slot never
24
+ * satisfies another relay's supervisor.
25
+ *
26
+ * `createLibp2pNodeBase` applies the plan before building libp2p's options, starts the supervisors
27
+ * first thing after `node.start()`, and awaits their first drives so a relay that cannot be
28
+ * reserved still rejects node creation, as it did when libp2p reserved from `listen()`.
29
+ *
30
+ * A host that passes a bare `/p2p-circuit` itself is left alone and not supervised: it owns that
31
+ * reservation (Sereus runs its own supervisor over exactly that shape).
32
+ *
33
+ * Everything here is fail-soft: a drive never throws, a stopped supervisor never schedules
34
+ * anything, and every timer is `unref`'d so a pending retry cannot keep a Node process alive.
35
+ * Adapted from `sereus/packages/cadre-core/src/relay-reservation.ts`, reduced to what this node
36
+ * needs: one relay per supervisor, event-driven re-checks, and no status surface.
37
+ */
38
+ import { multiaddr } from '@multiformats/multiaddr';
39
+ import { peerIdFromString } from '@libp2p/peer-id';
40
+ import { routesThroughRelay } from '../peer-address-book.js';
41
+ import { createLogger } from '../logger.js';
42
+ const log = createLogger('relay-reservation');
43
+ /** The bare "search" circuit listen address: one pending slot, filled by whoever asks for it. */
44
+ export const CIRCUIT_SEARCH_LISTEN_ADDR = '/p2p-circuit';
45
+ /**
46
+ * Split a node's listen addresses into what libp2p should listen on and which relays this node
47
+ * must supervise.
48
+ *
49
+ * A relay-naming circuit listen address — exactly one `p2p-circuit` component, last, with a `p2p`
50
+ * component (the relay's peer id) immediately before it — becomes one bare
51
+ * {@link CIRCUIT_SEARCH_LISTEN_ADDR} entry plus one supervised relay. libp2p keeps listen addresses
52
+ * as a plain array with no de-duplication, so N such entries give N bare listeners and N pending
53
+ * slots. Two entries naming one relay (by peer id, whatever the spelling) give one of each; the
54
+ * first entry's dial address wins. Everything else passes through untouched: a host's own bare
55
+ * `/p2p-circuit` (the host owns that reservation), TCP and WebSocket entries, and anything
56
+ * malformed or multi-hop, which libp2p rejects itself.
57
+ */
58
+ export function planRelayListenAddrs(listenAddrs) {
59
+ const listen = [];
60
+ const supervised = [];
61
+ const seen = new Set();
62
+ for (const addr of listenAddrs) {
63
+ const relay = relayNamedByListenAddr(addr);
64
+ if (relay === undefined) {
65
+ listen.push(addr);
66
+ continue;
67
+ }
68
+ if (seen.has(relay.peerId))
69
+ continue;
70
+ seen.add(relay.peerId);
71
+ supervised.push(relay);
72
+ listen.push(CIRCUIT_SEARCH_LISTEN_ADDR);
73
+ }
74
+ return { listenAddrs: listen, supervisedRelays: supervised };
75
+ }
76
+ /** The relay a listen address names, or `undefined` when it is not a relay-naming circuit listen address. */
77
+ function relayNamedByListenAddr(addr) {
78
+ let components;
79
+ try {
80
+ components = multiaddr(addr).getComponents();
81
+ }
82
+ catch {
83
+ return undefined;
84
+ }
85
+ const circuitHops = components.filter(c => c.name === 'p2p-circuit').length;
86
+ const last = components[components.length - 1];
87
+ const relayHop = components[components.length - 2];
88
+ if (circuitHops !== 1 || last?.name !== 'p2p-circuit' || relayHop?.name !== 'p2p' || relayHop.value === undefined) {
89
+ return undefined;
90
+ }
91
+ let peerId;
92
+ try {
93
+ peerId = peerIdFromString(relayHop.value).toString();
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ return { dialAddr: multiaddr(components.slice(0, -1)).toString(), peerId };
99
+ }
100
+ /**
101
+ * The running node's circuit-relay transport, or `null` when it has none.
102
+ *
103
+ * NOTE: the ONE place this package reaches libp2p internals for relay reservations, pinned against
104
+ * `libp2p` 3.1.3 and `@libp2p/circuit-relay-v2` 4.1.3 by `test/relay-reservation-seam.spec.ts`,
105
+ * which must fail loudly if an upgrade moves any of it. There is no public route: the `Libp2p`
106
+ * interface has no `listen`, and nothing public exposes the reservation store. `node.components`
107
+ * is a real public field on libp2p's node class, just not on the interface; `transportManager`
108
+ * is a component; the circuit-relay transport is the one whose `reservationStore` has `addRelay`
109
+ * and `hasReservation`. What the store does on 4.1.3, read from
110
+ * `node_modules/@libp2p/circuit-relay-v2/src/transport/`: `listener.ts` publishes a `configured`
111
+ * reservation only from inside `listen()` and ignores it on `relay:created-reservation`, while a
112
+ * bare listener publishes any `discovered` reservation carrying its pending id; and
113
+ * `reservation-store.ts` drops a reservation on `connection:close`, re-queues the pending id only
114
+ * for `discovered` ones, removes-then-recreates on refresh, and adds a relay whose request failed
115
+ * with `DialError` or `UnsupportedProtocolError` to the private `relayFilter`, refusing it
116
+ * afterwards with "The relay was previously invalid" until the filter is reset.
117
+ */
118
+ export function findCircuitRelayTransport(node) {
119
+ for (const transport of nodeTransports(node)) {
120
+ const store = transport.reservationStore;
121
+ if (isReservationStore(store)) {
122
+ return { reservationStore: store };
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+ function nodeTransports(node) {
128
+ const manager = node.components?.transportManager;
129
+ if (typeof manager?.getTransports !== 'function') {
130
+ log.error('no transportManager.getTransports() on this node — libp2p internals moved?');
131
+ return [];
132
+ }
133
+ try {
134
+ return manager.getTransports();
135
+ }
136
+ catch (err) {
137
+ log.error('transportManager.getTransports() threw: %o', err);
138
+ return [];
139
+ }
140
+ }
141
+ function isReservationStore(value) {
142
+ if (typeof value !== 'object' || value === null)
143
+ return false;
144
+ const store = value;
145
+ return typeof store.addRelay === 'function' && typeof store.hasReservation === 'function';
146
+ }
147
+ /**
148
+ * Fail node creation, before anything starts, when a listen address names a relay but no transport
149
+ * could ever reserve on it. libp2p would reject the bare `/p2p-circuit` entry itself during
150
+ * `start()`, with a generic unsupported-listen-address error; this names the actual omission.
151
+ */
152
+ export function assertCircuitRelayTransport(node, relays) {
153
+ if (relays.length === 0)
154
+ return;
155
+ if (findCircuitRelayTransport(node) !== null)
156
+ return;
157
+ throw new Error(`listen address names a relay (${relays[0].dialAddr}) but the node has no circuit-relay transport — add circuitRelayTransport() to transports`);
158
+ }
159
+ /**
160
+ * Fail node creation, before anything is built, when a listen address names a relay but
161
+ * `announceAddrs` is set. libp2p advertises ONLY the announce set then (the readme's "replaces the
162
+ * advertised set entirely"), so the circuit address through the relay could never appear in
163
+ * `node.getMultiaddrs()`, nobody could learn it, and every supervisor drive would time out waiting
164
+ * for it: node creation would reject after a full drive deadline with a message about publishing
165
+ * rather than about the configuration. `appendAnnounceAddrs` keeps the listener addresses and is fine.
166
+ */
167
+ export function assertRelayAddrsAdvertisable(relays, announceAddrs) {
168
+ if (relays.length === 0 || announceAddrs === undefined || announceAddrs.length === 0)
169
+ return;
170
+ throw new Error(`listen address names a relay (${relays[0].dialAddr}) but announceAddrs replaces the advertised address set, so the circuit address through it could never be advertised — drop announceAddrs or use appendAnnounceAddrs`);
171
+ }
172
+ /**
173
+ * Forget that a relay ever failed a reservation request, so the next request is actually made
174
+ * rather than refused with "The relay was previously invalid".
175
+ *
176
+ * The store resets its filter when a reservation is removed while a pending slot exists, which a
177
+ * bare listener always has, so this is a belt-and-braces step: the poisoning path this guards is a
178
+ * failed request from libp2p's own relay discovery while the relay was down. Fails soft in every
179
+ * direction; returns whether an entry was actually removed (for specs).
180
+ */
181
+ export function clearRelayFilterEntry(store, relayPeerId) {
182
+ const filter = store.relayFilter;
183
+ if (typeof filter?.remove !== 'function')
184
+ return false;
185
+ try {
186
+ return filter.remove(relayPeerId.toMultihash().bytes);
187
+ }
188
+ catch (err) {
189
+ log('could not clear the relayFilter entry for %s: %o', relayPeerId.toString(), err);
190
+ return false;
191
+ }
192
+ }
193
+ /** Deadline for one drive: dial, reservation request and the wait for the address share it. */
194
+ export const DEFAULT_RELAY_DRIVE_TIMEOUT_MS = 10_000;
195
+ /** Gap between liveness checks while the reservation is held; a fallback in case an event is missed. */
196
+ export const DEFAULT_RELAY_CHECK_MS = 5_000;
197
+ /** Backoff before the first retry after a failed drive. */
198
+ export const DEFAULT_RELAY_MIN_BACKOFF_MS = 1_000;
199
+ /**
200
+ * Backoff ceiling. It bounds recovery time after a long relay outage, because nothing else
201
+ * reconnects a lone phone to its relay; a WebSocket dial every 30 s during an outage is cheap.
202
+ */
203
+ export const DEFAULT_RELAY_MAX_BACKOFF_MS = 30_000;
204
+ /** How often a drive re-reads the address list while waiting for the address to be published. */
205
+ export const DEFAULT_RELAY_POLL_MS = 100;
206
+ /**
207
+ * Keep asking `relay` for a reservation until one is held, then keep watching that it still is.
208
+ *
209
+ * Drives run on: the first tick, right away; libp2p's `self:peer:update` (the listener withdrew or
210
+ * added addresses); `peer:connect` for this relay (a dial by anything on this node reconnected it,
211
+ * which turns a partner's dial through the relay into an immediate re-reserve, and resets the
212
+ * backoff); a liveness poll every `checkMs` while held; and a doubling backoff from `minBackoffMs`
213
+ * to `maxBackoffMs` after a failed drive. Drives are serialized per relay: a trigger that arrives
214
+ * mid-drive does nothing by itself, because every drive ends with the same re-check and reschedule.
215
+ *
216
+ * `HadEnoughRelaysError` from the reservation store means this node's pending slot was already
217
+ * filled with a reservation on a different relay (libp2p's relay discovery, see the accepted
218
+ * tradeoff in `libp2p-node-base.ts`); it is logged once per episode and retried at the backoff cap, since it
219
+ * recovers on its own only if that other reservation drops.
220
+ */
221
+ export function superviseRelayReservation(node, relay, opts = {}) {
222
+ return new RelayReservationLoop(node, relay, opts);
223
+ }
224
+ /** One {@link superviseRelayReservation} per relay, started immediately. */
225
+ export function superviseRelayReservations(node, relays, opts = {}) {
226
+ const supervisors = relays.map(relay => superviseRelayReservation(node, relay, opts));
227
+ return {
228
+ supervisors,
229
+ async awaitFirstDrives() {
230
+ const outcomes = await Promise.all(supervisors.map(async (s) => ({ relay: s.relay, reason: await s.firstDrive })));
231
+ const failed = outcomes.filter(o => o.reason !== null);
232
+ if (failed.length === 0)
233
+ return;
234
+ throw new Error(failed.map(f => `could not reserve a circuit on relay ${f.relay.dialAddr}: ${f.reason}`).join('; '));
235
+ },
236
+ stop() {
237
+ for (const s of supervisors)
238
+ s.stop();
239
+ }
240
+ };
241
+ }
242
+ /** Sentinel resolved by the deadline race around `addRelay`, which takes no signal. */
243
+ const DEADLINE_PASSED = Symbol('relay-reservation-deadline-passed');
244
+ /** Milliseconds left until `deadline`, never negative. */
245
+ const remaining = (deadline) => Math.max(0, deadline - Date.now());
246
+ const errorMessage = (err) => (err instanceof Error ? err.message : String(err));
247
+ /** A pending timer must not keep a stopped Node process alive; browsers and React Native have no `unref`. */
248
+ function unref(timer) {
249
+ timer.unref?.();
250
+ }
251
+ /** Resolve after `ms`, on an unref'd timer. */
252
+ function delay(ms) {
253
+ return new Promise(resolve => {
254
+ unref(setTimeout(resolve, ms));
255
+ });
256
+ }
257
+ /** The self-rescheduling loop behind {@link superviseRelayReservation}. */
258
+ class RelayReservationLoop {
259
+ node;
260
+ relay;
261
+ firstDrive;
262
+ relayPeerId;
263
+ driveTimeoutMs;
264
+ checkMs;
265
+ minBackoffMs;
266
+ maxBackoffMs;
267
+ pollMs;
268
+ backoffMs;
269
+ timer = null;
270
+ stopped = false;
271
+ inFlight = false;
272
+ nextTickAtMs = null;
273
+ failure = null;
274
+ dialAbort = null;
275
+ slotTakenLogged = false;
276
+ settleFirstDrive = () => { };
277
+ firstDriveSettled = false;
278
+ constructor(node, relay, opts) {
279
+ this.node = node;
280
+ this.relay = relay;
281
+ this.relayPeerId = peerIdFromString(relay.peerId);
282
+ // Clamped away from 0: a caller-supplied 0 would spin the loop hot.
283
+ this.driveTimeoutMs = Math.max(1, opts.driveTimeoutMs ?? DEFAULT_RELAY_DRIVE_TIMEOUT_MS);
284
+ this.checkMs = Math.max(1, opts.checkMs ?? DEFAULT_RELAY_CHECK_MS);
285
+ this.minBackoffMs = Math.max(1, opts.minBackoffMs ?? DEFAULT_RELAY_MIN_BACKOFF_MS);
286
+ this.maxBackoffMs = Math.max(this.minBackoffMs, opts.maxBackoffMs ?? DEFAULT_RELAY_MAX_BACKOFF_MS);
287
+ this.pollMs = Math.max(1, opts.pollMs ?? DEFAULT_RELAY_POLL_MS);
288
+ this.backoffMs = this.minBackoffMs;
289
+ this.firstDrive = new Promise(resolve => {
290
+ this.settleFirstDrive = resolve;
291
+ });
292
+ node.addEventListener('self:peer:update', this.onSelfPeerUpdate);
293
+ node.addEventListener('peer:connect', this.onPeerConnect);
294
+ void this.tick('start');
295
+ }
296
+ get held() {
297
+ return this.node.getMultiaddrs().some(addr => routesThroughRelay(addr.toString(), this.relay.peerId, log));
298
+ }
299
+ get driving() {
300
+ return this.inFlight;
301
+ }
302
+ get retryAtMs() {
303
+ return this.nextTickAtMs;
304
+ }
305
+ get lastError() {
306
+ return this.failure;
307
+ }
308
+ stop() {
309
+ if (this.stopped)
310
+ return;
311
+ this.stopped = true;
312
+ this.clearTimer();
313
+ this.nextTickAtMs = null;
314
+ this.node.removeEventListener('self:peer:update', this.onSelfPeerUpdate);
315
+ this.node.removeEventListener('peer:connect', this.onPeerConnect);
316
+ this.dialAbort?.abort();
317
+ this.settleFirst('the relay reservation supervisor was stopped before its first drive settled');
318
+ }
319
+ onSelfPeerUpdate = () => {
320
+ this.wake('self:peer:update', false);
321
+ };
322
+ onPeerConnect = (evt) => {
323
+ if (!evt.detail.equals(this.relayPeerId))
324
+ return;
325
+ this.wake('peer:connect', true);
326
+ };
327
+ /**
328
+ * Something may have changed the reservation: re-check now. Mid-drive, only the backoff reset
329
+ * takes effect — the drive's own tail re-checks and reschedules, so nothing is lost.
330
+ */
331
+ wake(trigger, resetBackoff) {
332
+ if (this.stopped)
333
+ return;
334
+ if (resetBackoff)
335
+ this.backoffMs = this.minBackoffMs;
336
+ if (this.inFlight)
337
+ return;
338
+ this.clearTimer();
339
+ void this.tick(trigger);
340
+ }
341
+ async tick(trigger) {
342
+ if (this.stopped || this.inFlight)
343
+ return;
344
+ this.nextTickAtMs = null;
345
+ if (this.held) {
346
+ this.onHeld();
347
+ return;
348
+ }
349
+ await this.driveOnce(trigger);
350
+ if (this.stopped)
351
+ return;
352
+ // A drive that landed the reservation resumes the healthy cadence at once; backing off here
353
+ // would leave the first liveness check after a long outage a fully grown backoff away.
354
+ if (this.held) {
355
+ this.onHeld();
356
+ return;
357
+ }
358
+ this.scheduleBackoff();
359
+ }
360
+ /** Healthy tick: nothing to request, so only reset and re-check later. */
361
+ onHeld() {
362
+ this.backoffMs = this.minBackoffMs;
363
+ this.failure = null;
364
+ // Once held, a later slot-taken episode is a new event and deserves its own log line.
365
+ this.slotTakenLogged = false;
366
+ this.settleFirst(null);
367
+ this.schedule(this.checkMs);
368
+ }
369
+ async driveOnce(trigger) {
370
+ this.inFlight = true;
371
+ log('relay-reservation:drive relay=%s trigger=%s', this.relay.dialAddr, trigger);
372
+ try {
373
+ const reason = await this.drive();
374
+ if (this.stopped)
375
+ return;
376
+ this.failure = reason;
377
+ if (reason === null) {
378
+ log('relay-reservation:held relay=%s', this.relay.dialAddr);
379
+ }
380
+ else {
381
+ log.error('relay-reservation:failed relay=%s reason=%s', this.relay.dialAddr, reason);
382
+ }
383
+ }
384
+ catch (err) {
385
+ // `drive` is fail-soft by contract, so reaching here means that contract broke. The loop
386
+ // must survive it anyway: an escaping rejection would leave `firstDrive` pending forever
387
+ // and schedule no further attempt, which is the failure this supervisor exists to prevent.
388
+ const message = errorMessage(err);
389
+ log.error('relay-reservation:drive-threw relay=%s err=%s', this.relay.dialAddr, message);
390
+ if (!this.stopped)
391
+ this.failure = message;
392
+ }
393
+ finally {
394
+ this.inFlight = false;
395
+ if (!this.stopped)
396
+ this.settleFirst(this.failure);
397
+ }
398
+ }
399
+ /** One attempt under one deadline. Never throws; returns `null` when the address is held afterwards. */
400
+ async drive() {
401
+ const transport = findCircuitRelayTransport(this.node);
402
+ if (transport === null) {
403
+ return 'the node has no circuit-relay transport — add circuitRelayTransport() to transports';
404
+ }
405
+ const deadline = Date.now() + this.driveTimeoutMs;
406
+ clearRelayFilterEntry(transport.reservationStore, this.relayPeerId);
407
+ const dialFailure = await this.dialRelay(deadline);
408
+ if (dialFailure !== null)
409
+ return dialFailure;
410
+ if (this.stopped)
411
+ return 'stopped';
412
+ const requestFailure = await this.requestReservation(transport.reservationStore, deadline);
413
+ if (requestFailure !== null)
414
+ return requestFailure;
415
+ if (await this.waitUntilHeld(deadline))
416
+ return null;
417
+ return `relay ${this.relay.dialAddr} granted a reservation but no circuit address through it was published within ${this.driveTimeoutMs}ms`;
418
+ }
419
+ /**
420
+ * Dial the relay, or return why that failed. An explicit controller rather than
421
+ * `AbortSignal.timeout`, which is unreliable on Hermes; {@link stop} aborts it too.
422
+ */
423
+ async dialRelay(deadline) {
424
+ const controller = new AbortController();
425
+ this.dialAbort = controller;
426
+ const timer = setTimeout(() => controller.abort(), remaining(deadline));
427
+ unref(timer);
428
+ try {
429
+ await this.node.dial(multiaddr(this.relay.dialAddr), { signal: controller.signal });
430
+ return null;
431
+ }
432
+ catch (err) {
433
+ return `dial to relay ${this.relay.dialAddr} failed: ${errorMessage(err)}`;
434
+ }
435
+ finally {
436
+ clearTimeout(timer);
437
+ this.dialAbort = null;
438
+ }
439
+ }
440
+ /**
441
+ * Ask the store for a `discovered` reservation on the relay, raced against the deadline because
442
+ * `addRelay` takes no signal. `'discovered'`, never `'configured'`: only a `discovered`
443
+ * reservation is published by the bare listener the plan gave libp2p. A concurrent `addRelay`
444
+ * for the same relay from libp2p's own discovery joins the same queue job, so there is no
445
+ * double reservation. The abandoned promise stays handled — `Promise.race` attaches handlers.
446
+ */
447
+ async requestReservation(store, deadline) {
448
+ let timer;
449
+ const expired = new Promise(resolve => {
450
+ timer = setTimeout(() => resolve(DEADLINE_PASSED), remaining(deadline));
451
+ unref(timer);
452
+ });
453
+ try {
454
+ const outcome = await Promise.race([store.addRelay(this.relayPeerId, 'discovered'), expired]);
455
+ if (outcome === DEADLINE_PASSED) {
456
+ return `reservation request to relay ${this.relay.dialAddr} did not complete within ${this.driveTimeoutMs}ms`;
457
+ }
458
+ return null;
459
+ }
460
+ catch (err) {
461
+ return this.describeReservationFailure(err);
462
+ }
463
+ finally {
464
+ clearTimeout(timer);
465
+ }
466
+ }
467
+ /** Turn the store's rejection into a reason that names the cause, and apply its consequence. */
468
+ describeReservationFailure(err) {
469
+ const name = err instanceof Error ? err.name : '';
470
+ switch (name) {
471
+ case 'HadEnoughRelaysError': {
472
+ // The pending slot the bare listener registered is already filled, by a reservation on
473
+ // some other relay. Nothing this supervisor does frees it, so retry only at the cap.
474
+ this.backoffMs = this.maxBackoffMs;
475
+ const reason = `this node's pending circuit slot is already filled by a reservation on another relay, so relay ${this.relay.dialAddr} was not asked; retrying every ${this.maxBackoffMs}ms until that reservation drops`;
476
+ if (!this.slotTakenLogged) {
477
+ this.slotTakenLogged = true;
478
+ log.error('relay-reservation:slot-taken relay=%s', this.relay.dialAddr);
479
+ }
480
+ return reason;
481
+ }
482
+ case 'UnsupportedProtocolError':
483
+ return `the peer at ${this.relay.dialAddr} does not serve the circuit-relay hop protocol, so it is not a relay`;
484
+ default:
485
+ return `reservation request to relay ${this.relay.dialAddr} failed: ${errorMessage(err)}`;
486
+ }
487
+ }
488
+ /** `addRelay` resolving means the relay accepted; the listener publishes the address a tick later. */
489
+ async waitUntilHeld(deadline) {
490
+ for (;;) {
491
+ if (this.held)
492
+ return true;
493
+ if (this.stopped || Date.now() >= deadline)
494
+ return false;
495
+ await delay(Math.min(this.pollMs, remaining(deadline)));
496
+ }
497
+ }
498
+ scheduleBackoff() {
499
+ const wait = this.backoffMs;
500
+ this.backoffMs = Math.min(this.backoffMs * 2, this.maxBackoffMs);
501
+ this.schedule(wait);
502
+ }
503
+ schedule(ms) {
504
+ if (this.stopped)
505
+ return;
506
+ this.clearTimer();
507
+ this.nextTickAtMs = Date.now() + ms;
508
+ this.timer = setTimeout(() => {
509
+ this.timer = null;
510
+ void this.tick('timer');
511
+ }, ms);
512
+ unref(this.timer);
513
+ }
514
+ clearTimer() {
515
+ if (this.timer === null)
516
+ return;
517
+ clearTimeout(this.timer);
518
+ this.timer = null;
519
+ }
520
+ settleFirst(reason) {
521
+ if (this.firstDriveSettled)
522
+ return;
523
+ this.firstDriveSettled = true;
524
+ this.settleFirstDrive(reason);
525
+ }
526
+ }
527
+ //# sourceMappingURL=relay-reservation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-reservation.js","sourceRoot":"","sources":["../../../src/network/relay-reservation.ts"],"names":[],"mappings":"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"}
@@ -37,8 +37,9 @@ export type PendValidationEvent = {
37
37
  *
38
38
  * Two decisions live here:
39
39
  *
40
- * - **No `validation` pair** (the single-collection `Collection.sync` shape bare transforms, so
41
- * there is nothing to re-execute): an explicit, LOGGED policy branch on both arms, never a silent
40
+ * - **No `validation` pair** (bare transforms, so there is nothing to re-execute: the
41
+ * single-collection `Collection.sync` shape, and the Quereus adapter's legacy multi-tree commit,
42
+ * whose coordinator is built with `pendValidation: 'none'`): an explicit, LOGGED policy branch on both arms, never a silent
42
43
  * fall-through. `'accept'` admits it unchecked, preserving the historical behaviour; `'reject'`
43
44
  * refuses with {@link PEND_NOT_VALIDATABLE}. The presence test is on the whole pair, so a sender
44
45
  * cannot talk a receiver out of validating by omitting half of it.
@@ -1 +1 @@
1
- {"version":3,"file":"pend-validation.d.ts","sourceRoot":"","sources":["../../src/pend-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEtG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAEhH,+FAA+F;AAC/F,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,WAAW,GAAG,SAAS,EAC9B,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CAmB/B"}
1
+ {"version":3,"file":"pend-validation.d.ts","sourceRoot":"","sources":["../../src/pend-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEtG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAEhH,+FAA+F;AAC/F,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,WAAW,GAAG,SAAS,EAC9B,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CAmB/B"}
@@ -21,8 +21,9 @@ export const VALIDATOR_FAULT = 'validator-fault';
21
21
  *
22
22
  * Two decisions live here:
23
23
  *
24
- * - **No `validation` pair** (the single-collection `Collection.sync` shape bare transforms, so
25
- * there is nothing to re-execute): an explicit, LOGGED policy branch on both arms, never a silent
24
+ * - **No `validation` pair** (bare transforms, so there is nothing to re-execute: the
25
+ * single-collection `Collection.sync` shape, and the Quereus adapter's legacy multi-tree commit,
26
+ * whose coordinator is built with `pendValidation: 'none'`): an explicit, LOGGED policy branch on both arms, never a silent
26
27
  * fall-through. `'accept'` admits it unchecked, preserving the historical behaviour; `'reject'`
27
28
  * refuses with {@link PEND_NOT_VALIDATABLE}. The presence test is on the whole pair, so a sender
28
29
  * cannot talk a receiver out of validating by omitting half of it.
@@ -1 +1 @@
1
- {"version":3,"file":"pend-validation.js","sourceRoot":"","sources":["../../src/pend-validation.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAejD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,OAAoB,EACpB,KAA8B,EAC9B,MAA+B,EAC/B,OAA6C;IAE7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACzB,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3C,OAAO,MAAM,KAAK,QAAQ;YACzB,iFAAiF;YACjF,yFAAyF;YACzF,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,6CAA6C,EAAE;YAChG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,KAAK,GAAI,GAAa,CAAC,OAAO,CAAC;QACrC,OAAO,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,KAAK,KAAK,EAAE,EAAE,CAAC;IACjE,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"pend-validation.js","sourceRoot":"","sources":["../../src/pend-validation.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAejD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,OAAoB,EACpB,KAA8B,EAC9B,MAA+B,EAC/B,OAA6C;IAE7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACzB,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3C,OAAO,MAAM,KAAK,QAAQ;YACzB,iFAAiF;YACjF,yFAAyF;YACzF,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,6CAA6C,EAAE;YAChG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,KAAK,GAAI,GAAa,CAAC,OAAO,CAAC;QACrC,OAAO,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,KAAK,KAAK,EAAE,EAAE,CAAC;IACjE,CAAC;AACF,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { PeerId, IPeerNetwork } from '@optimystic/db-core';
1
+ import type { PeerId, IPeerNetwork, BlockId } from '@optimystic/db-core';
2
2
  /**
3
3
  * Thrown when the per-peer dial deadline expires before a stream is established.
4
4
  * Distinct from a libp2p dial failure (no route, refused, etc.) so the
@@ -27,6 +27,13 @@ export declare class ProtocolClient {
27
27
  protected readonly peerId: PeerId;
28
28
  protected readonly peerNetwork: IPeerNetwork;
29
29
  constructor(peerId: PeerId, peerNetwork: IPeerNetwork);
30
+ /**
31
+ * Remember that `coordinator` coordinates `blockId`, so a follow-up op on that block dials it directly
32
+ * instead of being redirected again. Only a key network keeps coordinator hints — a plain
33
+ * {@link IPeerNetwork} omits `recordCoordinator`, and this is then a no-op. The hint is keyed on the
34
+ * block's routing key, the key a later `findCoordinator` looks it up by.
35
+ */
36
+ protected recordCoordinatorHint(blockId: BlockId, coordinator: PeerId): void;
30
37
  protected processMessage<T>(message: unknown, protocol: string, options?: {
31
38
  signal?: AbortSignal;
32
39
  correlationId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol-client.d.ts","sourceRoot":"","sources":["../../src/protocol-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOhE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAEtD,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,kBAA2B;gBAC5B,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;CAItD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAE9D,qBAAa,oBAAqB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,sBAA+B;gBAChC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;CAItD;AAED,oEAAoE;AACpE,qBAAa,cAAc;IAEzB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,YAAY;gBADzB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,YAAY;cAG7B,cAAc,CAAC,CAAC,EAC/B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACpI,OAAO,CAAC,CAAC,CAAC;CA6Ib"}
1
+ {"version":3,"file":"protocol-client.d.ts","sourceRoot":"","sources":["../../src/protocol-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAe,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAQtF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAEtD,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,kBAA2B;gBAC5B,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;CAItD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAE9D,qBAAa,oBAAqB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,sBAA+B;gBAChC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;CAItD;AAED,oEAAoE;AACpE,qBAAa,cAAc;IAEzB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,YAAY;gBADzB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,YAAY;IAG7C;;;;;OAKG;IACH,SAAS,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;cAK5D,cAAc,CAAC,CAAC,EAC/B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACpI,OAAO,CAAC,CAAC,CAAC;CA6Ib"}
@@ -1,5 +1,6 @@
1
1
  import { pipe } from 'it-pipe';
2
2
  import { encode as lpEncode, decode as lpDecode } from 'it-length-prefixed';
3
+ import { routingKeyForBlock } from '@optimystic/db-core';
3
4
  import { first } from './it-utility.js';
4
5
  import { createLogger } from './logger.js';
5
6
  import { MAX_BLOCK_MESSAGE_BYTES } from './protocol-limits.js';
@@ -41,6 +42,16 @@ export class ProtocolClient {
41
42
  this.peerId = peerId;
42
43
  this.peerNetwork = peerNetwork;
43
44
  }
45
+ /**
46
+ * Remember that `coordinator` coordinates `blockId`, so a follow-up op on that block dials it directly
47
+ * instead of being redirected again. Only a key network keeps coordinator hints — a plain
48
+ * {@link IPeerNetwork} omits `recordCoordinator`, and this is then a no-op. The hint is keyed on the
49
+ * block's routing key, the key a later `findCoordinator` looks it up by.
50
+ */
51
+ recordCoordinatorHint(blockId, coordinator) {
52
+ const hints = this.peerNetwork;
53
+ hints.recordCoordinator?.(routingKeyForBlock(blockId), coordinator);
54
+ }
44
55
  async processMessage(message, protocol, options) {
45
56
  const peer = this.peerId.toString();
46
57
  const cid = options?.correlationId;