@serfab/cadre-core 1.7.0 → 1.9.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 +8 -6
- package/dist/cadre-node.d.ts +527 -206
- package/dist/cadre-node.js +1084 -528
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +2 -2
- package/dist/control-authorization.js +1 -0
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +6 -0
- package/dist/control-cohort.js +6 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +46 -8
- package/dist/control-database.js +97 -18
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +37 -14
- package/dist/control-formation-recorder.js +60 -10
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-read-retry.d.ts +13 -12
- package/dist/control-read-retry.js +14 -12
- package/dist/control-read-retry.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +67 -5
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +9 -1
- package/dist/control-stream.js +24 -1
- package/dist/control-stream.js.map +1 -1
- package/dist/control-write-retry.d.ts +12 -2
- package/dist/control-write-retry.js +13 -2
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +11 -8
- package/dist/delegate-admission.js +19 -11
- package/dist/delegate-admission.js.map +1 -1
- package/dist/diagnostics/connection-path.d.ts +0 -7
- package/dist/diagnostics/connection-path.js +0 -7
- package/dist/diagnostics/connection-path.js.map +1 -1
- package/dist/formation-approval.d.ts +7 -0
- package/dist/formation-approval.js +9 -3
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +11 -2
- package/dist/fs-atomic.js +13 -3
- package/dist/fs-atomic.js.map +1 -1
- package/dist/hibernation-manager.d.ts +111 -8
- package/dist/hibernation-manager.js +234 -49
- package/dist/hibernation-manager.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +25 -20
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +90 -25
- package/dist/joined-strand-store.js +223 -55
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +14 -5
- package/dist/key-store-file.js.map +1 -1
- package/dist/link-budget.d.ts +367 -69
- package/dist/link-budget.js +375 -72
- package/dist/link-budget.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +30 -43
- package/dist/membership-connection-gater.js +31 -42
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-local-snapshot.d.ts +6 -4
- package/dist/node-local-snapshot.js +7 -3
- package/dist/node-local-snapshot.js.map +1 -1
- package/dist/peer-dial.d.ts +24 -4
- package/dist/peer-dial.js +74 -5
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +2 -10
- package/dist/peer-join-backfill.js +8 -8
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/push-fanout.d.ts +6 -0
- package/dist/push-fanout.js +6 -0
- package/dist/push-fanout.js.map +1 -1
- package/dist/relay-reservation.d.ts +3 -1
- package/dist/relay-reservation.js +3 -1
- package/dist/relay-reservation.js.map +1 -1
- package/dist/relay-server.d.ts +79 -0
- package/dist/relay-server.js +74 -0
- package/dist/relay-server.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +57 -4
- package/dist/seed-bootstrap.js +107 -83
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/storage-scope.d.ts +33 -17
- package/dist/storage-scope.js +44 -23
- package/dist/storage-scope.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +58 -21
- package/dist/strand-addr-protocol.js +133 -85
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-database.d.ts +20 -2
- package/dist/strand-database.js +42 -5
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +59 -21
- package/dist/strand-first-sync-gate.js +58 -19
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +151 -0
- package/dist/strand-formation-deadlines.js +171 -0
- package/dist/strand-formation-deadlines.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +63 -30
- package/dist/strand-formation-manager.js +67 -41
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +45 -46
- package/dist/strand-formation-protocol.js +62 -126
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +54 -0
- package/dist/strand-fret-addrs.js +108 -0
- package/dist/strand-fret-addrs.js.map +1 -0
- package/dist/strand-instance-manager.d.ts +122 -44
- package/dist/strand-instance-manager.js +270 -114
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +6 -0
- package/dist/strand-membership-writer.js +11 -0
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-network-state-file.d.ts +12 -0
- package/dist/strand-network-state-file.js +38 -0
- package/dist/strand-network-state-file.js.map +1 -0
- package/dist/strand-network-state.d.ts +111 -0
- package/dist/strand-network-state.js +191 -0
- package/dist/strand-network-state.js.map +1 -0
- package/dist/strand-revocation-enforcer.js +2 -1
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +36 -20
- package/dist/strand-solicitation.js +4 -4
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +80 -23
- package/dist/strand-wake-protocol.js +89 -64
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +148 -61
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/package.json +11 -10
- package/src/cadre-node.ts +8400 -7776
- package/src/control-authorization.ts +2 -1
- package/src/control-cohort.ts +6 -0
- package/src/control-database.ts +122 -18
- package/src/control-formation-recorder.ts +354 -305
- package/src/control-read-retry.ts +14 -12
- package/src/control-schema.ts +67 -5
- package/src/control-stream.ts +25 -1
- package/src/control-write-retry.ts +13 -2
- package/src/delegate-admission.ts +23 -13
- package/src/diagnostics/connection-path.ts +0 -7
- package/src/formation-approval.ts +610 -604
- package/src/fs-atomic.ts +14 -4
- package/src/hibernation-manager.ts +638 -437
- package/src/index.ts +43 -65
- package/src/joined-strand-store.ts +247 -57
- package/src/key-store-file.ts +14 -5
- package/src/link-budget.ts +393 -74
- package/src/membership-connection-gater.ts +31 -45
- package/src/node-local-snapshot.ts +12 -7
- package/src/peer-dial.ts +81 -7
- package/src/peer-join-backfill.ts +10 -9
- package/src/push-fanout.ts +6 -0
- package/src/relay-reservation.ts +3 -1
- package/src/relay-server.ts +98 -0
- package/src/seed-bootstrap.ts +133 -91
- package/src/storage-scope.ts +44 -23
- package/src/strand-addr-protocol.ts +547 -458
- package/src/strand-database.ts +287 -242
- package/src/strand-first-sync-gate.ts +58 -19
- package/src/strand-formation-deadlines.ts +227 -0
- package/src/strand-formation-manager.ts +700 -647
- package/src/strand-formation-protocol.ts +106 -154
- package/src/strand-fret-addrs.ts +145 -0
- package/src/strand-instance-manager.ts +307 -140
- package/src/strand-membership-writer.ts +12 -0
- package/src/strand-network-state-file.ts +40 -0
- package/src/strand-network-state.ts +243 -0
- package/src/strand-revocation-enforcer.ts +2 -1
- package/src/strand-solicitation.ts +519 -500
- package/src/strand-wake-protocol.ts +401 -355
- package/src/types.ts +150 -61
- package/dist/strand-peer-book-file.d.ts +0 -12
- package/dist/strand-peer-book-file.js +0 -37
- package/dist/strand-peer-book-file.js.map +0 -1
- package/dist/strand-peer-book-protocol.d.ts +0 -159
- package/dist/strand-peer-book-protocol.js +0 -360
- package/dist/strand-peer-book-protocol.js.map +0 -1
- package/dist/strand-peer-book-swap.d.ts +0 -104
- package/dist/strand-peer-book-swap.js +0 -284
- package/dist/strand-peer-book-swap.js.map +0 -1
- package/dist/strand-peer-book.d.ts +0 -159
- package/dist/strand-peer-book.js +0 -354
- package/dist/strand-peer-book.js.map +0 -1
- package/dist/strand-peer-observer.d.ts +0 -88
- package/dist/strand-peer-observer.js +0 -180
- package/dist/strand-peer-observer.js.map +0 -1
- package/src/strand-peer-book-file.ts +0 -39
- package/src/strand-peer-book-protocol.ts +0 -463
- package/src/strand-peer-book-swap.ts +0 -338
- package/src/strand-peer-book.ts +0 -468
- package/src/strand-peer-observer.ts +0 -235
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The per-strand driver of the strand peer book SWAP (`strand-peer-book-protocol.ts`
|
|
3
|
-
* has the wire format, the signatures and the reasons): one of these per running
|
|
4
|
-
* strand, armed on the strand's libp2p node right after it exists and released with
|
|
5
|
-
* it, so a hibernation wake re-arms one on the rebuilt node. It owns three things:
|
|
6
|
-
*
|
|
7
|
-
* - **This node's own signed entry.** Built from the node's own bound addresses
|
|
8
|
-
* (`dialableAddrs`, the binding the observer applies to a remote), stamped with the
|
|
9
|
-
* local clock, signed with the strand transport key, and filed in the book under
|
|
10
|
-
* the node's own peer id. Signed at arm time and RE-SIGNED whenever the address set
|
|
11
|
-
* changes — `self:peer:update`, debounced because a relay reservation landing fires
|
|
12
|
-
* it more than once — which is what makes a rotation propagate. An unchanged
|
|
13
|
-
* address set is deliberately not re-signed on a timer: freshness is about change,
|
|
14
|
-
* and a periodic re-sign would only make every peer rewrite its slot.
|
|
15
|
-
* - **The receiver**, registered through `node.handle` so a closed strand's revocation
|
|
16
|
-
* gate judges every inbound stream first.
|
|
17
|
-
* - **The exchanges.** One when a strand peer is identified (the same `peer:identify`
|
|
18
|
-
* + `speaksBlockTransfer` gate the observer and the peer-join catch-up use, plus
|
|
19
|
-
* "lists the swap protocol", so an older node is skipped rather than dialed),
|
|
20
|
-
* throttled to one per peer per {@link STRAND_PEER_BOOK_SWAP_THROTTLE_MS}; and one
|
|
21
|
-
* with EVERY connected strand peer whenever the own entry is re-signed, unthrottled,
|
|
22
|
-
* so a rotation reaches everyone while connections are up. Both sides do this, so
|
|
23
|
-
* one connection produces two small exchanges; accepted as simpler than electing an
|
|
24
|
-
* initiator. A failed exchange is logged, never fatal, and never retried before the
|
|
25
|
-
* throttle expires.
|
|
26
|
-
*
|
|
27
|
-
* What lands in the book: the sender's own statement is filed as seen now (there is a
|
|
28
|
-
* live connection to it); a forwarded third party's is filed as never seen, so it ages
|
|
29
|
-
* from the signer's `issuedAt` alone. The store's merge rule does the rest — a signed
|
|
30
|
-
* entry displaces an unsigned observation, and between two signed the greater
|
|
31
|
-
* `issuedAt` wins — so a stale forward never displaces a fresher statement already
|
|
32
|
-
* held. The seed and refresh read the book unchanged: a forwarded third party is
|
|
33
|
-
* dialed like any other peer, and a rotated peer's newer addresses displace its old.
|
|
34
|
-
*
|
|
35
|
-
* The own entry the node SENDS is the in-memory one, never re-read from the store, so
|
|
36
|
-
* the store's aging cannot lose it; the stored copy is a record for diagnostics and
|
|
37
|
-
* the seed's self-skip. Nothing here throws into a libp2p event handler.
|
|
38
|
-
*/
|
|
39
|
-
import debug from 'debug';
|
|
40
|
-
import { circuitRequestBudgetMs } from './link-budget.js';
|
|
41
|
-
import { speaksBlockTransfer } from './peer-join-backfill.js';
|
|
42
|
-
import { STRAND_PEER_BOOK_PROTOCOL, StrandPeerBookService, exchangeStrandPeerBook, signStrandPeerEntry, trimBookFrameToFit } from './strand-peer-book-protocol.js';
|
|
43
|
-
import { connectedIdentifiedPeers, dialableAddrs } from './strand-peer-observer.js';
|
|
44
|
-
const log = debug('sereus:cadre:strand-peer-book-swap');
|
|
45
|
-
/** Minimum gap between two exchanges with ONE peer, unless this node's own entry was re-signed. */
|
|
46
|
-
export const STRAND_PEER_BOOK_SWAP_THROTTLE_MS = 10 * 60 * 1000;
|
|
47
|
-
/** How long after the last `self:peer:update` the own entry is rebuilt — the event fires in bursts. */
|
|
48
|
-
export const OWN_ENTRY_RESIGN_DEBOUNCE_MS = 1000;
|
|
49
|
-
/**
|
|
50
|
-
* Bytes allowance on top of the link round trips in the exchange deadline. A frame is
|
|
51
|
-
* at most 64 KiB, so this is a token, not the 1 MiB push allowance.
|
|
52
|
-
*/
|
|
53
|
-
const SWAP_TRANSFER_ALLOWANCE_MS = 1000;
|
|
54
|
-
export class StrandPeerBookSwap {
|
|
55
|
-
constructor(deps, options) {
|
|
56
|
-
this.deps = deps;
|
|
57
|
-
/** Last exchange per peer id, for the throttle. */
|
|
58
|
-
this.exchangedAt = new Map();
|
|
59
|
-
/** Peers with an exchange in flight, so identify and a walk cannot double up. */
|
|
60
|
-
this.inFlight = new Set();
|
|
61
|
-
/**
|
|
62
|
-
* Peers whose in-flight exchange carries a since-replaced own entry: one more
|
|
63
|
-
* exchange follows the moment it settles. Without this a re-sign that lands while
|
|
64
|
-
* the identify exchange is still on the wire — the relay reservation arriving a
|
|
65
|
-
* second after the bootstrap dials do, which is routine — would never reach that
|
|
66
|
-
* peer until the next connection.
|
|
67
|
-
*/
|
|
68
|
-
this.resendAfter = new Set();
|
|
69
|
-
/** Serialises own-entry refreshes: a burst of triggers signs at most once per change. */
|
|
70
|
-
this.selfChain = Promise.resolve();
|
|
71
|
-
this.started = false;
|
|
72
|
-
this.stopped = false;
|
|
73
|
-
this.selfPeerId = deps.libp2p.peerId.toString();
|
|
74
|
-
this.signingKey = usableSigningKey(deps.privateKey, this.selfPeerId, deps.strandId);
|
|
75
|
-
this.throttleMs = options?.throttleMs ?? STRAND_PEER_BOOK_SWAP_THROTTLE_MS;
|
|
76
|
-
this.debounceMs = options?.debounceMs ?? OWN_ENTRY_RESIGN_DEBOUNCE_MS;
|
|
77
|
-
this.timeoutMs = options?.timeoutMs ?? circuitRequestBudgetMs(deps.linkRoundTripMs, SWAP_TRANSFER_ALLOWANCE_MS);
|
|
78
|
-
this.now = options?.now ?? Date.now;
|
|
79
|
-
this.service = new StrandPeerBookService({
|
|
80
|
-
strandId: deps.strandId,
|
|
81
|
-
selfPeerId: this.selfPeerId,
|
|
82
|
-
localEntries: (forPeerId) => this.frameFor(forPeerId).entries,
|
|
83
|
-
onEntries: (entries, fromPeerId) => this.accept(entries, fromPeerId),
|
|
84
|
-
readTimeoutMs: this.timeoutMs,
|
|
85
|
-
now: this.now
|
|
86
|
-
});
|
|
87
|
-
this.onPeerIdentify = (evt) => {
|
|
88
|
-
const { peerId, protocols, connection } = evt.detail;
|
|
89
|
-
if (this.isSwapPeer(peerId, protocols)) {
|
|
90
|
-
void this.exchangeWith(peerId, connection, false, 'identify');
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
this.onSelfUpdate = () => {
|
|
94
|
-
if (this.debounceTimer)
|
|
95
|
-
clearTimeout(this.debounceTimer);
|
|
96
|
-
this.debounceTimer = setTimeout(() => {
|
|
97
|
-
this.debounceTimer = undefined;
|
|
98
|
-
this.queueSelfRefresh('self:peer:update');
|
|
99
|
-
}, this.debounceMs);
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
/** This node's current signed entry, or `undefined` before the first signing or without a key. */
|
|
103
|
-
get ownEntry() {
|
|
104
|
-
return this.own;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Register the receiver, subscribe, sign the own entry, and exchange with the strand
|
|
108
|
-
* peers already connected — identify for an early peer may have fired before this
|
|
109
|
-
* object existed.
|
|
110
|
-
*/
|
|
111
|
-
start() {
|
|
112
|
-
if (this.started || this.stopped)
|
|
113
|
-
return;
|
|
114
|
-
this.started = true;
|
|
115
|
-
this.deps.libp2p.addEventListener('peer:identify', this.onPeerIdentify);
|
|
116
|
-
this.deps.libp2p.addEventListener('self:peer:update', this.onSelfUpdate);
|
|
117
|
-
void this.service.initialize(this.deps.libp2p).catch((error) => {
|
|
118
|
-
log('[%s] registering the swap handler failed — this node answers no swaps: %o', this.deps.strandId, error);
|
|
119
|
-
});
|
|
120
|
-
this.queueSelfRefresh('start');
|
|
121
|
-
}
|
|
122
|
-
/** Unsubscribe and unregister; an exchange in flight finishes on its own. */
|
|
123
|
-
async stop() {
|
|
124
|
-
if (this.stopped)
|
|
125
|
-
return;
|
|
126
|
-
this.stopped = true;
|
|
127
|
-
if (this.debounceTimer) {
|
|
128
|
-
clearTimeout(this.debounceTimer);
|
|
129
|
-
this.debounceTimer = undefined;
|
|
130
|
-
}
|
|
131
|
-
if (this.started) {
|
|
132
|
-
this.deps.libp2p.removeEventListener('peer:identify', this.onPeerIdentify);
|
|
133
|
-
this.deps.libp2p.removeEventListener('self:peer:update', this.onSelfUpdate);
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
await this.service.shutdown();
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
log('[%s] unregistering the swap handler failed (ignored): %o', this.deps.strandId, error);
|
|
140
|
-
}
|
|
141
|
-
this.exchangedAt.clear();
|
|
142
|
-
log('[%s] stopped', this.deps.strandId);
|
|
143
|
-
}
|
|
144
|
-
/** A strand peer that also speaks the swap; never self. */
|
|
145
|
-
isSwapPeer(peerId, protocols) {
|
|
146
|
-
if (peerId.toString() === this.selfPeerId || !speaksBlockTransfer(protocols, this.deps.protocolPrefix)) {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
if (!protocols.includes(STRAND_PEER_BOOK_PROTOCOL)) {
|
|
150
|
-
log('[%s] strand peer %s does not speak the swap (older node) — not dialing it', this.deps.strandId, peerId.toString());
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
queueSelfRefresh(reason) {
|
|
156
|
-
this.selfChain = this.selfChain
|
|
157
|
-
.then(() => this.refreshSelf(reason))
|
|
158
|
-
.catch((error) => log('[%s] own-entry refresh (%s) failed: %o', this.deps.strandId, reason, error));
|
|
159
|
-
}
|
|
160
|
-
/** Re-sign the own entry if the address set changed, then exchange — unthrottled after a re-sign. */
|
|
161
|
-
async refreshSelf(reason) {
|
|
162
|
-
if (this.stopped)
|
|
163
|
-
return;
|
|
164
|
-
const resigned = await this.refreshOwnEntry(reason);
|
|
165
|
-
await this.exchangeWithConnected(resigned, reason);
|
|
166
|
-
}
|
|
167
|
-
/** Returns whether a new own entry was signed. */
|
|
168
|
-
async refreshOwnEntry(reason) {
|
|
169
|
-
if (!this.signingKey)
|
|
170
|
-
return false;
|
|
171
|
-
const addrs = dialableAddrs(this.selfPeerId, this.deps.libp2p.getMultiaddrs(), []);
|
|
172
|
-
const addrKey = [...addrs].sort().join('\n');
|
|
173
|
-
if (addrKey === this.ownAddrKey)
|
|
174
|
-
return false;
|
|
175
|
-
// Strictly increasing past every statement this node is known to have made — the
|
|
176
|
-
// one in memory, and on a fresh driver the copy the store kept from the previous
|
|
177
|
-
// run — so the receiver's "greater issuedAt wins" always prefers the later one.
|
|
178
|
-
// The stored floor is what survives a restart on a clock that went backwards:
|
|
179
|
-
// without it every peer would keep the old statement until the clock passed it.
|
|
180
|
-
const issuedAt = Math.max(this.now(), this.lastOwnIssuedAt() + 1);
|
|
181
|
-
const entry = await signStrandPeerEntry(this.signingKey, this.deps.strandId, addrs, issuedAt);
|
|
182
|
-
if (this.stopped)
|
|
183
|
-
return false;
|
|
184
|
-
this.own = entry;
|
|
185
|
-
this.ownAddrKey = addrKey;
|
|
186
|
-
this.remember(entry, this.now());
|
|
187
|
-
log('[%s] own entry signed (%s): %d addr(s), issuedAt=%d', this.deps.strandId, reason, addrs.length, issuedAt);
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
/** The greatest `issuedAt` this node has signed: in memory, else the stored own entry's, else 0. */
|
|
191
|
-
lastOwnIssuedAt() {
|
|
192
|
-
if (this.own)
|
|
193
|
-
return this.own.issuedAt;
|
|
194
|
-
return this.deps.store.entries(this.deps.strandId).find((held) => held.peerId === this.selfPeerId)?.issuedAt ?? 0;
|
|
195
|
-
}
|
|
196
|
-
/** Exchange with every connected, identified strand peer that speaks the swap. */
|
|
197
|
-
async exchangeWithConnected(bypassThrottle, reason) {
|
|
198
|
-
const peers = await connectedIdentifiedPeers(this.deps.libp2p, this.deps.strandId);
|
|
199
|
-
await Promise.all(peers
|
|
200
|
-
.filter(({ peerId, protocols }) => this.isSwapPeer(peerId, protocols))
|
|
201
|
-
.map(({ peerId, connections }) => this.exchangeWith(peerId, connections[0], bypassThrottle, reason)));
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* One exchange with one peer over `connection`, throttled unless told otherwise (a
|
|
205
|
-
* re-sign) — in which case an exchange already in flight, which carries the old
|
|
206
|
-
* entry, is followed by one more once it settles. Never throws.
|
|
207
|
-
*/
|
|
208
|
-
async exchangeWith(peerId, connection, bypassThrottle, reason) {
|
|
209
|
-
if (this.stopped)
|
|
210
|
-
return;
|
|
211
|
-
const id = peerId.toString();
|
|
212
|
-
if (this.inFlight.has(id)) {
|
|
213
|
-
if (bypassThrottle)
|
|
214
|
-
this.resendAfter.add(id);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const now = this.now();
|
|
218
|
-
const last = this.exchangedAt.get(id);
|
|
219
|
-
if (!bypassThrottle && last !== undefined && now - last < this.throttleMs)
|
|
220
|
-
return;
|
|
221
|
-
this.exchangedAt.set(id, now);
|
|
222
|
-
this.inFlight.add(id);
|
|
223
|
-
try {
|
|
224
|
-
const request = this.frameFor(id);
|
|
225
|
-
const received = await exchangeStrandPeerBook(connection, request, {
|
|
226
|
-
selfPeerId: this.selfPeerId,
|
|
227
|
-
timeoutMs: this.timeoutMs,
|
|
228
|
-
now: this.now
|
|
229
|
-
});
|
|
230
|
-
this.accept(received, id);
|
|
231
|
-
log('[%s] swapped with %s (%s): sent %d, received %d', this.deps.strandId, id, reason, request.entries.length, received.length);
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
log('[%s] swap with %s (%s) failed — next try after the throttle: %o', this.deps.strandId, id, reason, error);
|
|
235
|
-
}
|
|
236
|
-
finally {
|
|
237
|
-
this.inFlight.delete(id);
|
|
238
|
-
}
|
|
239
|
-
if (this.resendAfter.delete(id)) {
|
|
240
|
-
await this.exchangeWith(peerId, connection, true, `${reason}, re-signed meanwhile`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* What this node sends `remotePeerId`: its own entry first, then every signed entry
|
|
245
|
-
* held for anyone else, freshest first, minus the recipient's own — trimmed from the
|
|
246
|
-
* stale end to the frame's entry and byte caps (the own entry can push the store's
|
|
247
|
-
* cap over by one, and long addresses can push the bytes over).
|
|
248
|
-
*/
|
|
249
|
-
frameFor(remotePeerId) {
|
|
250
|
-
const entries = this.own ? [this.own] : [];
|
|
251
|
-
for (const held of this.deps.store.entries(this.deps.strandId)) {
|
|
252
|
-
if (held.sig === undefined || held.peerId === this.selfPeerId || held.peerId === remotePeerId)
|
|
253
|
-
continue;
|
|
254
|
-
entries.push({ peerId: held.peerId, addrs: held.addrs, issuedAt: held.issuedAt, sig: held.sig });
|
|
255
|
-
}
|
|
256
|
-
return trimBookFrameToFit({ strandId: this.deps.strandId, entries });
|
|
257
|
-
}
|
|
258
|
-
/** File verified entries: the sender's own as seen now, a forwarded third party's as never seen. */
|
|
259
|
-
accept(entries, fromPeerId) {
|
|
260
|
-
const now = this.now();
|
|
261
|
-
for (const entry of entries) {
|
|
262
|
-
this.remember(entry, entry.peerId === fromPeerId ? now : 0);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
/** Fire-and-log, like every book write: visible synchronously, the promise tracks durability. */
|
|
266
|
-
remember(entry, lastSeenAt) {
|
|
267
|
-
void this.deps.store.merge(this.deps.strandId, { ...entry, lastSeenAt }).catch((error) => {
|
|
268
|
-
log('[%s] persisting entry for %s failed (continuing): %o', this.deps.strandId, entry.peerId, error);
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
/** The key, if it is Ed25519 and actually the node's own; else `undefined`, logged. */
|
|
273
|
-
function usableSigningKey(privateKey, selfPeerId, strandId) {
|
|
274
|
-
if (!privateKey) {
|
|
275
|
-
log('[%s] no strand transport key — no own entry will be signed', strandId);
|
|
276
|
-
return undefined;
|
|
277
|
-
}
|
|
278
|
-
if (privateKey.type !== 'Ed25519' || privateKey.publicKey.toString() !== selfPeerId) {
|
|
279
|
-
log('[%s] the supplied key is not this node\'s Ed25519 transport key — no own entry will be signed', strandId);
|
|
280
|
-
return undefined;
|
|
281
|
-
}
|
|
282
|
-
return privateKey;
|
|
283
|
-
}
|
|
284
|
-
//# sourceMappingURL=strand-peer-book-swap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"strand-peer-book-swap.js","sourceRoot":"","sources":["../src/strand-peer-book-swap.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { type DurableSlot } from './node-local-snapshot.js';
|
|
2
|
-
/**
|
|
3
|
-
* Cap on the peers remembered per strand. When full, the entry with the smallest
|
|
4
|
-
* `max(issuedAt, lastSeenAt)` is evicted. Sixteen is generous for the parties a
|
|
5
|
-
* strand has today (two, occasionally a few), and it bounds the worst case of a
|
|
6
|
-
* launch dialing dead entries: a failed relayed dial costs up to 14 s at the
|
|
7
|
-
* declared link, so the cap — with aging — is what keeps a stale book from
|
|
8
|
-
* stalling bring-up for minutes.
|
|
9
|
-
*/
|
|
10
|
-
export declare const MAX_STRAND_PEERS = 16;
|
|
11
|
-
/**
|
|
12
|
-
* Default age after which an entry nobody has refreshed is dropped: 14 days from
|
|
13
|
-
* `max(issuedAt, lastSeenAt)`. A live peer refreshes its entry on every connection,
|
|
14
|
-
* so only a peer this node has not reached in two weeks ages out. Overridable per
|
|
15
|
-
* store via {@link StrandPeerBookOptions.maxAgeMs}.
|
|
16
|
-
*/
|
|
17
|
-
export declare const STRAND_PEER_MAX_AGE_MS: number;
|
|
18
|
-
/**
|
|
19
|
-
* One strand peer this node knows an address for. `sig` is set only on an entry the
|
|
20
|
-
* peer signed itself and the book swap carried here (`strand-peer-book-protocol.ts`),
|
|
21
|
-
* or on this node's own entry, which the swap files under the node's own strand
|
|
22
|
-
* transport id; a formation-carried or observed entry is unsigned and local-only.
|
|
23
|
-
*/
|
|
24
|
-
export interface StrandPeerEntry {
|
|
25
|
-
/** The peer's STRAND transport peer id (never a control peer id). */
|
|
26
|
-
peerId: string;
|
|
27
|
-
/**
|
|
28
|
-
* Multiaddr strings, each bound to `peerId` (trailing `/p2p/<peerId>`, a relay
|
|
29
|
-
* hop only with that destination behind it), signaling-first, at most
|
|
30
|
-
* `MAX_STRAND_ADDRS`. Anything that does not attribute to `peerId` is dropped
|
|
31
|
-
* at merge.
|
|
32
|
-
*/
|
|
33
|
-
addrs: string[];
|
|
34
|
-
/**
|
|
35
|
-
* Signer's clock, ms: when the peer itself issued these addresses. 0 for an
|
|
36
|
-
* entry this node observed or was handed unsigned.
|
|
37
|
-
*/
|
|
38
|
-
issuedAt: number;
|
|
39
|
-
/**
|
|
40
|
-
* base64url Ed25519 signature by the peer's strand transport key over the canonical
|
|
41
|
-
* JSON of `{ v, strandId, peerId, addrs, issuedAt }` (`signedStrandPeerEntryPayload`);
|
|
42
|
-
* absent = local-only, never forwarded.
|
|
43
|
-
*/
|
|
44
|
-
sig?: string;
|
|
45
|
-
/**
|
|
46
|
-
* This node's clock, ms: when it last held a connection to the peer — or, for a
|
|
47
|
-
* formation-carried entry, when the responder disclosed the addresses live — and 0
|
|
48
|
-
* for an entry it has never connected to. Local, never on the wire.
|
|
49
|
-
*/
|
|
50
|
-
lastSeenAt: number;
|
|
51
|
-
}
|
|
52
|
-
export interface StrandPeerBookStore {
|
|
53
|
-
/** Party this store is scoped to. */
|
|
54
|
-
readonly partyId: string;
|
|
55
|
-
/**
|
|
56
|
-
* Snapshot of one strand's LIVE entries, freshest first
|
|
57
|
-
* (`max(issuedAt, lastSeenAt)` descending). Empty for an unknown strand. Aged-out
|
|
58
|
-
* entries are never returned. Every backend copies the entries per call, so the
|
|
59
|
-
* result is decoupled from later {@link merge} / {@link forget} calls.
|
|
60
|
-
*/
|
|
61
|
-
entries(strandId: string): StrandPeerEntry[];
|
|
62
|
-
/**
|
|
63
|
-
* Insert or replace by `(strandId, entry.peerId)` under the merge rule
|
|
64
|
-
* ({@link mergeStrandPeerEntry}): a signed entry never yields to an unsigned
|
|
65
|
-
* one; between two signed the greater `issuedAt` wins; between two unsigned the
|
|
66
|
-
* greater `lastSeenAt` wins; `lastSeenAt` is always the max of old and new. Then
|
|
67
|
-
* the strand's aged-out entries are dropped and, past {@link MAX_STRAND_PEERS},
|
|
68
|
-
* the stalest evicted.
|
|
69
|
-
*
|
|
70
|
-
* Implementations MUST reflect the merge in {@link entries} SYNCHRONOUSLY; the
|
|
71
|
-
* returned promise tracks durability only. An entry whose peer id does not parse
|
|
72
|
-
* is logged and dropped rather than thrown — every writer is a best-effort event
|
|
73
|
-
* handler.
|
|
74
|
-
*/
|
|
75
|
-
merge(strandId: string, entry: StrandPeerEntry): Promise<void>;
|
|
76
|
-
/**
|
|
77
|
-
* Drop one peer, or the whole strand when `peerId` is omitted — for a strand this
|
|
78
|
-
* node unpublished or left, whose peers must not stay dial targets. Forgetting
|
|
79
|
-
* something absent is a no-op. Same contract as {@link merge}: reflected
|
|
80
|
-
* synchronously, the promise tracks durability.
|
|
81
|
-
*/
|
|
82
|
-
forget(strandId: string, peerId?: string): Promise<void>;
|
|
83
|
-
}
|
|
84
|
-
/** Tuning shared by every backend. */
|
|
85
|
-
export interface StrandPeerBookOptions {
|
|
86
|
-
/** Age after which an unrefreshed entry is dropped. Default {@link STRAND_PEER_MAX_AGE_MS}. */
|
|
87
|
-
maxAgeMs?: number;
|
|
88
|
-
/** Clock, for tests. Default `Date.now`. */
|
|
89
|
-
now?: () => number;
|
|
90
|
-
}
|
|
91
|
-
/** The moment an entry was last vouched for, by whichever clock spoke last. */
|
|
92
|
-
export declare function strandPeerFreshness(entry: StrandPeerEntry): number;
|
|
93
|
-
/**
|
|
94
|
-
* The merge rule, in one place. Returns the entry to hold for `incoming.peerId`
|
|
95
|
-
* given what is already held — always a NEW object, so a held entry is never mutated
|
|
96
|
-
* in place (the persistent backend's snapshot `put` replaces rather than patches).
|
|
97
|
-
* `incoming` is assumed sanitized ({@link sanitizeStrandPeerEntry}).
|
|
98
|
-
*
|
|
99
|
-
* - A signed entry never yields to an unsigned one for the same peer: the peer's own
|
|
100
|
-
* statement outranks this node's observation of it.
|
|
101
|
-
* - Between two signed entries the greater `issuedAt` wins; between two unsigned
|
|
102
|
-
* entries the greater `lastSeenAt` wins. A tie goes to `incoming`.
|
|
103
|
-
* - `lastSeenAt` is always the max of old and new whichever entry wins, because it
|
|
104
|
-
* is this node's own observation and no remote statement can lower it.
|
|
105
|
-
*/
|
|
106
|
-
export declare function mergeStrandPeerEntry(existing: StrandPeerEntry | undefined, incoming: StrandPeerEntry): StrandPeerEntry;
|
|
107
|
-
/**
|
|
108
|
-
* Shape an entry for storage: the peer id must parse (else `undefined` — not a dial
|
|
109
|
-
* target at all), and `addrs` is reduced to the entries that attribute to `peerId`
|
|
110
|
-
* under `groupAddrsByPeerId`'s rule, de-duplicated, capped at `MAX_STRAND_ADDRS`, and
|
|
111
|
-
* — for an UNSIGNED entry only — reordered signaling-first. A signed entry keeps the
|
|
112
|
-
* signer's order: its signature covers the address list as signed, and the swap
|
|
113
|
-
* forwards the stored copy, so any reordering here would make every forwarded entry
|
|
114
|
-
* fail verification at the next peer (every signer already orders signaling-first). An
|
|
115
|
-
* entry may legitimately end up with NO addresses — a signed "not reachable right
|
|
116
|
-
* now" from the swap protocol is truthful and displaces a stale reachable list — so
|
|
117
|
-
* an empty list is kept, not rejected.
|
|
118
|
-
*/
|
|
119
|
-
export declare function sanitizeStrandPeerEntry(entry: StrandPeerEntry): StrandPeerEntry | undefined;
|
|
120
|
-
/**
|
|
121
|
-
* Ephemeral in-memory book for nodes without durable storage (tests, and the
|
|
122
|
-
* default when nothing is injected). Same contract, no disk: a node using this
|
|
123
|
-
* forgets every strand peer on restart — the #18 shape.
|
|
124
|
-
*/
|
|
125
|
-
export declare class MemoryStrandPeerBookStore implements StrandPeerBookStore {
|
|
126
|
-
readonly partyId: string;
|
|
127
|
-
private readonly strands;
|
|
128
|
-
private readonly options;
|
|
129
|
-
constructor(partyId: string, options?: StrandPeerBookOptions);
|
|
130
|
-
entries(strandId: string): StrandPeerEntry[];
|
|
131
|
-
merge(strandId: string, entry: StrandPeerEntry): Promise<void>;
|
|
132
|
-
forget(strandId: string, peerId?: string): Promise<void>;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Durable {@link StrandPeerBookStore} over an app-supplied {@link DurableSlot} —
|
|
136
|
-
* the cross-platform half of every persistent backend (the Node
|
|
137
|
-
* `FileStrandPeerBookStore` is this class over a file slot).
|
|
138
|
-
*
|
|
139
|
-
* Construct via {@link open}. Load and persist policy — what an absent, corrupt,
|
|
140
|
-
* foreign-party or unreadable slot does (the last one THROWS, as
|
|
141
|
-
* `PersistentBootstrapPeerStore.open` does), and what a failed persist does — is
|
|
142
|
-
* documented once on `NodeLocalSnapshot`; this class only supplies the payload shape
|
|
143
|
-
* and the drop-the-bad-entry policy above. Writes are serialised in-process by the
|
|
144
|
-
* snapshot's write chain, so several strands' identify handlers merging at once
|
|
145
|
-
* cannot interleave partial snapshots.
|
|
146
|
-
*/
|
|
147
|
-
export declare class PersistentStrandPeerBookStore implements StrandPeerBookStore {
|
|
148
|
-
private readonly snapshot;
|
|
149
|
-
private readonly options;
|
|
150
|
-
private constructor();
|
|
151
|
-
/** Load (or cold-start) the party's book from `slot`. */
|
|
152
|
-
static open(slot: DurableSlot, partyId: string, options?: StrandPeerBookOptions): Promise<PersistentStrandPeerBookStore>;
|
|
153
|
-
get partyId(): string;
|
|
154
|
-
entries(strandId: string): StrandPeerEntry[];
|
|
155
|
-
/** Merge, visible via {@link entries} synchronously, then the full snapshot is persisted. */
|
|
156
|
-
merge(strandId: string, entry: StrandPeerEntry): Promise<void>;
|
|
157
|
-
/** Forget, gone from {@link entries} synchronously, then persisted — unless nothing was there. */
|
|
158
|
-
forget(strandId: string, peerId?: string): Promise<void>;
|
|
159
|
-
}
|