@serfab/cadre-core 0.11.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/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 +281 -30
- package/dist/cadre-node.js +513 -103
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-database.d.ts +155 -23
- package/dist/control-database.js +272 -104
- package/dist/control-database.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 +20 -4
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +59 -35
- package/dist/control-write-retry.js +21 -92
- 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 +6 -4
- package/dist/index.js +17 -8
- 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-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/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-formation-manager.d.ts +10 -9
- package/dist/strand-formation-manager.js +10 -9
- 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 +4 -3
- package/dist/strand-instance-manager.js +31 -6
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +106 -14
- package/dist/strand-membership-writer.js +180 -14
- 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 +74 -29
- package/dist/types.js.map +1 -1
- package/package.json +8 -8
- package/src/announce-addrs.ts +93 -0
- package/src/cadre-node.ts +5603 -5158
- package/src/control-database.ts +2584 -2406
- package/src/control-read-retry.ts +167 -0
- package/src/control-retry.ts +190 -0
- package/src/control-schema.ts +20 -4
- package/src/control-write-retry.ts +30 -130
- package/src/delegate-admission.ts +17 -15
- package/src/index.ts +435 -407
- package/src/membership-connection-gater.ts +533 -204
- package/src/peer-record.ts +33 -0
- package/src/relay-addrs.ts +218 -111
- package/src/relay-reservation.ts +828 -823
- package/src/strand-addr-protocol.ts +17 -0
- package/src/strand-backfill.ts +389 -366
- package/src/strand-formation-manager.ts +492 -491
- package/src/strand-formation-protocol.ts +8 -8
- package/src/strand-instance-manager.ts +636 -611
- package/src/strand-membership-writer.ts +209 -14
- package/src/strand-solicitation.ts +6 -7
- package/src/strand-wake-protocol.ts +94 -8
- package/src/types.ts +74 -29
package/dist/types.d.ts
CHANGED
|
@@ -98,12 +98,12 @@ export declare const HIBERNATION_TIMEOUTS: Record<LatencyHint, HibernationTimeou
|
|
|
98
98
|
* cadre-core disposes only its own cache wrapper — it never closes the store you
|
|
99
99
|
* returned. Closing the underlying handle stays the embedder's job.
|
|
100
100
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
101
|
+
* Either form is safe with a persistent backend. One instance handed to every scope
|
|
102
|
+
* also shares ONE cache wrapper (`wrapStorageWithCache` memoizes per inner instance),
|
|
103
|
+
* which counts its holders: a scope stopping releases only its own claim, and the
|
|
104
|
+
* wrapper is retired when the last scope releases it. The factory form is still the
|
|
105
|
+
* better default for a different reason — it partitions each strand's data, which
|
|
106
|
+
* simplifies cleanup.
|
|
107
107
|
*/
|
|
108
108
|
export type RawStorageProvider = IRawStorage | ((strandId: string) => IRawStorage);
|
|
109
109
|
/**
|
|
@@ -142,26 +142,48 @@ export interface StorageConfig {
|
|
|
142
142
|
export interface NetworkConfig {
|
|
143
143
|
listenAddrs?: string[];
|
|
144
144
|
/**
|
|
145
|
-
* Addresses to advertise to peers instead of `listenAddrs` — for a node behind
|
|
145
|
+
* Addresses to advertise to peers **instead of** `listenAddrs` — for a node behind
|
|
146
146
|
* NAT or a reverse proxy that is reachable at a different address than it binds
|
|
147
|
-
* (e.g. `mynode.example.com:4001` in front of a `0.0.0.0:4001` listener).
|
|
147
|
+
* (e.g. `mynode.example.com:4001` in front of a `0.0.0.0:4001` listener). Reaches
|
|
148
|
+
* libp2p's `addresses.announce` via `@optimystic/db-p2p`'s `NodeOptions.announceAddrs`,
|
|
149
|
+
* on this node's control node and on every strand node it runs.
|
|
148
150
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* request filed against `@optimystic/db-p2p`; wiring lands here once this repo's
|
|
156
|
-
* dependency range moves to a version carrying it.
|
|
151
|
+
* **A non-empty value REPLACES the advertised set entirely.** Observed addresses,
|
|
152
|
+
* and the `/p2p-circuit` address earned from a {@link relayAddrs} reservation, are
|
|
153
|
+
* all dropped from what peers are told — so a node configured with both this and
|
|
154
|
+
* `relayAddrs` stops being reachable through its relay. `CadreNode.start()` warns
|
|
155
|
+
* when it sees that combination; it does not refuse it, since a node whose relay
|
|
156
|
+
* slot is decorative may legitimately want only the announced address.
|
|
157
157
|
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
158
|
+
* Reach for {@link appendAnnounceAddrs} instead in the common case — one publicly
|
|
159
|
+
* reachable address ADDED to everything else the node advertises. Or for
|
|
160
|
+
* {@link relayAddrs} alone, when the node has no stable public address to name and
|
|
161
|
+
* a relay reservation is what makes it dialable. (`inviteAddressResolver` is a
|
|
162
|
+
* narrower tool again: it substitutes a reachable address into invite payloads
|
|
163
|
+
* only, and changes nothing about what this node advertises on the wire.)
|
|
164
|
+
*
|
|
165
|
+
* An empty array means "unset" — it is dropped rather than forwarded, so it cannot
|
|
166
|
+
* land as an explicit empty announce set. An empty *entry* is a different thing and
|
|
167
|
+
* is rejected: a list of one blank string is non-empty, so it would replace every
|
|
168
|
+
* advertised address with nothing. Malformed entries are rejected on the same terms,
|
|
169
|
+
* both at node start — libp2p itself would not notice either until its first address
|
|
170
|
+
* lookup (see `announce-addrs.ts`), so this repo checks each entry up front.
|
|
163
171
|
*/
|
|
164
172
|
announceAddrs?: string[];
|
|
173
|
+
/**
|
|
174
|
+
* Addresses to advertise **in addition to** `listenAddrs` — the field most
|
|
175
|
+
* deployments actually want, since it makes a node reachable at a public address
|
|
176
|
+
* without discarding the observed and `/p2p-circuit` addresses it would otherwise
|
|
177
|
+
* advertise. Reaches libp2p's `addresses.appendAnnounce` via
|
|
178
|
+
* `@optimystic/db-p2p`'s `NodeOptions.appendAnnounceAddrs`.
|
|
179
|
+
*
|
|
180
|
+
* **Ignored while {@link announceAddrs} is non-empty** — that is libp2p's own
|
|
181
|
+
* precedence, applied upstream; this repo does not merge the two locally. Set one
|
|
182
|
+
* or the other, not both.
|
|
183
|
+
*
|
|
184
|
+
* Empty-array, blank-entry and malformed-entry handling all match {@link announceAddrs}.
|
|
185
|
+
*/
|
|
186
|
+
appendAnnounceAddrs?: string[];
|
|
165
187
|
/**
|
|
166
188
|
* Circuit-relay servers this node reserves a slot on, so peers can reach it
|
|
167
189
|
* from behind NAT. Each entry is the relay's DIRECT dial multiaddr ending in
|
|
@@ -169,18 +191,26 @@ export interface NetworkConfig {
|
|
|
169
191
|
* `/p2p-circuit` suffix (one is appended for you; an entry that already has
|
|
170
192
|
* one is passed through unchanged).
|
|
171
193
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
194
|
+
* On the CONTROL node this adds libp2p's bare `/p2p-circuit` SEARCH listener to
|
|
195
|
+
* {@link listenAddrs} (`relay-addrs.ts`) and `CadreNode.start()` drives the
|
|
196
|
+
* reservation itself once the control database is up — the ordering matters,
|
|
197
|
+
* because a listener that dials its relay from inside `libp2p.start()` put a
|
|
198
|
+
* sibling in this node's cohort before its own database existed, and a sibling
|
|
199
|
+
* that had not yet replicated this node's membership row refused the bring-up.
|
|
200
|
+
* STRAND nodes inherit the older per-relay `<relay>/p2p-circuit` listener, which
|
|
201
|
+
* has no such ordering hazard. A relay named here also becomes a
|
|
202
|
+
* delegate-announce target, so this node's strand nodes may reserve on it too
|
|
203
|
+
* (see `delegate-admission.ts`).
|
|
177
204
|
*
|
|
178
205
|
* Setting this gives the node a listener even when `listenAddrs` is `[]` —
|
|
179
206
|
* that is the point of naming a relay, but note that React Native hosts
|
|
180
207
|
* (`reference-app-rn`'s `cadre-phone.ts`) deliberately do NOT listen, and
|
|
181
208
|
* must not acquire a listener by accident.
|
|
182
209
|
*
|
|
183
|
-
*
|
|
210
|
+
* FAIL-FAST both ways: a malformed entry throws at node start rather than being
|
|
211
|
+
* silently dropped, and a first reservation attempt that lands no
|
|
212
|
+
* `/p2p-circuit` address throws `RelayReservationFailedError` out of `start()`.
|
|
213
|
+
* The fail-soft posture over the same machinery is `CadreNode.reserveRelays()`.
|
|
184
214
|
*/
|
|
185
215
|
relayAddrs?: string[];
|
|
186
216
|
/**
|
|
@@ -189,6 +219,16 @@ export interface NetworkConfig {
|
|
|
189
219
|
* better connectivity and uptime), false for transaction profile nodes.
|
|
190
220
|
*/
|
|
191
221
|
enableRelay?: boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Cap on concurrent circuit-relay reservations this node's relay server grants to
|
|
224
|
+
* peers it cannot (yet) recognize as authorized members — the boot-ordering window
|
|
225
|
+
* where a genuine member reserves its relay slot before its membership row has
|
|
226
|
+
* replicated here (see `membership-connection-gater.ts` → "The relay-reservation
|
|
227
|
+
* seam"). Authorized members and announced delegate peers are never counted against
|
|
228
|
+
* it. Default `MAX_UNAUTHORIZED_RELAY_RESERVATIONS` (8); 0 refuses every
|
|
229
|
+
* unauthorized reservation. Only meaningful while {@link enableRelay} is on.
|
|
230
|
+
*/
|
|
231
|
+
unauthorizedRelayReservationCap?: number;
|
|
192
232
|
/**
|
|
193
233
|
* Custom libp2p transports. When omitted, the default transports from
|
|
194
234
|
* `@optimystic/db-p2p` are used (TCP + circuit relay for Node.js).
|
|
@@ -211,6 +251,11 @@ export interface NetworkConfig {
|
|
|
211
251
|
* (and other owner-address contexts). When unset, `libp2pNode.getMultiaddrs()`
|
|
212
252
|
* is used. Hosts behind NAT supply this to substitute their DDNS hostname
|
|
213
253
|
* and externally-mapped port — see `@serfab/cadre-host`'s NatService.
|
|
254
|
+
*
|
|
255
|
+
* Returned addresses need NOT carry a `/p2p/<peerId>` suffix: `CadreNode`
|
|
256
|
+
* appends its own before publishing or dialing any of them, so a resolver
|
|
257
|
+
* cannot leave a sibling with the mixed suffixed/unsuffixed list that
|
|
258
|
+
* `libp2p.dial` rejects. Returning them already suffixed is equally fine.
|
|
214
259
|
*/
|
|
215
260
|
inviteAddressResolver?: () => Promise<string[]>;
|
|
216
261
|
/**
|
|
@@ -323,8 +368,8 @@ export interface CadreNodeConfig {
|
|
|
323
368
|
* Pluggable secure store for node key material. When set, the node loads its
|
|
324
369
|
* identity from `keyStore` under {@link identityKeyId}, generating + persisting
|
|
325
370
|
* a fresh Ed25519 key on first run (protobuf bytes are the canonical stored
|
|
326
|
-
* form). Mutually exclusive with {@link privateKey}. Absent ⇒
|
|
327
|
-
*
|
|
371
|
+
* form). Mutually exclusive with {@link privateKey}. Absent ⇒ use `privateKey`
|
|
372
|
+
* when set, else libp2p generates an ephemeral key.
|
|
328
373
|
*
|
|
329
374
|
* In the single-key reference model the owner signing key is *derived from*
|
|
330
375
|
* the node identity (see {@link CadreNode.getIdentityOwnerKey}), so
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2EA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2EA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;AA4PF;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AAYnD;;;;;;;;;;;GAWG;AACH,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,+BAA+B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serfab/cadre-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core library for Sereus cadre nodes - manages control networks and strand participation",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@libp2p/tcp": "^11.0.10",
|
|
68
68
|
"@libp2p/webrtc": "6.0.14",
|
|
69
69
|
"@libp2p/websockets": "^10.1.3",
|
|
70
|
-
"@optimystic/db-p2p-storage-fs": "^0.
|
|
70
|
+
"@optimystic/db-p2p-storage-fs": "^0.27.0",
|
|
71
71
|
"@types/debug": "^4.1.12",
|
|
72
72
|
"@types/node": "^25.0.10",
|
|
73
73
|
"@vitest/coverage-v8": "^4.0.17",
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"@libp2p/interface": "^3.1.0",
|
|
81
81
|
"@libp2p/peer-id": "^6.0.4",
|
|
82
82
|
"@multiformats/multiaddr": "^12.5.1",
|
|
83
|
-
"@optimystic/db-core": "^0.
|
|
84
|
-
"@optimystic/db-p2p": "^0.
|
|
85
|
-
"@optimystic/quereus-plugin-crypto": "^0.
|
|
86
|
-
"@optimystic/quereus-plugin-optimystic": "^0.
|
|
87
|
-
"@quereus/quereus": "^4.
|
|
88
|
-
"@serfab/quereus-plugin-sereus": "^0.
|
|
83
|
+
"@optimystic/db-core": "^0.27.0",
|
|
84
|
+
"@optimystic/db-p2p": "^0.27.0",
|
|
85
|
+
"@optimystic/quereus-plugin-crypto": "^0.27.0",
|
|
86
|
+
"@optimystic/quereus-plugin-optimystic": "^0.27.0",
|
|
87
|
+
"@quereus/quereus": "^4.18.0",
|
|
88
|
+
"@serfab/quereus-plugin-sereus": "^0.12.0",
|
|
89
89
|
"debug": "^4.4.3",
|
|
90
90
|
"libp2p": "^3.1.3",
|
|
91
91
|
"uint8arrays": "^5.1.0"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translate `network.announceAddrs` / `network.appendAnnounceAddrs` — the operator-facing
|
|
3
|
+
* "advertise these addresses" settings — into the two `@optimystic/db-p2p` `NodeOptions`
|
|
4
|
+
* fields of the same names, which reach libp2p's `addresses.announce` /
|
|
5
|
+
* `addresses.appendAnnounce`.
|
|
6
|
+
*
|
|
7
|
+
* Two jobs, both of which the control node and every strand node need identically, hence
|
|
8
|
+
* one module rather than the same conditional spread written twice:
|
|
9
|
+
*
|
|
10
|
+
* - **Empty means unset.** libp2p treats `announce: []` as "no override", and forwarding
|
|
11
|
+
* `[]` would still be forwarding a field. An empty config array is dropped so it cannot
|
|
12
|
+
* land as an explicit empty announce set.
|
|
13
|
+
* - **Validate the multiaddrs here, at start.** libp2p does NOT validate announce addrs
|
|
14
|
+
* at construction: `AddressManager` stores them as raw strings and only parses them on
|
|
15
|
+
* the first `getAnnounceAddrs()`. A typo therefore produces a node that starts
|
|
16
|
+
* cleanly, then throws `InvalidMultiaddrError` out of every `getMultiaddrs()` call —
|
|
17
|
+
* including an UNHANDLED one from the debounced peer-store update libp2p runs on
|
|
18
|
+
* `transport:listening`. Verified against libp2p's `address-manager` (its constructor
|
|
19
|
+
* does `announce.map(ma => ma.toString())`; `getAnnounceAddrs()` does `multiaddr(a)`).
|
|
20
|
+
* Parsing each entry up front converts that into a loud failure at node start, which is
|
|
21
|
+
* what {@link relayCircuitAddrs} already does for `network.relayAddrs`.
|
|
22
|
+
*
|
|
23
|
+
* Parsing alone is not enough, though: `''` and `'/'` both parse into a component-less
|
|
24
|
+
* multiaddr, and a set holding only those is non-empty by the rule above — so it would
|
|
25
|
+
* replace every advertised address with nothing at all. An entry naming no address is
|
|
26
|
+
* therefore rejected alongside one that fails to parse.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { multiaddr, type Multiaddr } from '@multiformats/multiaddr';
|
|
30
|
+
import type { NetworkConfig } from './types.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The announce fields to spread into `createLibp2pNode` options. A field is present
|
|
34
|
+
* only when the operator configured a non-empty list for it, so a node that configures
|
|
35
|
+
* neither passes neither.
|
|
36
|
+
*/
|
|
37
|
+
export interface ResolvedAnnounceAddrs {
|
|
38
|
+
announceAddrs?: string[];
|
|
39
|
+
appendAnnounceAddrs?: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve both announce fields off a `NetworkConfig`. Throws — naming the offending
|
|
44
|
+
* config field and entry — when any entry is not a parsable multiaddr.
|
|
45
|
+
*/
|
|
46
|
+
export function resolveAnnounceAddrs(network: NetworkConfig | undefined): ResolvedAnnounceAddrs {
|
|
47
|
+
const announceAddrs = configuredAddrs('network.announceAddrs', network?.announceAddrs);
|
|
48
|
+
const appendAnnounceAddrs = configuredAddrs('network.appendAnnounceAddrs', network?.appendAnnounceAddrs);
|
|
49
|
+
return {
|
|
50
|
+
...(announceAddrs && { announceAddrs }),
|
|
51
|
+
...(appendAnnounceAddrs && { appendAnnounceAddrs })
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether this node's advertised set will be REPLACED rather than extended — i.e.
|
|
57
|
+
* whether {@link ResolvedAnnounceAddrs.announceAddrs} is in play at all. The single
|
|
58
|
+
* predicate behind `CadreNode.start()`'s operator warning, kept here so the warning
|
|
59
|
+
* and the forwarding agree on what "set" means (non-empty, not merely present).
|
|
60
|
+
*/
|
|
61
|
+
export function replacesAdvertisedAddrs(network: NetworkConfig | undefined): boolean {
|
|
62
|
+
return (network?.announceAddrs?.length ?? 0) > 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The entries to forward for one announce field, or `undefined` when it is unset or empty. */
|
|
66
|
+
function configuredAddrs(field: string, addrs: readonly string[] | undefined): string[] | undefined {
|
|
67
|
+
if (!addrs || addrs.length === 0) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
return addrs.map((addr) => validated(field, addr));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** `addr` unchanged, having proved it names a real address — config field named on failure. */
|
|
74
|
+
function validated(field: string, addr: string): string {
|
|
75
|
+
if (parsed(field, addr).getComponents().length === 0) {
|
|
76
|
+
// `''` and `'/'` PARSE: both yield a component-less multiaddr that stringifies to
|
|
77
|
+
// `/`. Forwarded, they make an announce set that is non-empty but names nothing —
|
|
78
|
+
// which by the replacement rule above costs the node every address it would
|
|
79
|
+
// otherwise advertise, silently. A templated `cadre.yaml` whose address variable went
|
|
80
|
+
// unsubstituted is the realistic way in, so it is rejected rather than forwarded.
|
|
81
|
+
throw new Error(`${field} entry names no address: ${JSON.stringify(addr)}`);
|
|
82
|
+
}
|
|
83
|
+
return addr;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** `addr` as a multiaddr, with the config field named on a parse failure. */
|
|
87
|
+
function parsed(field: string, addr: string): Multiaddr {
|
|
88
|
+
try {
|
|
89
|
+
return multiaddr(addr);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
throw new Error(`${field} entry is not a valid multiaddr: ${addr}`, { cause: err });
|
|
92
|
+
}
|
|
93
|
+
}
|