@hashtree/worker 0.1.25 → 0.1.26
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/capabilities/blossomTransport.d.ts +0 -2
- package/dist/capabilities/blossomTransport.d.ts.map +1 -1
- package/dist/capabilities/blossomTransport.js +2 -59
- package/dist/capabilities/blossomTransport.js.map +1 -1
- package/dist/capabilities/meshRouterStore.d.ts +71 -0
- package/dist/capabilities/meshRouterStore.d.ts.map +1 -0
- package/dist/capabilities/meshRouterStore.js +316 -0
- package/dist/capabilities/meshRouterStore.js.map +1 -0
- package/dist/iris/worker.js +32 -83
- package/dist/iris/worker.js.map +1 -1
- package/dist/p2p/boundedQueue.d.ts +5 -0
- package/dist/p2p/boundedQueue.d.ts.map +1 -1
- package/dist/p2p/boundedQueue.js +22 -0
- package/dist/p2p/boundedQueue.js.map +1 -1
- package/dist/p2p/index.d.ts +1 -0
- package/dist/p2p/index.d.ts.map +1 -1
- package/dist/p2p/index.js +1 -0
- package/dist/p2p/index.js.map +1 -1
- package/dist/p2p/meshQueryRouter.d.ts +44 -0
- package/dist/p2p/meshQueryRouter.d.ts.map +1 -0
- package/dist/p2p/meshQueryRouter.js +228 -0
- package/dist/p2p/meshQueryRouter.js.map +1 -0
- package/dist/p2p/uploadRateLimiter.d.ts +21 -0
- package/dist/p2p/uploadRateLimiter.d.ts.map +1 -0
- package/dist/p2p/uploadRateLimiter.js +62 -0
- package/dist/p2p/uploadRateLimiter.js.map +1 -0
- package/dist/p2p/webrtcController.d.ts +3 -5
- package/dist/p2p/webrtcController.d.ts.map +1 -1
- package/dist/p2p/webrtcController.js +34 -108
- package/dist/p2p/webrtcController.js.map +1 -1
- package/dist/p2p/webrtcProxy.d.ts +8 -1
- package/dist/p2p/webrtcProxy.d.ts.map +1 -1
- package/dist/p2p/webrtcProxy.js +64 -8
- package/dist/p2p/webrtcProxy.js.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +38 -27
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { MAX_HTL, createRequest, decrementHTL, encodeRequest, hashToKey, shouldForward, verifyHash, } from '@hashtree/nostr';
|
|
2
|
+
class SlidingWindowRateLimiter {
|
|
3
|
+
maxEvents;
|
|
4
|
+
windowMs;
|
|
5
|
+
eventsByPeer = new Map();
|
|
6
|
+
constructor(maxEvents, windowMs) {
|
|
7
|
+
this.maxEvents = maxEvents;
|
|
8
|
+
this.windowMs = windowMs;
|
|
9
|
+
}
|
|
10
|
+
allow(peerId) {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
const events = this.eventsByPeer.get(peerId) ?? [];
|
|
13
|
+
let firstActiveIndex = 0;
|
|
14
|
+
while (firstActiveIndex < events.length && now - events[firstActiveIndex] >= this.windowMs) {
|
|
15
|
+
firstActiveIndex += 1;
|
|
16
|
+
}
|
|
17
|
+
if (firstActiveIndex > 0) {
|
|
18
|
+
events.splice(0, firstActiveIndex);
|
|
19
|
+
}
|
|
20
|
+
if (events.length >= this.maxEvents) {
|
|
21
|
+
this.eventsByPeer.set(peerId, events);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
events.push(now);
|
|
25
|
+
this.eventsByPeer.set(peerId, events);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
resetPeer(peerId) {
|
|
29
|
+
this.eventsByPeer.delete(peerId);
|
|
30
|
+
}
|
|
31
|
+
clear() {
|
|
32
|
+
this.eventsByPeer.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class MeshQueryRouter {
|
|
36
|
+
localStore;
|
|
37
|
+
requestTimeoutMs;
|
|
38
|
+
rateLimiter;
|
|
39
|
+
peers = new Map();
|
|
40
|
+
hashesByRequester = new Map();
|
|
41
|
+
inFlightByHash = new Map();
|
|
42
|
+
pendingUpstreamFetches = new Map();
|
|
43
|
+
upstreamFetch;
|
|
44
|
+
constructor(config) {
|
|
45
|
+
this.localStore = config.localStore;
|
|
46
|
+
this.requestTimeoutMs = config.requestTimeoutMs;
|
|
47
|
+
this.upstreamFetch = config.upstreamFetch;
|
|
48
|
+
this.rateLimiter = new SlidingWindowRateLimiter(config.maxForwardsPerPeerWindow ?? 64, config.forwardRateLimitWindowMs ?? 1000);
|
|
49
|
+
}
|
|
50
|
+
registerPeer(peer) {
|
|
51
|
+
this.peers.set(peer.peerId, peer);
|
|
52
|
+
}
|
|
53
|
+
removePeer(peerId) {
|
|
54
|
+
const hashes = this.hashesByRequester.get(peerId);
|
|
55
|
+
if (hashes) {
|
|
56
|
+
for (const hashKey of Array.from(hashes)) {
|
|
57
|
+
const inFlight = this.inFlightByHash.get(hashKey);
|
|
58
|
+
if (!inFlight)
|
|
59
|
+
continue;
|
|
60
|
+
inFlight.requesterIds.delete(peerId);
|
|
61
|
+
if (inFlight.requesterIds.size === 0) {
|
|
62
|
+
this.clearQuery(hashKey);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
this.hashesByRequester.delete(peerId);
|
|
67
|
+
this.peers.delete(peerId);
|
|
68
|
+
this.rateLimiter.resetPeer(peerId);
|
|
69
|
+
}
|
|
70
|
+
setUpstreamFetch(upstreamFetch) {
|
|
71
|
+
this.upstreamFetch = upstreamFetch;
|
|
72
|
+
}
|
|
73
|
+
hasInFlight(hashKey) {
|
|
74
|
+
return this.inFlightByHash.has(hashKey);
|
|
75
|
+
}
|
|
76
|
+
stop() {
|
|
77
|
+
for (const hashKey of Array.from(this.inFlightByHash.keys())) {
|
|
78
|
+
this.clearQuery(hashKey);
|
|
79
|
+
}
|
|
80
|
+
this.hashesByRequester.clear();
|
|
81
|
+
this.pendingUpstreamFetches.clear();
|
|
82
|
+
this.rateLimiter.clear();
|
|
83
|
+
}
|
|
84
|
+
async handleRequest(requesterId, req) {
|
|
85
|
+
const hashKey = hashToKey(req.h);
|
|
86
|
+
const requester = this.peers.get(requesterId);
|
|
87
|
+
if (!requester) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const local = await this.localStore.get(req.h);
|
|
91
|
+
if (local) {
|
|
92
|
+
await requester.sendResponse(req.h, local);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const begin = this.beginQuery(hashKey, requesterId);
|
|
96
|
+
if (begin === 'suppressed') {
|
|
97
|
+
requester.onForwardedSuppressed?.();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (begin === 'rate_limited') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const upstreamActive = this.startUpstreamFetch(hashKey, req.h);
|
|
104
|
+
const forwarded = this.forwardRequest(requesterId, req.h, req.htl ?? MAX_HTL);
|
|
105
|
+
if (forwarded > 0) {
|
|
106
|
+
requester.onForwardedRequest?.();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!upstreamActive) {
|
|
110
|
+
this.clearQuery(hashKey);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async resolve(hash, data) {
|
|
114
|
+
const hashKey = hashToKey(hash);
|
|
115
|
+
const requesterIds = this.clearQuery(hashKey);
|
|
116
|
+
if (requesterIds.length === 0) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
await this.localStore.put(hash, data).catch(() => false);
|
|
120
|
+
for (const requesterId of requesterIds) {
|
|
121
|
+
const requester = this.peers.get(requesterId);
|
|
122
|
+
if (!requester) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
requester.onForwardedResolved?.();
|
|
126
|
+
await requester.sendResponse(hash, data);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
beginQuery(hashKey, requesterId) {
|
|
130
|
+
const existing = this.inFlightByHash.get(hashKey);
|
|
131
|
+
if (existing) {
|
|
132
|
+
this.trackRequester(hashKey, existing.requesterIds, requesterId);
|
|
133
|
+
return 'suppressed';
|
|
134
|
+
}
|
|
135
|
+
if (!this.rateLimiter.allow(requesterId)) {
|
|
136
|
+
return 'rate_limited';
|
|
137
|
+
}
|
|
138
|
+
const requesterIds = new Set();
|
|
139
|
+
this.trackRequester(hashKey, requesterIds, requesterId);
|
|
140
|
+
const timeoutId = setTimeout(() => {
|
|
141
|
+
this.clearQuery(hashKey);
|
|
142
|
+
}, this.requestTimeoutMs);
|
|
143
|
+
this.inFlightByHash.set(hashKey, { requesterIds, timeoutId });
|
|
144
|
+
return 'new';
|
|
145
|
+
}
|
|
146
|
+
clearQuery(hashKey) {
|
|
147
|
+
const inFlight = this.inFlightByHash.get(hashKey);
|
|
148
|
+
if (!inFlight) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
clearTimeout(inFlight.timeoutId);
|
|
152
|
+
this.inFlightByHash.delete(hashKey);
|
|
153
|
+
const requesterIds = Array.from(inFlight.requesterIds);
|
|
154
|
+
for (const requesterId of requesterIds) {
|
|
155
|
+
const hashes = this.hashesByRequester.get(requesterId);
|
|
156
|
+
if (!hashes)
|
|
157
|
+
continue;
|
|
158
|
+
hashes.delete(hashKey);
|
|
159
|
+
if (hashes.size === 0) {
|
|
160
|
+
this.hashesByRequester.delete(requesterId);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return requesterIds;
|
|
164
|
+
}
|
|
165
|
+
trackRequester(hashKey, requesterIds, requesterId) {
|
|
166
|
+
requesterIds.add(requesterId);
|
|
167
|
+
let hashes = this.hashesByRequester.get(requesterId);
|
|
168
|
+
if (!hashes) {
|
|
169
|
+
hashes = new Set();
|
|
170
|
+
this.hashesByRequester.set(requesterId, hashes);
|
|
171
|
+
}
|
|
172
|
+
hashes.add(hashKey);
|
|
173
|
+
}
|
|
174
|
+
forwardRequest(requesterId, hash, htl) {
|
|
175
|
+
if (!shouldForward(htl)) {
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
const requester = this.peers.get(requesterId);
|
|
179
|
+
if (!requester) {
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
const nextHtl = decrementHTL(htl, requester.getHtlConfig());
|
|
183
|
+
let forwarded = 0;
|
|
184
|
+
for (const peer of this.peers.values()) {
|
|
185
|
+
if (peer.peerId === requesterId || !peer.canSend()) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (peer.sendRequest(hash, nextHtl)) {
|
|
189
|
+
forwarded += 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return forwarded;
|
|
193
|
+
}
|
|
194
|
+
startUpstreamFetch(hashKey, hash) {
|
|
195
|
+
if (!this.upstreamFetch) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const existing = this.pendingUpstreamFetches.get(hashKey);
|
|
199
|
+
if (existing) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
let pending;
|
|
203
|
+
pending = this.upstreamFetch(hash)
|
|
204
|
+
.then(async (data) => {
|
|
205
|
+
if (!data) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const valid = await verifyHash(data, hash);
|
|
209
|
+
if (!valid) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
await this.resolve(hash, data);
|
|
213
|
+
return data;
|
|
214
|
+
})
|
|
215
|
+
.catch(() => null)
|
|
216
|
+
.finally(() => {
|
|
217
|
+
if (this.pendingUpstreamFetches.get(hashKey) === pending) {
|
|
218
|
+
this.pendingUpstreamFetches.delete(hashKey);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
this.pendingUpstreamFetches.set(hashKey, pending);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
export function encodeForwardRequest(hash, htl) {
|
|
226
|
+
return new Uint8Array(encodeRequest(createRequest(hash, htl)));
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=meshQueryRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meshQueryRouter.js","sourceRoot":"","sources":["../../src/p2p/meshQueryRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type UploadRateLimiterConfig = {
|
|
2
|
+
bytesPerSecond?: number | null;
|
|
3
|
+
now?: () => number;
|
|
4
|
+
};
|
|
5
|
+
type UploadReservation = {
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
delayMs: number;
|
|
8
|
+
};
|
|
9
|
+
export declare class UploadRateLimiter {
|
|
10
|
+
private bytesPerSecond;
|
|
11
|
+
private availableBytes;
|
|
12
|
+
private lastRefillMs;
|
|
13
|
+
private readonly now;
|
|
14
|
+
constructor(config?: UploadRateLimiterConfig);
|
|
15
|
+
setBytesPerSecond(bytesPerSecond?: number | null): void;
|
|
16
|
+
getBytesPerSecond(): number | null;
|
|
17
|
+
reserve(byteLength: number): UploadReservation;
|
|
18
|
+
private refill;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=uploadRateLimiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadRateLimiter.d.ts","sourceRoot":"","sources":["../../src/p2p/uploadRateLimiter.ts"],"names":[],"mappings":"AAAA,KAAK,uBAAuB,GAAG;IAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AASF,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,MAAM,GAAE,uBAA4B;IAOhD,iBAAiB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAUvD,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAIlC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB;IAyB9C,OAAO,CAAC,MAAM;CAef"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function normalizeBytesPerSecond(value) {
|
|
2
|
+
if (!Number.isFinite(value) || !value || value <= 0) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
return Math.floor(value);
|
|
6
|
+
}
|
|
7
|
+
export class UploadRateLimiter {
|
|
8
|
+
bytesPerSecond;
|
|
9
|
+
availableBytes;
|
|
10
|
+
lastRefillMs;
|
|
11
|
+
now;
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
this.now = config.now ?? (() => performance.now());
|
|
14
|
+
this.bytesPerSecond = normalizeBytesPerSecond(config.bytesPerSecond);
|
|
15
|
+
this.availableBytes = this.bytesPerSecond ?? Number.POSITIVE_INFINITY;
|
|
16
|
+
this.lastRefillMs = this.now();
|
|
17
|
+
}
|
|
18
|
+
setBytesPerSecond(bytesPerSecond) {
|
|
19
|
+
const nowMs = this.now();
|
|
20
|
+
this.refill(nowMs);
|
|
21
|
+
this.bytesPerSecond = normalizeBytesPerSecond(bytesPerSecond);
|
|
22
|
+
this.availableBytes = this.bytesPerSecond
|
|
23
|
+
? Math.min(this.availableBytes, this.bytesPerSecond)
|
|
24
|
+
: Number.POSITIVE_INFINITY;
|
|
25
|
+
this.lastRefillMs = nowMs;
|
|
26
|
+
}
|
|
27
|
+
getBytesPerSecond() {
|
|
28
|
+
return this.bytesPerSecond;
|
|
29
|
+
}
|
|
30
|
+
reserve(byteLength) {
|
|
31
|
+
if (byteLength <= 0) {
|
|
32
|
+
return { allowed: true, delayMs: 0 };
|
|
33
|
+
}
|
|
34
|
+
const limit = this.bytesPerSecond;
|
|
35
|
+
if (!limit) {
|
|
36
|
+
return { allowed: true, delayMs: 0 };
|
|
37
|
+
}
|
|
38
|
+
const nowMs = this.now();
|
|
39
|
+
this.refill(nowMs);
|
|
40
|
+
if (this.availableBytes >= byteLength) {
|
|
41
|
+
this.availableBytes = Math.max(0, this.availableBytes - byteLength);
|
|
42
|
+
return { allowed: true, delayMs: 0 };
|
|
43
|
+
}
|
|
44
|
+
const missingBytes = byteLength - this.availableBytes;
|
|
45
|
+
return {
|
|
46
|
+
allowed: false,
|
|
47
|
+
delayMs: Math.max(4, Math.ceil((missingBytes / limit) * 1000)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
refill(nowMs) {
|
|
51
|
+
const limit = this.bytesPerSecond;
|
|
52
|
+
if (!limit) {
|
|
53
|
+
this.availableBytes = Number.POSITIVE_INFINITY;
|
|
54
|
+
this.lastRefillMs = nowMs;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const elapsedMs = Math.max(0, nowMs - this.lastRefillMs);
|
|
58
|
+
this.lastRefillMs = nowMs;
|
|
59
|
+
this.availableBytes = Math.min(limit, this.availableBytes + (elapsedMs * limit) / 1000);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=uploadRateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadRateLimiter.js","sourceRoot":"","sources":["../../src/p2p/uploadRateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,7 @@ export interface WebRTCControllerConfig {
|
|
|
22
22
|
localStore: Store;
|
|
23
23
|
sendCommand: (cmd: WebRTCCommand) => void;
|
|
24
24
|
sendSignaling: (msg: SignalingMessage, recipientPubkey?: string) => Promise<void>;
|
|
25
|
+
upstreamFetch?: (hash: Uint8Array) => Promise<Uint8Array | null>;
|
|
25
26
|
getFollows?: () => Set<string>;
|
|
26
27
|
requestTimeout?: number;
|
|
27
28
|
forwardRateLimit?: {
|
|
@@ -44,7 +45,7 @@ export declare class WebRTCController {
|
|
|
44
45
|
private requestTimeout;
|
|
45
46
|
private debug;
|
|
46
47
|
private recentRequests;
|
|
47
|
-
private
|
|
48
|
+
private readonly meshRouter;
|
|
48
49
|
private readonly peerSelector;
|
|
49
50
|
private routing;
|
|
50
51
|
private poolConfig;
|
|
@@ -117,10 +118,7 @@ export declare class WebRTCController {
|
|
|
117
118
|
private processRequest;
|
|
118
119
|
private handleResponse;
|
|
119
120
|
private sendResponse;
|
|
120
|
-
private
|
|
121
|
-
private forwardRequest;
|
|
122
|
-
private pushToRequesters;
|
|
123
|
-
private clearRequesterMarkers;
|
|
121
|
+
private sendRequestToPeer;
|
|
124
122
|
/**
|
|
125
123
|
* Request data from peers
|
|
126
124
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webrtcController.d.ts","sourceRoot":"","sources":["../../src/p2p/webrtcController.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,
|
|
1
|
+
{"version":3,"file":"webrtcController.d.ts","sourceRoot":"","sources":["../../src/p2p/webrtcController.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* - Data channel I/O
|
|
16
16
|
*/
|
|
17
17
|
import { fromHex, sha256, toHex } from '@hashtree/core';
|
|
18
|
-
import { MAX_HTL, MSG_TYPE_REQUEST, MSG_TYPE_RESPONSE, FRAGMENT_SIZE, PeerId, encodeRequest, encodeResponse, parseMessage, createRequest, createResponse, createFragmentResponse, hashToKey, verifyHash, generatePeerHTLConfig,
|
|
18
|
+
import { MAX_HTL, MSG_TYPE_REQUEST, MSG_TYPE_RESPONSE, FRAGMENT_SIZE, PeerId, encodeRequest, encodeResponse, parseMessage, createRequest, createResponse, createFragmentResponse, hashToKey, verifyHash, generatePeerHTLConfig, PeerSelector, buildHedgedWavePlan, normalizeDispatchConfig, syncSelectorPeers, } from '@hashtree/nostr';
|
|
19
19
|
import { LRUCache } from './lruCache.js';
|
|
20
|
-
import {
|
|
20
|
+
import { MeshQueryRouter, encodeForwardRequest } from './meshQueryRouter.js';
|
|
21
21
|
const PEER_METADATA_POINTER_SLOT_KEY = 'hashtree-webrtc/peer-metadata/latest/v1';
|
|
22
22
|
const DEFAULT_REQUEST_DISPATCH = {
|
|
23
23
|
initialFanout: 2,
|
|
@@ -39,7 +39,7 @@ export class WebRTCController {
|
|
|
39
39
|
requestTimeout;
|
|
40
40
|
debug;
|
|
41
41
|
recentRequests = new LRUCache(1000);
|
|
42
|
-
|
|
42
|
+
meshRouter;
|
|
43
43
|
peerSelector;
|
|
44
44
|
routing;
|
|
45
45
|
// Pool configuration - reasonable defaults, settings sync will override
|
|
@@ -64,13 +64,12 @@ export class WebRTCController {
|
|
|
64
64
|
};
|
|
65
65
|
this.peerSelector = PeerSelector.withStrategy(this.routing.selectionStrategy);
|
|
66
66
|
this.peerSelector.setFairness(this.routing.fairnessEnabled);
|
|
67
|
-
this.
|
|
67
|
+
this.meshRouter = new MeshQueryRouter({
|
|
68
|
+
localStore: this.localStore,
|
|
68
69
|
requestTimeoutMs: this.requestTimeout,
|
|
70
|
+
upstreamFetch: config.upstreamFetch,
|
|
69
71
|
maxForwardsPerPeerWindow: config.forwardRateLimit?.maxForwardsPerPeerWindow,
|
|
70
72
|
forwardRateLimitWindowMs: config.forwardRateLimit?.windowMs,
|
|
71
|
-
onForwardTimeout: ({ hashKey, requesterIds }) => {
|
|
72
|
-
this.clearRequesterMarkers(hashKey, requesterIds);
|
|
73
|
-
},
|
|
74
73
|
});
|
|
75
74
|
// Default classifier: check if pubkey is in follows
|
|
76
75
|
const getFollows = config.getFollows ?? (() => new Set());
|
|
@@ -102,7 +101,7 @@ export class WebRTCController {
|
|
|
102
101
|
for (const peerId of this.peers.keys()) {
|
|
103
102
|
this.closePeer(peerId);
|
|
104
103
|
}
|
|
105
|
-
this.
|
|
104
|
+
this.meshRouter.stop();
|
|
106
105
|
}
|
|
107
106
|
// ============================================================================
|
|
108
107
|
// Signaling
|
|
@@ -302,7 +301,6 @@ export class WebRTCController {
|
|
|
302
301
|
answerCreated: false,
|
|
303
302
|
htlConfig: generatePeerHTLConfig(),
|
|
304
303
|
pendingRequests: new Map(),
|
|
305
|
-
theirRequests: new LRUCache(200),
|
|
306
304
|
stats: {
|
|
307
305
|
requestsSent: 0,
|
|
308
306
|
requestsReceived: 0,
|
|
@@ -320,6 +318,22 @@ export class WebRTCController {
|
|
|
320
318
|
};
|
|
321
319
|
this.peers.set(peerId, peer);
|
|
322
320
|
this.peerSelector.addPeer(peerId);
|
|
321
|
+
this.meshRouter.registerPeer({
|
|
322
|
+
peerId,
|
|
323
|
+
canSend: () => peer.dataChannelReady,
|
|
324
|
+
getHtlConfig: () => peer.htlConfig,
|
|
325
|
+
sendRequest: (hash, htl) => this.sendRequestToPeer(peer, hash, htl),
|
|
326
|
+
sendResponse: async (hash, data) => this.sendResponse(peer, hash, data),
|
|
327
|
+
onForwardedRequest: () => {
|
|
328
|
+
peer.stats.forwardedRequests++;
|
|
329
|
+
},
|
|
330
|
+
onForwardedResolved: () => {
|
|
331
|
+
peer.stats.forwardedResolved++;
|
|
332
|
+
},
|
|
333
|
+
onForwardedSuppressed: () => {
|
|
334
|
+
peer.stats.forwardedSuppressed++;
|
|
335
|
+
},
|
|
336
|
+
});
|
|
323
337
|
this.sendCommand({ type: 'rtc:createPeer', peerId, pubkey });
|
|
324
338
|
return peer;
|
|
325
339
|
}
|
|
@@ -341,7 +355,7 @@ export class WebRTCController {
|
|
|
341
355
|
this.peers.delete(peerId);
|
|
342
356
|
this.pendingRemoteCandidates.delete(peerId);
|
|
343
357
|
this.peerSelector.removePeer(peerId);
|
|
344
|
-
this.
|
|
358
|
+
this.meshRouter.removePeer(peerId);
|
|
345
359
|
this.log(`Closed peer: ${peerId.slice(0, 20)}`);
|
|
346
360
|
}
|
|
347
361
|
// ============================================================================
|
|
@@ -694,53 +708,14 @@ export class WebRTCController {
|
|
|
694
708
|
await this.processRequest(peer, req);
|
|
695
709
|
}
|
|
696
710
|
async processRequest(peer, req) {
|
|
697
|
-
|
|
698
|
-
// Try to get from local store
|
|
699
|
-
const data = await this.localStore.get(req.h);
|
|
700
|
-
if (data) {
|
|
701
|
-
// Send response
|
|
702
|
-
await this.sendResponse(peer, req.h, data);
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
// Track their request for later push
|
|
706
|
-
peer.theirRequests.set(hashKey, {
|
|
707
|
-
hash: req.h,
|
|
708
|
-
requestedAt: Date.now(),
|
|
709
|
-
});
|
|
710
|
-
// Forward if HTL allows
|
|
711
|
-
const htl = req.htl ?? MAX_HTL;
|
|
712
|
-
if (shouldForward(htl)) {
|
|
713
|
-
const newHtl = decrementHTL(htl, peer.htlConfig);
|
|
714
|
-
const decision = this.forwardingMachine.beginForward(hashKey, peer.peerId, this.getForwardTargets(peer.peerId));
|
|
715
|
-
if (decision.kind === 'suppressed') {
|
|
716
|
-
peer.stats.forwardedSuppressed++;
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
if (decision.kind === 'rate_limited') {
|
|
720
|
-
peer.theirRequests.delete(hashKey);
|
|
721
|
-
this.log(`Forward rate-limited for ${peer.peerId.slice(0, 20)} hash ${hashKey.slice(0, 16)}`);
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
if (decision.kind === 'no_targets') {
|
|
725
|
-
peer.theirRequests.delete(hashKey);
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
const forwarded = this.forwardRequest(req.h, decision.targets, newHtl);
|
|
729
|
-
if (forwarded <= 0) {
|
|
730
|
-
const requesterIds = this.forwardingMachine.cancelForward(hashKey);
|
|
731
|
-
this.clearRequesterMarkers(hashKey, requesterIds);
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
peer.stats.forwardedRequests++;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
711
|
+
await this.meshRouter.handleRequest(peer.peerId, req);
|
|
737
712
|
}
|
|
738
713
|
async handleResponse(peer, res) {
|
|
739
714
|
peer.stats.responsesReceived++;
|
|
740
715
|
const hashKey = hashToKey(res.h);
|
|
741
716
|
const pending = peer.pendingRequests.get(hashKey);
|
|
742
717
|
if (!pending) {
|
|
743
|
-
const hasRequesters =
|
|
718
|
+
const hasRequesters = this.meshRouter.hasInFlight(hashKey);
|
|
744
719
|
// Late response: cache if we requested this hash recently
|
|
745
720
|
const requestedAt = this.recentRequests.get(hashKey);
|
|
746
721
|
if (!requestedAt && !hasRequesters)
|
|
@@ -757,8 +732,7 @@ export class WebRTCController {
|
|
|
757
732
|
this.recentRequests.delete(hashKey);
|
|
758
733
|
}
|
|
759
734
|
if (hasRequesters) {
|
|
760
|
-
await this.
|
|
761
|
-
this.forwardingMachine.resolveForward(hashKey);
|
|
735
|
+
await this.meshRouter.resolve(res.h, res.d);
|
|
762
736
|
}
|
|
763
737
|
}
|
|
764
738
|
return;
|
|
@@ -773,9 +747,7 @@ export class WebRTCController {
|
|
|
773
747
|
await this.localStore.put(res.h, res.d);
|
|
774
748
|
this.peerSelector.recordSuccess(peer.peerId, elapsedMs, res.d.length);
|
|
775
749
|
pending.resolve(res.d);
|
|
776
|
-
|
|
777
|
-
await this.pushToRequesters(res.h, res.d, peer.peerId);
|
|
778
|
-
this.forwardingMachine.resolveForward(hashKey);
|
|
750
|
+
await this.meshRouter.resolve(res.h, res.d);
|
|
779
751
|
}
|
|
780
752
|
else {
|
|
781
753
|
this.log(`Hash mismatch from ${peer.peerId}`);
|
|
@@ -805,59 +777,13 @@ export class WebRTCController {
|
|
|
805
777
|
this.sendDataToPeer(peer, encoded);
|
|
806
778
|
}
|
|
807
779
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
if (peerId === excludePeerId)
|
|
812
|
-
continue;
|
|
813
|
-
if (!peer.dataChannelReady)
|
|
814
|
-
continue;
|
|
815
|
-
targets.push(peerId);
|
|
816
|
-
}
|
|
817
|
-
return targets;
|
|
818
|
-
}
|
|
819
|
-
forwardRequest(hash, targetPeerIds, htl) {
|
|
820
|
-
const hashKey = hashToKey(hash);
|
|
821
|
-
let forwarded = 0;
|
|
822
|
-
for (const peerId of targetPeerIds) {
|
|
823
|
-
const peer = this.peers.get(peerId);
|
|
824
|
-
if (!peer || !peer.dataChannelReady)
|
|
825
|
-
continue;
|
|
826
|
-
// Set up pending request so we can process the response
|
|
827
|
-
const timeout = setTimeout(() => {
|
|
828
|
-
peer.pendingRequests.delete(hashKey);
|
|
829
|
-
}, this.requestTimeout);
|
|
830
|
-
peer.pendingRequests.set(hashKey, {
|
|
831
|
-
hash,
|
|
832
|
-
resolve: () => {
|
|
833
|
-
// Response will be pushed to original requester via pushToRequesters
|
|
834
|
-
},
|
|
835
|
-
timeout,
|
|
836
|
-
});
|
|
837
|
-
const req = createRequest(hash, htl);
|
|
838
|
-
const encoded = new Uint8Array(encodeRequest(req));
|
|
839
|
-
this.sendDataToPeer(peer, encoded);
|
|
840
|
-
forwarded++;
|
|
841
|
-
}
|
|
842
|
-
return forwarded;
|
|
843
|
-
}
|
|
844
|
-
async pushToRequesters(hash, data, excludePeerId) {
|
|
845
|
-
const hashKey = hashToKey(hash);
|
|
846
|
-
for (const [peerId, peer] of this.peers) {
|
|
847
|
-
if (peerId === excludePeerId)
|
|
848
|
-
continue;
|
|
849
|
-
const theirReq = peer.theirRequests.get(hashKey);
|
|
850
|
-
if (theirReq) {
|
|
851
|
-
peer.theirRequests.delete(hashKey);
|
|
852
|
-
peer.stats.forwardedResolved++;
|
|
853
|
-
await this.sendResponse(peer, hash, data);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
clearRequesterMarkers(hashKey, requesterIds) {
|
|
858
|
-
for (const requesterId of requesterIds) {
|
|
859
|
-
this.peers.get(requesterId)?.theirRequests.delete(hashKey);
|
|
780
|
+
sendRequestToPeer(peer, hash, htl) {
|
|
781
|
+
if (!peer.dataChannelReady) {
|
|
782
|
+
return false;
|
|
860
783
|
}
|
|
784
|
+
const encoded = encodeForwardRequest(hash, htl);
|
|
785
|
+
this.sendDataToPeer(peer, encoded);
|
|
786
|
+
return true;
|
|
861
787
|
}
|
|
862
788
|
// ============================================================================
|
|
863
789
|
// Public API
|