@optimystic/db-p2p 1.0.0-beta.2 → 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.
- package/dist/src/cluster/block-transfer-service.d.ts +73 -1
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +70 -3
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/block-transfer.d.ts +16 -1
- package/dist/src/cluster/block-transfer.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer.js +73 -72
- package/dist/src/cluster/block-transfer.js.map +1 -1
- package/dist/src/cluster/client.d.ts +4 -6
- package/dist/src/cluster/client.d.ts.map +1 -1
- package/dist/src/cluster/client.js +8 -13
- package/dist/src/cluster/client.js.map +1 -1
- package/dist/src/cluster/cluster-policy.d.ts +2 -1
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +172 -17
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +553 -149
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/commit-proof.js +2 -2
- package/dist/src/cluster/race-resolution.d.ts +51 -7
- package/dist/src/cluster/race-resolution.d.ts.map +1 -1
- package/dist/src/cluster/race-resolution.js +71 -7
- package/dist/src/cluster/race-resolution.js.map +1 -1
- package/dist/src/cluster/rebalance-monitor.d.ts +67 -5
- package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
- package/dist/src/cluster/rebalance-monitor.js +120 -26
- package/dist/src/cluster/rebalance-monitor.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +34 -47
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cohort-topic/host.d.ts.map +1 -1
- package/dist/src/cohort-topic/host.js +8 -6
- package/dist/src/cohort-topic/host.js.map +1 -1
- package/dist/src/cohort-topic/membership-source.js +1 -1
- package/dist/src/cohort-topic/membership-source.js.map +1 -1
- package/dist/src/cohort-topic/peer-codec.d.ts.map +1 -1
- package/dist/src/cohort-topic/peer-codec.js +5 -2
- package/dist/src/cohort-topic/peer-codec.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +24 -3
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +36 -3
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/cohort-topic/topic-router.d.ts +9 -0
- package/dist/src/cohort-topic/topic-router.d.ts.map +1 -1
- package/dist/src/cohort-topic/topic-router.js +12 -2
- package/dist/src/cohort-topic/topic-router.js.map +1 -1
- package/dist/src/dispute/client.d.ts.map +1 -1
- package/dist/src/dispute/client.js +15 -3
- package/dist/src/dispute/client.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +78 -17
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +241 -147
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +35 -1
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +151 -26
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/libp2p-node-rn.d.ts +2 -2
- package/dist/src/libp2p-node-rn.d.ts.map +1 -1
- package/dist/src/libp2p-node-rn.js.map +1 -1
- package/dist/src/libp2p-node.d.ts +2 -2
- package/dist/src/libp2p-node.d.ts.map +1 -1
- package/dist/src/libp2p-node.js.map +1 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +19 -7
- package/dist/src/logger.js.map +1 -1
- package/dist/src/matchmaking/query-transport.d.ts +3 -4
- package/dist/src/matchmaking/query-transport.d.ts.map +1 -1
- package/dist/src/matchmaking/query-transport.js +9 -9
- package/dist/src/matchmaking/query-transport.js.map +1 -1
- package/dist/src/network/network-manager-service.d.ts +10 -5
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +11 -2
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/network/relay-reservation.d.ts +170 -0
- package/dist/src/network/relay-reservation.d.ts.map +1 -0
- package/dist/src/network/relay-reservation.js +527 -0
- package/dist/src/network/relay-reservation.js.map +1 -0
- package/dist/src/pend-validation.d.ts +3 -2
- package/dist/src/pend-validation.d.ts.map +1 -1
- package/dist/src/pend-validation.js +3 -2
- package/dist/src/pend-validation.js.map +1 -1
- package/dist/src/protocol-client.d.ts +8 -1
- package/dist/src/protocol-client.d.ts.map +1 -1
- package/dist/src/protocol-client.js +11 -0
- package/dist/src/protocol-client.js.map +1 -1
- package/dist/src/reactivity/forwarder-host.d.ts +2 -1
- package/dist/src/reactivity/forwarder-host.d.ts.map +1 -1
- package/dist/src/reactivity/forwarder-host.js +2 -1
- package/dist/src/reactivity/forwarder-host.js.map +1 -1
- package/dist/src/reactivity/origination-manager.d.ts +3 -3
- package/dist/src/reactivity/origination-manager.js +3 -3
- package/dist/src/reactivity/recover-transport.d.ts +22 -14
- package/dist/src/reactivity/recover-transport.d.ts.map +1 -1
- package/dist/src/reactivity/recover-transport.js +32 -19
- package/dist/src/reactivity/recover-transport.js.map +1 -1
- package/dist/src/reactivity/subscription-manager.d.ts +2 -2
- package/dist/src/reactivity/topic-bytes.d.ts +6 -7
- package/dist/src/reactivity/topic-bytes.d.ts.map +1 -1
- package/dist/src/reactivity/topic-bytes.js +8 -9
- package/dist/src/reactivity/topic-bytes.js.map +1 -1
- package/dist/src/repo/client.d.ts +5 -2
- package/dist/src/repo/client.d.ts.map +1 -1
- package/dist/src/repo/client.js +49 -40
- package/dist/src/repo/client.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +119 -5
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +181 -29
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +259 -76
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +716 -328
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/repo/i-under-replication-ledger.d.ts +87 -0
- package/dist/src/repo/i-under-replication-ledger.d.ts.map +1 -0
- package/dist/src/repo/i-under-replication-ledger.js +2 -0
- package/dist/src/repo/i-under-replication-ledger.js.map +1 -0
- package/dist/src/repo/kv-under-replication-ledger.d.ts +91 -0
- package/dist/src/repo/kv-under-replication-ledger.d.ts.map +1 -0
- package/dist/src/repo/kv-under-replication-ledger.js +287 -0
- package/dist/src/repo/kv-under-replication-ledger.js.map +1 -0
- package/dist/src/repo/responsibility.d.ts +37 -0
- package/dist/src/repo/responsibility.d.ts.map +1 -0
- package/dist/src/repo/responsibility.js +37 -0
- package/dist/src/repo/responsibility.js.map +1 -0
- package/dist/src/repo/service.d.ts +44 -15
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +64 -27
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/repo/stuck-reservation.d.ts +127 -0
- package/dist/src/repo/stuck-reservation.d.ts.map +1 -0
- package/dist/src/repo/stuck-reservation.js +179 -0
- package/dist/src/repo/stuck-reservation.js.map +1 -0
- package/dist/src/repo/under-replication-drain.d.ts +208 -0
- package/dist/src/repo/under-replication-drain.d.ts.map +1 -0
- package/dist/src/repo/under-replication-drain.js +461 -0
- package/dist/src/repo/under-replication-drain.js.map +1 -0
- package/dist/src/rn.d.ts +5 -0
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +5 -0
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-latch.d.ts +3 -2
- package/dist/src/storage/block-latch.d.ts.map +1 -1
- package/dist/src/storage/block-latch.js +7 -4
- package/dist/src/storage/block-latch.js.map +1 -1
- package/dist/src/storage/block-storage.d.ts +45 -3
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +156 -9
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +57 -5
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-block-storage.js.map +1 -1
- package/dist/src/storage/pending-claim.d.ts +167 -0
- package/dist/src/storage/pending-claim.d.ts.map +1 -0
- package/dist/src/storage/pending-claim.js +156 -0
- package/dist/src/storage/pending-claim.js.map +1 -0
- package/dist/src/storage/raw-store-codec.d.ts +13 -0
- package/dist/src/storage/raw-store-codec.d.ts.map +1 -1
- package/dist/src/storage/raw-store-codec.js +24 -3
- package/dist/src/storage/raw-store-codec.js.map +1 -1
- package/dist/src/storage/restoration-coordinator.d.ts.map +1 -1
- package/dist/src/storage/restoration-coordinator.js +2 -1
- package/dist/src/storage/restoration-coordinator.js.map +1 -1
- package/dist/src/storage/ring-shift-coordinator.d.ts.map +1 -1
- package/dist/src/storage/ring-shift-coordinator.js +3 -3
- package/dist/src/storage/ring-shift-coordinator.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +137 -5
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +273 -68
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/storage/struct.d.ts +58 -0
- package/dist/src/storage/struct.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.d.ts +92 -11
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +246 -103
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +27 -3
- package/src/cluster/block-transfer-service.ts +132 -4
- package/src/cluster/block-transfer.ts +79 -84
- package/src/cluster/client.ts +115 -119
- package/src/cluster/cluster-policy.ts +2 -1
- package/src/cluster/cluster-repo.ts +3115 -2671
- package/src/cluster/commit-proof.ts +2 -2
- package/src/cluster/race-resolution.ts +76 -7
- package/src/cluster/rebalance-monitor.ts +677 -539
- package/src/cluster/spread-on-churn.ts +34 -54
- package/src/cohort-topic/host.ts +8 -6
- package/src/cohort-topic/membership-source.ts +1 -1
- package/src/cohort-topic/peer-codec.ts +34 -31
- package/src/cohort-topic/stream-util.ts +39 -4
- package/src/cohort-topic/topic-router.ts +12 -2
- package/src/dispute/client.ts +17 -7
- package/src/index.ts +64 -59
- package/src/libp2p-key-network.ts +1377 -1253
- package/src/libp2p-node-base.ts +186 -27
- package/src/libp2p-node-rn.ts +31 -30
- package/src/libp2p-node.ts +37 -36
- package/src/logger.ts +20 -7
- package/src/matchmaking/query-transport.ts +9 -9
- package/src/network/network-manager-service.ts +16 -5
- package/src/network/relay-reservation.ts +636 -0
- package/src/pend-validation.ts +3 -2
- package/src/protocol-client.ts +13 -1
- package/src/reactivity/forwarder-host.ts +2 -1
- package/src/reactivity/origination-manager.ts +3 -3
- package/src/reactivity/recover-transport.ts +39 -22
- package/src/reactivity/subscription-manager.ts +2 -2
- package/src/reactivity/topic-bytes.ts +35 -37
- package/src/repo/client.ts +172 -173
- package/src/repo/cluster-coordinator.ts +1296 -1113
- package/src/repo/coordinator-repo.ts +3087 -2687
- package/src/repo/i-under-replication-ledger.ts +89 -0
- package/src/repo/kv-under-replication-ledger.ts +302 -0
- package/src/repo/responsibility.ts +48 -0
- package/src/repo/service.ts +76 -39
- package/src/repo/stuck-reservation.ts +224 -0
- package/src/repo/under-replication-drain.ts +582 -0
- package/src/rn.ts +74 -69
- package/src/storage/block-latch.ts +7 -4
- package/src/storage/block-storage.ts +161 -10
- package/src/storage/i-block-storage.ts +60 -5
- package/src/storage/pending-claim.ts +200 -0
- package/src/storage/raw-store-codec.ts +26 -3
- package/src/storage/restoration-coordinator.ts +212 -211
- package/src/storage/ring-shift-coordinator.ts +271 -271
- package/src/storage/storage-repo.ts +311 -73
- package/src/storage/struct.ts +58 -0
- package/src/testing/mesh-harness.ts +329 -115
|
@@ -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** (
|
|
41
|
-
*
|
|
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
|
|
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** (
|
|
25
|
-
*
|
|
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
|
|
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;
|
|
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;
|