@serfab/cadre-core 0.10.0 → 0.12.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/README.md +255 -255
- package/dist/announce-addrs.d.ts +49 -0
- package/dist/announce-addrs.js +78 -0
- package/dist/announce-addrs.js.map +1 -0
- package/dist/cadre-node.d.ts +484 -58
- package/dist/cadre-node.js +953 -182
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-cohort.d.ts +26 -0
- package/dist/control-cohort.js +26 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +222 -125
- package/dist/control-database.js +356 -266
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +8 -7
- package/dist/control-formation-recorder.js +8 -7
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-read-retry.d.ts +103 -0
- package/dist/control-read-retry.js +145 -0
- package/dist/control-read-retry.js.map +1 -0
- package/dist/control-retry.d.ts +101 -0
- package/dist/control-retry.js +108 -0
- package/dist/control-retry.js.map +1 -0
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +805 -774
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +65 -31
- package/dist/control-write-retry.js +33 -103
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +17 -15
- package/dist/delegate-admission.js +17 -15
- package/dist/delegate-admission.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +18 -9
- package/dist/index.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +211 -25
- package/dist/membership-connection-gater.js +309 -29
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/peer-addr-book.d.ts +92 -0
- package/dist/peer-addr-book.js +220 -0
- package/dist/peer-addr-book.js.map +1 -0
- package/dist/peer-record.d.ts +26 -0
- package/dist/peer-record.js +32 -0
- package/dist/peer-record.js.map +1 -1
- package/dist/relay-addrs.d.ts +76 -26
- package/dist/relay-addrs.js +115 -27
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +20 -15
- package/dist/relay-reservation.js +18 -14
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +7 -0
- package/dist/seed-bootstrap.js +15 -13
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-addr-protocol.js +16 -0
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-backfill.js +13 -4
- package/dist/strand-backfill.js.map +1 -1
- package/dist/strand-cohort.d.ts +4 -37
- package/dist/strand-cohort.js +4 -15
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +11 -16
- package/dist/strand-database.js +19 -7
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +18 -17
- package/dist/strand-formation-manager.js +25 -27
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +7 -7
- package/dist/strand-formation-protocol.js +8 -8
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +53 -21
- package/dist/strand-instance-manager.js +128 -37
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +113 -19
- package/dist/strand-membership-writer.js +219 -51
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +3 -3
- package/dist/strand-solicitation.js +4 -5
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +47 -2
- package/dist/strand-wake-protocol.js +76 -6
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +107 -54
- package/dist/types.js.map +1 -1
- package/package.json +8 -7
- package/src/announce-addrs.ts +93 -0
- package/src/arachnode-stub.ts +159 -159
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +172 -172
- package/src/cadre-node.ts +1026 -194
- package/src/canonical-json.ts +30 -30
- package/src/control-authorization.ts +114 -114
- package/src/control-cohort.ts +130 -103
- package/src/control-database.ts +376 -298
- package/src/control-formation-recorder.ts +305 -304
- package/src/control-read-retry.ts +167 -0
- package/src/control-retry.ts +190 -0
- package/src/control-schema.ts +817 -786
- package/src/control-stream.ts +205 -205
- package/src/control-write-retry.ts +383 -473
- package/src/delegate-admission.ts +304 -302
- package/src/device-token.ts +94 -94
- package/src/diagnostics/connection-path.ts +277 -277
- package/src/diagnostics/webrtc-turn-tracker.ts +222 -222
- package/src/ed25519-key.ts +134 -134
- package/src/enrollment.ts +199 -199
- package/src/file-durable-slot.ts +61 -61
- package/src/formation-approval.ts +604 -604
- package/src/fs-atomic.ts +107 -107
- package/src/hibernation-manager.ts +437 -437
- package/src/identity-key.ts +109 -109
- package/src/index.ts +435 -406
- package/src/key-store-file.ts +132 -132
- package/src/key-store.ts +99 -99
- package/src/membership-connection-gater.ts +368 -39
- package/src/node-local-snapshot.ts +260 -260
- package/src/peer-addr-book.ts +250 -0
- package/src/peer-authorization.ts +259 -259
- package/src/peer-record.ts +197 -164
- package/src/push-credentials.ts +84 -84
- package/src/push-node.ts +64 -64
- package/src/push-notifier-apns.ts +247 -247
- package/src/push-notifier-fcm.ts +220 -220
- package/src/push-notifier-shared.ts +28 -28
- package/src/push-notifier.ts +52 -52
- package/src/relay-addrs.ts +134 -27
- package/src/relay-reservation.ts +22 -17
- package/src/seed-bootstrap.ts +15 -15
- package/src/seed-trust-policy.ts +140 -140
- package/src/strand-addr-protocol.ts +458 -441
- package/src/strand-backfill.ts +28 -5
- package/src/strand-cohort.ts +38 -77
- package/src/strand-database.ts +202 -204
- package/src/strand-formation-manager.ts +26 -28
- package/src/strand-formation-protocol.ts +692 -692
- package/src/strand-instance-manager.ts +636 -553
- package/src/strand-member-key.ts +39 -39
- package/src/strand-member-registry.ts +182 -182
- package/src/strand-membership-writer.ts +1627 -1430
- package/src/strand-solicitation.ts +471 -472
- package/src/strand-transport-key.ts +58 -58
- package/src/strand-wake-payload.ts +32 -32
- package/src/strand-wake-protocol.ts +400 -314
- package/src/strand-watcher.ts +314 -314
- package/src/trusted-owner-store-file.ts +41 -41
- package/src/trusted-owner-store.ts +171 -171
- package/src/types.ts +1354 -1303
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merging verified cadre addresses into a libp2p node's **address book** (its
|
|
3
|
+
* peerStore), so that every layer below cadre-core which dials by bare peer id
|
|
4
|
+
* — Optimystic's cluster client, repo client, FRET ping/announce, all funnelling
|
|
5
|
+
* into `libp2p.dialProtocol(peerId, …)` — has an address to use for a sibling
|
|
6
|
+
* whose connection has dropped, instead of failing with `NoValidAddressesError`
|
|
7
|
+
* until the next reconcile pass happens to re-dial it.
|
|
8
|
+
*
|
|
9
|
+
* Shared by the control-cohort reconcile pass and the strand one (both in
|
|
10
|
+
* `CadreNode`): each resolves a peer's addresses its own way and then needs
|
|
11
|
+
* exactly this write, with exactly this workaround.
|
|
12
|
+
*
|
|
13
|
+
* Two exports, in the order a caller uses them: {@link groupAddrsByPeerId} turns
|
|
14
|
+
* a flat address list into per-peer groups (the strand path starts from the
|
|
15
|
+
* peer-agnostic union the strand-addr RPC returns), and {@link mergePeerAddrs}
|
|
16
|
+
* writes one peer's group.
|
|
17
|
+
*/
|
|
18
|
+
import debug from 'debug';
|
|
19
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
20
|
+
import { multiaddr, CODE_P2P, CODE_P2P_CIRCUIT } from '@multiformats/multiaddr';
|
|
21
|
+
const log = debug('sereus:cadre:peer-addr-book');
|
|
22
|
+
/**
|
|
23
|
+
* Write `addrs` into `host`'s address book for `peerId`, best-effort. `peerId`
|
|
24
|
+
* may be the string form callers hold (a `CadrePeer.PeerId` column, a strand-addr
|
|
25
|
+
* RPC result); parsing it is part of the best-effort contract below, so no caller
|
|
26
|
+
* has to wrap this in a second try/catch of its own.
|
|
27
|
+
*
|
|
28
|
+
* - **Empty `addrs` writes nothing** and reports `'skipped'`. That is how a
|
|
29
|
+
* revoked / stale / untrusted peer's existing entry is allowed to age out on
|
|
30
|
+
* its own: the caller resolves `[]` for it, and nothing here refreshes it.
|
|
31
|
+
* - Otherwise the addresses are merged, and — see the timestamp trap below —
|
|
32
|
+
* re-saved when the merge alone leaves them invisible (`'restamped'`).
|
|
33
|
+
* - Any throw (unparsable peer id, datastore failure, a peerStore double without
|
|
34
|
+
* `merge`) is logged and folded to `'failed'`. Callers are best-effort loops:
|
|
35
|
+
* an address-book failure must never abort a reconcile pass.
|
|
36
|
+
*
|
|
37
|
+
* NOTE: `peerStore.merge` cannot refresh an address's `observed` timestamp in
|
|
38
|
+
* `@libp2p/peer-store` 12.0.10 — `to-peer-pb.js` shadows its own loop variable
|
|
39
|
+
* (`…addresses?.find(addr => uint8ArrayEquals(addr.multiaddr, addr.multiaddr))`,
|
|
40
|
+
* always true, so it returns the FIRST stored address's timestamp), and every
|
|
41
|
+
* read filters out addresses older than `MAX_ADDRESS_AGE` (1 hour). So an entry
|
|
42
|
+
* silently dies at the one-hour mark and no amount of re-merging revives it —
|
|
43
|
+
* not even merging a brand-new address. The `save` below is the workaround:
|
|
44
|
+
* `save` is the one write path that omits `existingPeer` and therefore stamps
|
|
45
|
+
* `Date.now()`. Drop it once upstream stamps `Date.now()` on merge.
|
|
46
|
+
*
|
|
47
|
+
* NOTE: raising the store's own `maxAddressAge` (libp2p forwards `init.peerStore`
|
|
48
|
+
* straight into `persistentPeerStore`) would sidestep the bug in one line, and is
|
|
49
|
+
* deliberately NOT done: expiry is load-bearing here. Only *verified* addresses
|
|
50
|
+
* come through this helper, and the design relies on everything else — the
|
|
51
|
+
* cold-start seed entries, identify-learned addresses — still ageing out on the
|
|
52
|
+
* stock schedule. A global age bump would keep those alive too.
|
|
53
|
+
*
|
|
54
|
+
* NOTE: if the store ever REJECTS an address we hand it, the restamp repeats
|
|
55
|
+
* every pass forever — `allVisible` stays false, and `save` re-submits the same
|
|
56
|
+
* rejected address. libp2p wires the store's `addressFilter` to
|
|
57
|
+
* `connectionGater.filterMultiaddrForPeer`, which nothing in this repo implements
|
|
58
|
+
* today (`createMembershipConnectionGater` gates dials, not addresses), so the
|
|
59
|
+
* loop is unreachable. If an app ever supplies that gater hook via
|
|
60
|
+
* `NetworkConfig.connectionGater`, bound the retry — the cost is one redundant
|
|
61
|
+
* datastore write per gated sibling per reconcile pass.
|
|
62
|
+
*/
|
|
63
|
+
export async function mergePeerAddrs(host, peerId, addrs) {
|
|
64
|
+
if (addrs.length === 0) {
|
|
65
|
+
return 'skipped';
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const id = typeof peerId === 'string' ? peerIdFromString(peerId) : peerId;
|
|
69
|
+
const peer = await host.peerStore.merge(id, { multiaddrs: addrs });
|
|
70
|
+
// The returned Peer is already expiry-filtered, so it says whether the merge
|
|
71
|
+
// is actually VISIBLE — which is the only question that matters downstream.
|
|
72
|
+
if (allVisible(peer.addresses, addrs, id)) {
|
|
73
|
+
return 'merged';
|
|
74
|
+
}
|
|
75
|
+
await host.peerStore.save(id, restampData(peer, addrs, id));
|
|
76
|
+
return 'restamped';
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
log('mergePeerAddrs: address-book write for %s failed: %o', peerId.toString(), error);
|
|
80
|
+
return 'failed';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Group multiaddr strings by the peer id in their final `/p2p/` component — the
|
|
85
|
+
* attribution step between a flat, peer-agnostic address list and the per-peer
|
|
86
|
+
* writes {@link mergePeerAddrs} takes.
|
|
87
|
+
*
|
|
88
|
+
* The **last** `/p2p/` component is the addressed peer, not the first: a relayed
|
|
89
|
+
* address (`…/p2p/<relay>/p2p-circuit/p2p/<dst>`) names the relay first and the
|
|
90
|
+
* destination last, while a direct address (`…/tcp/…/p2p/<dst>`) names only the
|
|
91
|
+
* destination. Taking the last therefore attributes both shapes to the peer the
|
|
92
|
+
* address actually reaches.
|
|
93
|
+
*
|
|
94
|
+
* Three shapes are dropped rather than attributed, because none of them names a
|
|
95
|
+
* peer the address reaches: one that does not parse, one carrying no `/p2p/`
|
|
96
|
+
* component at all (a peer advertising a bare listen addr), and one whose
|
|
97
|
+
* `p2p-circuit` comes AFTER its last `/p2p/` (`…/p2p/<relay>/p2p-circuit`) — that
|
|
98
|
+
* last one is a relay hop with the destination missing, so its trailing peer id
|
|
99
|
+
* is the RELAY's and filing it under the relay would claim the relay is reachable
|
|
100
|
+
* at a circuit leading nowhere. Duplicates collapse within a group. Insertion
|
|
101
|
+
* order is preserved, both between groups and inside one.
|
|
102
|
+
*/
|
|
103
|
+
export function groupAddrsByPeerId(addrs) {
|
|
104
|
+
const groups = new Map();
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
for (const addr of addrs) {
|
|
107
|
+
const attributed = attributeAddr(addr);
|
|
108
|
+
if (!attributed) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const key = `${attributed.peerId}\n${addr}`;
|
|
112
|
+
if (seen.has(key)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
seen.add(key);
|
|
116
|
+
const group = groups.get(attributed.peerId);
|
|
117
|
+
if (group) {
|
|
118
|
+
group.push(attributed.multiaddr);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
groups.set(attributed.peerId, [attributed.multiaddr]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return groups;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parse one address and pull out the peer id it addresses (its last `/p2p/`
|
|
128
|
+
* value); null when the address is unparsable or names no destination. Every case
|
|
129
|
+
* is logged at the same level — an unattributable address is a sibling telling us
|
|
130
|
+
* something we cannot use, which is worth seeing but never worth throwing over.
|
|
131
|
+
*/
|
|
132
|
+
function attributeAddr(addr) {
|
|
133
|
+
let parsed;
|
|
134
|
+
try {
|
|
135
|
+
parsed = multiaddr(addr);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
log('groupAddrsByPeerId: skipping unparsable addr %s: %o', addr, error);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const peerId = lastAddressedPeerId(parsed);
|
|
142
|
+
if (peerId === null) {
|
|
143
|
+
log('groupAddrsByPeerId: skipping addr naming no destination peer: %s', addr);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return { peerId, multiaddr: parsed };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The peer id a parsed address reaches: its last `/p2p/` value, or null when it
|
|
150
|
+
* has none — or when a `p2p-circuit` sits after it, which makes that value the
|
|
151
|
+
* relay's rather than a destination's (see {@link groupAddrsByPeerId}). Walking
|
|
152
|
+
* backwards, meeting the circuit first is exactly that case.
|
|
153
|
+
*/
|
|
154
|
+
function lastAddressedPeerId(parsed) {
|
|
155
|
+
const components = parsed.getComponents();
|
|
156
|
+
for (let i = components.length - 1; i >= 0; i--) {
|
|
157
|
+
const component = components[i];
|
|
158
|
+
if (component.code === CODE_P2P_CIRCUIT) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (component.code === CODE_P2P && component.value) {
|
|
162
|
+
return component.value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The peerStore strips a trailing `/p2p/<peerId>` from an address it stores when
|
|
169
|
+
* that id is the address's FIRST `/p2p/` component (`dedupe-addresses.js`), so a
|
|
170
|
+
* plain address round-trips without its suffix while a relayed one — whose first
|
|
171
|
+
* `/p2p/` names the relay — keeps it. Comparing and de-duplicating on this
|
|
172
|
+
* suffix-stripped form matches either shape.
|
|
173
|
+
*/
|
|
174
|
+
function addrKey(addr, peerId) {
|
|
175
|
+
const suffix = `/p2p/${peerId.toString()}`;
|
|
176
|
+
return addr.endsWith(suffix) ? addr.slice(0, -suffix.length) : addr;
|
|
177
|
+
}
|
|
178
|
+
/** Is every address in `addrs` readable back from the stored, expiry-filtered set? */
|
|
179
|
+
function allVisible(stored, addrs, peerId) {
|
|
180
|
+
const keys = new Set(stored.map((a) => addrKey(a.multiaddr.toString(), peerId)));
|
|
181
|
+
return addrs.every((addr) => keys.has(addrKey(addr.toString(), peerId)));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The full `save` payload for a restamp: `save` re-uses the `patch` strategy with
|
|
185
|
+
* no existing peer, so every field it is not handed is DROPPED — and Optimystic
|
|
186
|
+
* classifies peers by their peerStore protocol list (`libp2p-key-network.ts`
|
|
187
|
+
* `membershipOf`), so losing protocols here would break cluster/repo routing.
|
|
188
|
+
* Everything therefore carries forward from the just-merged `peer`.
|
|
189
|
+
*
|
|
190
|
+
* The read-modify-write is NOT atomic against a concurrent `identify` write to
|
|
191
|
+
* the same entry. The loss window is milliseconds and costs at most one identify
|
|
192
|
+
* update, recovered by the next identify push — not worth a lock.
|
|
193
|
+
*
|
|
194
|
+
* A tag's remaining TTL is not carried: the public `Tag` type exposes only
|
|
195
|
+
* `value`, so the stored expiry is unreadable from here and the tag comes back
|
|
196
|
+
* non-expiring. `publicKey` is likewise not passed, which is lossless for the
|
|
197
|
+
* Ed25519 peer ids a cadre uses (the key is embedded in the id).
|
|
198
|
+
*/
|
|
199
|
+
function restampData(peer, addrs, peerId) {
|
|
200
|
+
const addresses = new Map();
|
|
201
|
+
for (const address of peer.addresses) {
|
|
202
|
+
addresses.set(addrKey(address.multiaddr.toString(), peerId), address);
|
|
203
|
+
}
|
|
204
|
+
for (const addr of addrs) {
|
|
205
|
+
const key = addrKey(addr.toString(), peerId);
|
|
206
|
+
if (!addresses.has(key)) {
|
|
207
|
+
// Uncertified, matching what `merge`'s own `multiaddrs` path would have
|
|
208
|
+
// stored; an address already present keeps whatever flag it carried.
|
|
209
|
+
addresses.set(key, { multiaddr: addr, isCertified: false });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
addresses: [...addresses.values()],
|
|
214
|
+
protocols: peer.protocols,
|
|
215
|
+
metadata: new Map(peer.metadata),
|
|
216
|
+
tags: new Map([...peer.tags].map(([name, tag]) => [name, { value: tag.value }])),
|
|
217
|
+
peerRecordEnvelope: peer.peerRecordEnvelope
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=peer-addr-book.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer-addr-book.js","sourceRoot":"","sources":["../src/peer-addr-book.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAkB,MAAM,yBAAyB,CAAC;AAEhG,MAAM,GAAG,GAAG,KAAK,CAAC,6BAA6B,CAAC,CAAC;AAcjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAsB,EACtB,MAAuB,EACvB,KAAkB;IAElB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,6EAA6E;QAC7E,4EAA4E;QAC5E,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;YAC1C,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5D,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,sDAAsD,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;QACtF,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAe;IAChD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,UAAU,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,MAAiB,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,qDAAqD,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,GAAG,CAAC,kEAAkE,EAAE,IAAI,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,MAAiB;IAC5C,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,SAAS,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;YACnD,OAAO,SAAS,CAAC,KAAK,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,OAAO,CAAC,IAAY,EAAE,MAAc;IAC3C,MAAM,MAAM,GAAG,QAAQ,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC3C,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,sFAAsF;AACtF,SAAS,UAAU,CAAC,MAAiB,EAAE,KAAkB,EAAE,MAAc;IACvE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACjF,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,WAAW,CAAC,IAAU,EAAE,KAAkB,EAAE,MAAc;IAOjE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC7C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,wEAAwE;YACxE,qEAAqE;YACrE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,OAAO;QACL,SAAS,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;QAClC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,QAAQ,EAAE,IAAI,GAAG,CAAiC,IAAI,CAAC,QAAQ,CAAC;QAChE,IAAI,EAAE,IAAI,GAAG,CACX,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAClE;QACD,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;KAC5C,CAAC;AACJ,CAAC"}
|
package/dist/peer-record.d.ts
CHANGED
|
@@ -84,6 +84,32 @@ export declare function isSignalingAddr(addr: string): boolean;
|
|
|
84
84
|
* cannot express that distinction.
|
|
85
85
|
*/
|
|
86
86
|
export declare function trailingPeerId(addr: Multiaddr): string | null;
|
|
87
|
+
/**
|
|
88
|
+
* `addr` guaranteed to terminate in `/p2p/<peerId>` — the shape every dial path
|
|
89
|
+
* needs — or `null` when it cannot, because the address already terminates in a
|
|
90
|
+
* DIFFERENT peer id and therefore does not reach `peerId` at all.
|
|
91
|
+
*
|
|
92
|
+
* Three input shapes, matching what a `CadrePeer` record can legitimately carry:
|
|
93
|
+
*
|
|
94
|
+
* - no `/p2p/` component (`/ip4/1.2.3.4/tcp/4001`) — the suffix is appended;
|
|
95
|
+
* - a relay hop with the destination missing (`…/p2p/<relay>/p2p-circuit`) — the
|
|
96
|
+
* last component is the circuit, not a peer id, so the suffix is appended and
|
|
97
|
+
* the address becomes the full `…/p2p/<relay>/p2p-circuit/p2p/<peerId>`;
|
|
98
|
+
* - already terminating in `/p2p/<X>` — returned untouched when `X` is `peerId`,
|
|
99
|
+
* dropped otherwise.
|
|
100
|
+
*
|
|
101
|
+
* This is deliberately the same rule libp2p applies internally in
|
|
102
|
+
* `calculateMultiaddrs` (append when the LAST component is not `p2p`) followed by
|
|
103
|
+
* its wrong-peer-id filter, so a normalized list matches what libp2p would have
|
|
104
|
+
* built anyway. Normalizing before the dial is what keeps a list from mixing
|
|
105
|
+
* suffixed and unsuffixed entries, which `getPeerAddress` rejects outright
|
|
106
|
+
* ("Multiaddrs must all have the same peer id or have no peer id") — taking the
|
|
107
|
+
* whole peer down with it, not just the odd address.
|
|
108
|
+
*
|
|
109
|
+
* Never use before signature verification: verification runs against the
|
|
110
|
+
* original on-record strings.
|
|
111
|
+
*/
|
|
112
|
+
export declare function withTrailingPeerId(addr: Multiaddr, peerId: string): Multiaddr | null;
|
|
87
113
|
/**
|
|
88
114
|
* Return `addrs` with signaling (`/p2p-circuit`) addrs first, otherwise stable.
|
|
89
115
|
* Used to present the WebRTC dial input ahead of direct addrs. Does NOT mutate
|
package/dist/peer-record.js
CHANGED
|
@@ -104,6 +104,38 @@ export function trailingPeerId(addr) {
|
|
|
104
104
|
const p2p = addr.getComponents().filter((c) => c.name === 'p2p');
|
|
105
105
|
return p2p.length > 0 ? (p2p[p2p.length - 1].value ?? null) : null;
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* `addr` guaranteed to terminate in `/p2p/<peerId>` — the shape every dial path
|
|
109
|
+
* needs — or `null` when it cannot, because the address already terminates in a
|
|
110
|
+
* DIFFERENT peer id and therefore does not reach `peerId` at all.
|
|
111
|
+
*
|
|
112
|
+
* Three input shapes, matching what a `CadrePeer` record can legitimately carry:
|
|
113
|
+
*
|
|
114
|
+
* - no `/p2p/` component (`/ip4/1.2.3.4/tcp/4001`) — the suffix is appended;
|
|
115
|
+
* - a relay hop with the destination missing (`…/p2p/<relay>/p2p-circuit`) — the
|
|
116
|
+
* last component is the circuit, not a peer id, so the suffix is appended and
|
|
117
|
+
* the address becomes the full `…/p2p/<relay>/p2p-circuit/p2p/<peerId>`;
|
|
118
|
+
* - already terminating in `/p2p/<X>` — returned untouched when `X` is `peerId`,
|
|
119
|
+
* dropped otherwise.
|
|
120
|
+
*
|
|
121
|
+
* This is deliberately the same rule libp2p applies internally in
|
|
122
|
+
* `calculateMultiaddrs` (append when the LAST component is not `p2p`) followed by
|
|
123
|
+
* its wrong-peer-id filter, so a normalized list matches what libp2p would have
|
|
124
|
+
* built anyway. Normalizing before the dial is what keeps a list from mixing
|
|
125
|
+
* suffixed and unsuffixed entries, which `getPeerAddress` rejects outright
|
|
126
|
+
* ("Multiaddrs must all have the same peer id or have no peer id") — taking the
|
|
127
|
+
* whole peer down with it, not just the odd address.
|
|
128
|
+
*
|
|
129
|
+
* Never use before signature verification: verification runs against the
|
|
130
|
+
* original on-record strings.
|
|
131
|
+
*/
|
|
132
|
+
export function withTrailingPeerId(addr, peerId) {
|
|
133
|
+
const components = addr.getComponents();
|
|
134
|
+
if (components[components.length - 1]?.name !== 'p2p') {
|
|
135
|
+
return addr.encapsulate(`/p2p/${peerId}`);
|
|
136
|
+
}
|
|
137
|
+
return trailingPeerId(addr) === peerId ? addr : null;
|
|
138
|
+
}
|
|
107
139
|
/**
|
|
108
140
|
* Return `addrs` with signaling (`/p2p-circuit`) addrs first, otherwise stable.
|
|
109
141
|
* Used to present the WebRTC dial input ahead of direct addrs. Does NOT mutate
|
package/dist/peer-record.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peer-record.js","sourceRoot":"","sources":["../src/peer-record.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"peer-record.js","sourceRoot":"","sources":["../src/peer-record.ts"],"names":[],"mappings":"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"}
|
package/dist/relay-addrs.d.ts
CHANGED
|
@@ -1,29 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Translate `network.relayAddrs` — the operator-facing "use these relay servers"
|
|
3
|
-
* setting — into the
|
|
4
|
-
* slot
|
|
5
|
-
*
|
|
6
|
-
* `@libp2p/circuit-relay-v2`'s
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
3
|
+
* setting — into the listen entry a node needs before it can hold a circuit-relay
|
|
4
|
+
* slot, and validate the operator's entries while doing it.
|
|
5
|
+
*
|
|
6
|
+
* `@libp2p/circuit-relay-v2`'s listener branches on the SHAPE of the listen addr,
|
|
7
|
+
* and the same `relayAddrs` list resolves to either shape depending on WHO is
|
|
8
|
+
* listening ({@link RelayListenRoute}):
|
|
9
|
+
*
|
|
10
|
+
* | route | listen entry | libp2p behaviour |
|
|
11
|
+
* | -------------- | ------------------------------------------- | --------------------------------------------------------- |
|
|
12
|
+
* | `'configured'` | `<relayAddr>/p2p-circuit`, one per relay | dial that relay from inside `listen()` and reserve, or throw |
|
|
13
|
+
* | `'search'` | a single bare `/p2p-circuit` | register a pending reservation; open no connection |
|
|
14
|
+
*
|
|
15
|
+
* The CONTROL node takes the `'search'` route, and `CadreNode.start()` drives the
|
|
16
|
+
* reservation explicitly (`relay-reservation.ts`) once the control database is up.
|
|
17
|
+
* That ordering is the point: a configured circuit listener dials the relay from
|
|
18
|
+
* inside `libp2p.start()`, so the control database was being built while a sibling
|
|
19
|
+
* relay was already in this node's cohort — and a sibling that has not yet
|
|
20
|
+
* replicated this node's `CadrePeer` row correctly refuses its control-DB streams,
|
|
21
|
+
* which killed `start()` outright. See `cadre-node.ts` → `start()`.
|
|
22
|
+
*
|
|
23
|
+
* STRAND nodes keep the `'configured'` route (`strand-instance-manager.ts`): they
|
|
24
|
+
* inherit the control node's `NetworkConfig`, nothing drives an explicit
|
|
25
|
+
* reservation for them, and their relay connection cannot disturb a control-DB
|
|
26
|
+
* bring-up (a strand node's protocol ids are namespaced `/optimystic/strand-<id>/…`,
|
|
27
|
+
* so the relay is never in its cohort).
|
|
28
|
+
*
|
|
29
|
+
* `network.relayAddrs` is FAIL-FAST for the operator on both routes, just from
|
|
30
|
+
* different places: a malformed entry throws here at config resolution, and on the
|
|
31
|
+
* control node a first reservation attempt that does not land throws out of
|
|
32
|
+
* `start()` (`RelayReservationFailedError`). Naming a relay that is down still
|
|
33
|
+
* means the node does not come up.
|
|
34
|
+
*
|
|
35
|
+
* The bare `/p2p-circuit` search listener cannot open a connection on its own:
|
|
36
|
+
* libp2p fills a pending reservation from `RelayDiscovery`, which nominates a peer
|
|
37
|
+
* only once the relay-hop protocol id is in that peer's peer-store protocol list,
|
|
38
|
+
* and that list is written exclusively by IDENTIFY — which `@optimystic/db-p2p`
|
|
39
|
+
* namespaces per network, so a cadre node and a stock relay never identify each
|
|
40
|
+
* other. See `relay-reservation.ts`'s module doc and `docs/architecture.md`.
|
|
25
41
|
*/
|
|
26
42
|
import type { NetworkConfig } from './types.js';
|
|
43
|
+
import type { RelayReservationState } from './relay-reservation.js';
|
|
44
|
+
/**
|
|
45
|
+
* libp2p's relay SEARCH listener: registers one pending reservation and dials
|
|
46
|
+
* nothing. One entry covers every configured relay — the pending reservation is
|
|
47
|
+
* per-listener, not per-relay, and `relay-reservation.ts` fills it with whichever
|
|
48
|
+
* relay answers first.
|
|
49
|
+
*/
|
|
50
|
+
export declare const RELAY_SEARCH_LISTEN_ADDR = "/p2p-circuit";
|
|
51
|
+
/**
|
|
52
|
+
* Which listener shape `network.relayAddrs` resolves to for this caller — see the
|
|
53
|
+
* table in the module doc. Defaults to `'configured'` everywhere except the
|
|
54
|
+
* control node's own libp2p options.
|
|
55
|
+
*/
|
|
56
|
+
export type RelayListenRoute = 'configured' | 'search';
|
|
27
57
|
/**
|
|
28
58
|
* The circuit-listen multiaddr for each configured relay: `<relayAddr>/p2p-circuit`.
|
|
29
59
|
* An entry that already carries `/p2p-circuit` is passed through unchanged.
|
|
@@ -38,8 +68,28 @@ import type { NetworkConfig } from './types.js';
|
|
|
38
68
|
export declare function relayCircuitAddrs(relayAddrs: readonly string[]): string[];
|
|
39
69
|
/**
|
|
40
70
|
* The listen multiaddrs a node actually binds: configured `listenAddrs` plus the
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
71
|
+
* relay listen entry `route` calls for, deduplicated, order-stable (configured
|
|
72
|
+
* entries first). Returns `undefined` when neither field is set, so callers keep
|
|
73
|
+
* omitting `listenAddrs` and inherit db-p2p's default.
|
|
74
|
+
*
|
|
75
|
+
* Every `relayAddrs` entry is validated on BOTH routes — the `'search'` route
|
|
76
|
+
* discards the resolved circuit addrs, but an operator typo must fail at config
|
|
77
|
+
* resolution either way.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveListenAddrs(network: NetworkConfig | undefined, route?: RelayListenRoute): string[] | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Thrown out of `CadreNode.start()` when the boot-path reservation drive for
|
|
82
|
+
* `network.relayAddrs` produces no `/p2p-circuit` address on its FIRST attempt.
|
|
83
|
+
*
|
|
84
|
+
* This is what keeps `network.relayAddrs` fail-fast now that the control node
|
|
85
|
+
* takes the `'search'` route: libp2p's own `UnsupportedListenAddressesError` used
|
|
86
|
+
* to abort start from inside `listen()`, and an operator who names a relay is
|
|
87
|
+
* telling the node it has no other reachability — coming up undialable is worse
|
|
88
|
+
* than not coming up. Names the relays and the reservation's own reason, both of
|
|
89
|
+
* which the libp2p error omitted.
|
|
44
90
|
*/
|
|
45
|
-
export declare
|
|
91
|
+
export declare class RelayReservationFailedError extends Error {
|
|
92
|
+
readonly relayAddrs: readonly string[];
|
|
93
|
+
readonly state: RelayReservationState;
|
|
94
|
+
constructor(relayAddrs: readonly string[], state: RelayReservationState);
|
|
95
|
+
}
|
package/dist/relay-addrs.js
CHANGED
|
@@ -1,27 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Translate `network.relayAddrs` — the operator-facing "use these relay servers"
|
|
3
|
-
* setting — into the
|
|
4
|
-
* slot
|
|
3
|
+
* setting — into the listen entry a node needs before it can hold a circuit-relay
|
|
4
|
+
* slot, and validate the operator's entries while doing it.
|
|
5
5
|
*
|
|
6
|
-
* `@libp2p/circuit-relay-v2`'s
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* that relay and hold a reservation. Everything downstream of the listen list —
|
|
10
|
-
* the reservation itself, `CadreNode.circuitRelayTargets()` picking delegate-announce
|
|
11
|
-
* targets, strand nodes inheriting the listen addrs, the `/p2p-circuit`-first address
|
|
12
|
-
* publishing in `registerSelf` — then works unchanged.
|
|
6
|
+
* `@libp2p/circuit-relay-v2`'s listener branches on the SHAPE of the listen addr,
|
|
7
|
+
* and the same `relayAddrs` list resolves to either shape depending on WHO is
|
|
8
|
+
* listening ({@link RelayListenRoute}):
|
|
13
9
|
*
|
|
14
|
-
*
|
|
10
|
+
* | route | listen entry | libp2p behaviour |
|
|
11
|
+
* | -------------- | ------------------------------------------- | --------------------------------------------------------- |
|
|
12
|
+
* | `'configured'` | `<relayAddr>/p2p-circuit`, one per relay | dial that relay from inside `listen()` and reserve, or throw |
|
|
13
|
+
* | `'search'` | a single bare `/p2p-circuit` | register a pending reservation; open no connection |
|
|
15
14
|
*
|
|
16
|
-
*
|
|
15
|
+
* The CONTROL node takes the `'search'` route, and `CadreNode.start()` drives the
|
|
16
|
+
* reservation explicitly (`relay-reservation.ts`) once the control database is up.
|
|
17
|
+
* That ordering is the point: a configured circuit listener dials the relay from
|
|
18
|
+
* inside `libp2p.start()`, so the control database was being built while a sibling
|
|
19
|
+
* relay was already in this node's cohort — and a sibling that has not yet
|
|
20
|
+
* replicated this node's `CadrePeer` row correctly refuses its control-DB streams,
|
|
21
|
+
* which killed `start()` outright. See `cadre-node.ts` → `start()`.
|
|
17
22
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* STRAND nodes keep the `'configured'` route (`strand-instance-manager.ts`): they
|
|
24
|
+
* inherit the control node's `NetworkConfig`, nothing drives an explicit
|
|
25
|
+
* reservation for them, and their relay connection cannot disturb a control-DB
|
|
26
|
+
* bring-up (a strand node's protocol ids are namespaced `/optimystic/strand-<id>/…`,
|
|
27
|
+
* so the relay is never in its cohort).
|
|
28
|
+
*
|
|
29
|
+
* `network.relayAddrs` is FAIL-FAST for the operator on both routes, just from
|
|
30
|
+
* different places: a malformed entry throws here at config resolution, and on the
|
|
31
|
+
* control node a first reservation attempt that does not land throws out of
|
|
32
|
+
* `start()` (`RelayReservationFailedError`). Naming a relay that is down still
|
|
33
|
+
* means the node does not come up.
|
|
34
|
+
*
|
|
35
|
+
* The bare `/p2p-circuit` search listener cannot open a connection on its own:
|
|
36
|
+
* libp2p fills a pending reservation from `RelayDiscovery`, which nominates a peer
|
|
37
|
+
* only once the relay-hop protocol id is in that peer's peer-store protocol list,
|
|
38
|
+
* and that list is written exclusively by IDENTIFY — which `@optimystic/db-p2p`
|
|
39
|
+
* namespaces per network, so a cadre node and a stock relay never identify each
|
|
40
|
+
* other. See `relay-reservation.ts`'s module doc and `docs/architecture.md`.
|
|
25
41
|
*/
|
|
26
42
|
import { multiaddr } from '@multiformats/multiaddr';
|
|
27
43
|
import { circuitRelayTargetOrThrow } from './delegate-admission.js';
|
|
@@ -36,6 +52,13 @@ import { circuitRelayTargetOrThrow } from './delegate-admission.js';
|
|
|
36
52
|
* ever changes its default, this constant has to follow.
|
|
37
53
|
*/
|
|
38
54
|
const DEFAULT_DIRECT_LISTEN_ADDR = '/ip4/0.0.0.0/tcp/0';
|
|
55
|
+
/**
|
|
56
|
+
* libp2p's relay SEARCH listener: registers one pending reservation and dials
|
|
57
|
+
* nothing. One entry covers every configured relay — the pending reservation is
|
|
58
|
+
* per-listener, not per-relay, and `relay-reservation.ts` fills it with whichever
|
|
59
|
+
* relay answers first.
|
|
60
|
+
*/
|
|
61
|
+
export const RELAY_SEARCH_LISTEN_ADDR = '/p2p-circuit';
|
|
39
62
|
/**
|
|
40
63
|
* The circuit-listen multiaddr for each configured relay: `<relayAddr>/p2p-circuit`.
|
|
41
64
|
* An entry that already carries `/p2p-circuit` is passed through unchanged.
|
|
@@ -52,20 +75,85 @@ export function relayCircuitAddrs(relayAddrs) {
|
|
|
52
75
|
}
|
|
53
76
|
/**
|
|
54
77
|
* The listen multiaddrs a node actually binds: configured `listenAddrs` plus the
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
78
|
+
* relay listen entry `route` calls for, deduplicated, order-stable (configured
|
|
79
|
+
* entries first). Returns `undefined` when neither field is set, so callers keep
|
|
80
|
+
* omitting `listenAddrs` and inherit db-p2p's default.
|
|
81
|
+
*
|
|
82
|
+
* Every `relayAddrs` entry is validated on BOTH routes — the `'search'` route
|
|
83
|
+
* discards the resolved circuit addrs, but an operator typo must fail at config
|
|
84
|
+
* resolution either way.
|
|
58
85
|
*/
|
|
59
|
-
export function resolveListenAddrs(network) {
|
|
60
|
-
const
|
|
86
|
+
export function resolveListenAddrs(network, route = 'configured') {
|
|
87
|
+
const configured = relayCircuitAddrs(network?.relayAddrs ?? []);
|
|
88
|
+
const relayEntries = route === 'search'
|
|
89
|
+
? (configured.length > 0 ? [RELAY_SEARCH_LISTEN_ADDR] : [])
|
|
90
|
+
: configured;
|
|
61
91
|
const listenAddrs = network?.listenAddrs;
|
|
62
|
-
if (
|
|
92
|
+
if (route === 'search') {
|
|
93
|
+
rejectConfiguredCircuitListenAddrs(listenAddrs ?? []);
|
|
94
|
+
}
|
|
95
|
+
if (!listenAddrs && relayEntries.length === 0) {
|
|
63
96
|
return undefined;
|
|
64
97
|
}
|
|
65
98
|
// An explicitly empty `listenAddrs` (the React Native "cannot listen" case) stays
|
|
66
|
-
// empty — but a relay named alongside it still gets its
|
|
67
|
-
// acquiring
|
|
68
|
-
return dedupe([...(listenAddrs ?? [DEFAULT_DIRECT_LISTEN_ADDR]), ...
|
|
99
|
+
// empty — but a relay named alongside it still gets its listen entry, since
|
|
100
|
+
// acquiring a slot is the whole point of configuring a relay.
|
|
101
|
+
return dedupe([...(listenAddrs ?? [DEFAULT_DIRECT_LISTEN_ADDR]), ...relayEntries]);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Thrown out of `CadreNode.start()` when the boot-path reservation drive for
|
|
105
|
+
* `network.relayAddrs` produces no `/p2p-circuit` address on its FIRST attempt.
|
|
106
|
+
*
|
|
107
|
+
* This is what keeps `network.relayAddrs` fail-fast now that the control node
|
|
108
|
+
* takes the `'search'` route: libp2p's own `UnsupportedListenAddressesError` used
|
|
109
|
+
* to abort start from inside `listen()`, and an operator who names a relay is
|
|
110
|
+
* telling the node it has no other reachability — coming up undialable is worse
|
|
111
|
+
* than not coming up. Names the relays and the reservation's own reason, both of
|
|
112
|
+
* which the libp2p error omitted.
|
|
113
|
+
*/
|
|
114
|
+
export class RelayReservationFailedError extends Error {
|
|
115
|
+
constructor(relayAddrs, state) {
|
|
116
|
+
super(`network.relayAddrs reservation failed (status: ${state.status}): ` +
|
|
117
|
+
`${state.error ?? 'no /p2p-circuit address appeared'} — relays: ${relayAddrs.join(', ')}`);
|
|
118
|
+
this.relayAddrs = relayAddrs;
|
|
119
|
+
this.state = state;
|
|
120
|
+
this.name = 'RelayReservationFailedError';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A hand-written `<relay>/p2p-circuit` entry in `network.listenAddrs` is the
|
|
125
|
+
* CONFIGURED listener shape, and it is unusable on the `'search'` route: libp2p
|
|
126
|
+
* dials that relay from inside `listen()`, the bring-up quiet period
|
|
127
|
+
* (`membership-connection-gater.ts`) denies exactly that dial, `listen()` fails, and
|
|
128
|
+
* the transport manager's default `FATAL_ALL` aborts `libp2p.start()`. The operator
|
|
129
|
+
* would see `UnsupportedListenAddressesError` from deep inside bring-up with nothing
|
|
130
|
+
* naming the cause, so name it here instead.
|
|
131
|
+
*
|
|
132
|
+
* `network.relayAddrs` is the exact replacement — same relay, and the reservation is
|
|
133
|
+
* driven after bring-up, which is the whole point of the route.
|
|
134
|
+
*/
|
|
135
|
+
function rejectConfiguredCircuitListenAddrs(listenAddrs) {
|
|
136
|
+
const configured = listenAddrs.filter(isConfiguredCircuitListenAddr);
|
|
137
|
+
if (configured.length > 0) {
|
|
138
|
+
throw new Error('network.listenAddrs names a relay directly ' +
|
|
139
|
+
`(${configured.join(', ')}), which a control node cannot listen on: libp2p would ` +
|
|
140
|
+
'dial that relay during control-database bring-up, when the node accepts no ' +
|
|
141
|
+
'connections. Move the relay to network.relayAddrs, which reserves after bring-up.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** `<something>/p2p-circuit` — the configured shape, as opposed to the bare search addr. */
|
|
145
|
+
function isConfiguredCircuitListenAddr(listenAddr) {
|
|
146
|
+
if (listenAddr === RELAY_SEARCH_LISTEN_ADDR) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
return multiaddr(listenAddr).getComponents().some((c) => c.name === 'p2p-circuit');
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Not our error to raise: libp2p reports an unparsable listen addr itself, and
|
|
154
|
+
// this helper only ever ADDS a denial.
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
69
157
|
}
|
|
70
158
|
/**
|
|
71
159
|
* One entry of {@link relayCircuitAddrs}: the addr the node listens on for this relay.
|
package/dist/relay-addrs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-addrs.js","sourceRoot":"","sources":["../src/relay-addrs.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"relay-addrs.js","sourceRoot":"","sources":["../src/relay-addrs.ts"],"names":[],"mappings":"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"}
|