@waku/sds 0.0.8-d637425.0 → 0.0.8

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/bundle/index.js +614 -25
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/index.d.ts +6 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/message_channel/events.d.ts +27 -3
  7. package/dist/message_channel/events.js +6 -0
  8. package/dist/message_channel/events.js.map +1 -1
  9. package/dist/message_channel/index.d.ts +1 -1
  10. package/dist/message_channel/message.d.ts +17 -13
  11. package/dist/message_channel/message.js +19 -11
  12. package/dist/message_channel/message.js.map +1 -1
  13. package/dist/message_channel/message_channel.d.ts +31 -3
  14. package/dist/message_channel/message_channel.js +99 -10
  15. package/dist/message_channel/message_channel.js.map +1 -1
  16. package/dist/message_channel/repair/buffers.d.ts +106 -0
  17. package/dist/message_channel/repair/buffers.js +206 -0
  18. package/dist/message_channel/repair/buffers.js.map +1 -0
  19. package/dist/message_channel/repair/repair.d.ts +92 -0
  20. package/dist/message_channel/repair/repair.js +209 -0
  21. package/dist/message_channel/repair/repair.js.map +1 -0
  22. package/dist/message_channel/repair/utils.d.ts +40 -0
  23. package/dist/message_channel/repair/utils.js +61 -0
  24. package/dist/message_channel/repair/utils.js.map +1 -0
  25. package/package.json +93 -1
  26. package/src/index.ts +7 -1
  27. package/src/message_channel/events.ts +28 -3
  28. package/src/message_channel/index.ts +1 -1
  29. package/src/message_channel/message.ts +24 -13
  30. package/src/message_channel/message_channel.ts +151 -17
  31. package/src/message_channel/repair/buffers.ts +277 -0
  32. package/src/message_channel/repair/repair.ts +331 -0
  33. package/src/message_channel/repair/utils.ts +80 -0
@@ -0,0 +1,206 @@
1
+ import { Logger } from "@waku/utils";
2
+ const log = new Logger("sds:repair:buffers");
3
+ /**
4
+ * Buffer for outgoing repair requests (messages we need)
5
+ * Maintains a sorted array by T_req for efficient retrieval of eligible entries
6
+ */
7
+ export class OutgoingRepairBuffer {
8
+ // Sorted array by T_req (ascending - earliest first)
9
+ items = [];
10
+ maxSize;
11
+ constructor(maxSize = 1000) {
12
+ this.maxSize = maxSize;
13
+ }
14
+ /**
15
+ * Add a missing message to the outgoing repair request buffer
16
+ * If message already exists, it is not updated (keeps original T_req)
17
+ * @returns true if the entry was added, false if it already existed
18
+ */
19
+ add(entry, tReq) {
20
+ const messageId = entry.messageId;
21
+ // Check if already exists - do NOT update T_req per spec
22
+ if (this.has(messageId)) {
23
+ log.info(`Message ${messageId} already in outgoing buffer, keeping original T_req`);
24
+ return false;
25
+ }
26
+ // Check buffer size limit
27
+ if (this.items.length >= this.maxSize) {
28
+ // Evict furthest T_req entry (last in sorted array) to preserve repairs that need to be sent the soonest
29
+ const evicted = this.items.pop();
30
+ log.warn(`Buffer full, evicted furthest entry ${evicted.entry.messageId} with T_req ${evicted.tReq}`);
31
+ }
32
+ // Add new entry and re-sort
33
+ const newEntry = { entry, tReq, requested: false };
34
+ const combined = [...this.items, newEntry];
35
+ // Sort by T_req (ascending)
36
+ combined.sort((a, b) => a.tReq - b.tReq);
37
+ this.items = combined;
38
+ log.info(`Added ${messageId} to outgoing buffer with T_req: ${tReq}`);
39
+ return true;
40
+ }
41
+ /**
42
+ * Remove a message from the buffer (e.g., when received)
43
+ */
44
+ remove(messageId) {
45
+ this.items = this.items.filter((item) => item.entry.messageId !== messageId);
46
+ }
47
+ /**
48
+ * Get eligible repair requests (where T_req <= currentTime)
49
+ * Returns up to maxRequests entries from the front of the sorted array
50
+ * Marks returned entries as requested but keeps them in buffer until received
51
+ */
52
+ getEligible(currentTime = Date.now(), maxRequests = 3) {
53
+ const eligible = [];
54
+ // Iterate from front of sorted array (earliest T_req first)
55
+ for (const item of this.items) {
56
+ // Since array is sorted, once we hit an item with tReq > currentTime,
57
+ // all remaining items also have tReq > currentTime
58
+ if (item.tReq > currentTime) {
59
+ break;
60
+ }
61
+ // Only return items that haven't been requested yet
62
+ if (!item.requested && eligible.length < maxRequests) {
63
+ eligible.push(item.entry);
64
+ // Mark as requested so we don't request it again
65
+ item.requested = true;
66
+ log.info(`Repair request for ${item.entry.messageId} is eligible and marked as requested`);
67
+ }
68
+ // If we've found enough eligible items, exit early
69
+ if (eligible.length >= maxRequests) {
70
+ break;
71
+ }
72
+ }
73
+ return eligible;
74
+ }
75
+ /**
76
+ * Check if a message is in the buffer
77
+ */
78
+ has(messageId) {
79
+ return this.items.some((item) => item.entry.messageId === messageId);
80
+ }
81
+ /**
82
+ * Get the current buffer size
83
+ */
84
+ get size() {
85
+ return this.items.length;
86
+ }
87
+ /**
88
+ * Clear all entries
89
+ */
90
+ clear() {
91
+ this.items = [];
92
+ }
93
+ /**
94
+ * Get all entries (for testing/debugging)
95
+ */
96
+ getAll() {
97
+ return this.items.map((item) => item.entry);
98
+ }
99
+ /**
100
+ * Get items array directly (for testing)
101
+ */
102
+ getItems() {
103
+ return [...this.items];
104
+ }
105
+ }
106
+ /**
107
+ * Buffer for incoming repair requests (repairs we need to send)
108
+ * Maintains a sorted array by T_resp for efficient retrieval of ready entries
109
+ */
110
+ export class IncomingRepairBuffer {
111
+ // Sorted array by T_resp (ascending - earliest first)
112
+ items = [];
113
+ maxSize;
114
+ constructor(maxSize = 1000) {
115
+ this.maxSize = maxSize;
116
+ }
117
+ /**
118
+ * Add a repair request that we can fulfill
119
+ * If message already exists, it is ignored (not updated)
120
+ * @returns true if the entry was added, false if it already existed
121
+ */
122
+ add(entry, tResp) {
123
+ const messageId = entry.messageId;
124
+ // Check if already exists - ignore per spec
125
+ if (this.has(messageId)) {
126
+ log.info(`Message ${messageId} already in incoming buffer, ignoring`);
127
+ return false;
128
+ }
129
+ // Check buffer size limit
130
+ if (this.items.length >= this.maxSize) {
131
+ // Evict furthest T_resp entry (last in sorted array)
132
+ const evicted = this.items.pop();
133
+ log.warn(`Buffer full, evicted furthest entry ${evicted.entry.messageId} with T_resp ${evicted.tResp}`);
134
+ }
135
+ // Add new entry and re-sort
136
+ const newEntry = { entry, tResp };
137
+ const combined = [...this.items, newEntry];
138
+ // Sort by T_resp (ascending)
139
+ combined.sort((a, b) => a.tResp - b.tResp);
140
+ this.items = combined;
141
+ log.info(`Added ${messageId} to incoming buffer with T_resp: ${tResp}`);
142
+ return true;
143
+ }
144
+ /**
145
+ * Remove a message from the buffer
146
+ */
147
+ remove(messageId) {
148
+ this.items = this.items.filter((item) => item.entry.messageId !== messageId);
149
+ }
150
+ /**
151
+ * Get repairs ready to be sent (where T_resp <= currentTime)
152
+ * Removes and returns ready entries
153
+ */
154
+ getReady(currentTime) {
155
+ // Find cutoff point - first item with tResp > currentTime
156
+ // Since array is sorted, all items before this are ready
157
+ let cutoff = 0;
158
+ for (let i = 0; i < this.items.length; i++) {
159
+ if (this.items[i].tResp > currentTime) {
160
+ cutoff = i;
161
+ break;
162
+ }
163
+ // If we reach the end, all items are ready
164
+ cutoff = i + 1;
165
+ }
166
+ // Extract ready items and log them
167
+ const ready = this.items.slice(0, cutoff).map((item) => {
168
+ log.info(`Repair for ${item.entry.messageId} is ready to be sent`);
169
+ return item.entry;
170
+ });
171
+ // Keep only items after cutoff
172
+ this.items = this.items.slice(cutoff);
173
+ return ready;
174
+ }
175
+ /**
176
+ * Check if a message is in the buffer
177
+ */
178
+ has(messageId) {
179
+ return this.items.some((item) => item.entry.messageId === messageId);
180
+ }
181
+ /**
182
+ * Get the current buffer size
183
+ */
184
+ get size() {
185
+ return this.items.length;
186
+ }
187
+ /**
188
+ * Clear all entries
189
+ */
190
+ clear() {
191
+ this.items = [];
192
+ }
193
+ /**
194
+ * Get all entries (for testing/debugging)
195
+ */
196
+ getAll() {
197
+ return this.items.map((item) => item.entry);
198
+ }
199
+ /**
200
+ * Get items array directly (for testing)
201
+ */
202
+ getItems() {
203
+ return [...this.items];
204
+ }
205
+ }
206
+ //# sourceMappingURL=buffers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffers.js","sourceRoot":"","sources":["../../../src/message_channel/repair/buffers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAmB7C;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IAC/B,qDAAqD;IAC7C,KAAK,GAA0B,EAAE,CAAC;IACzB,OAAO,CAAS;IAEjC,YAAmB,OAAO,GAAG,IAAI;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,KAAmB,EAAE,IAAY;QAC1C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAElC,yDAAyD;QACzD,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,IAAI,CACN,WAAW,SAAS,qDAAqD,CAC1E,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,0BAA0B;QAC1B,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACtC,yGAAyG;YACzG,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAG,CAAC;YAClC,GAAG,CAAC,IAAI,CACN,uCAAuC,OAAO,CAAC,KAAK,CAAC,SAAS,eAAe,OAAO,CAAC,IAAI,EAAE,CAC5F,CAAC;QACJ,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAwB,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACxE,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAE3C,4BAA4B;QAC5B,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAEzC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,SAAS,SAAS,mCAAmC,IAAI,EAAE,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,SAAoB;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAC5B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAC7C,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACI,WAAW,CAChB,cAAsB,IAAI,CAAC,GAAG,EAAE,EAChC,WAAW,GAAG,CAAC;QAEf,MAAM,QAAQ,GAAmB,EAAE,CAAC;QAEpC,4DAA4D;QAC5D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,sEAAsE;YACtE,mDAAmD;YACnD,IAAI,IAAI,CAAC,IAAI,GAAG,WAAW,EAAE,CAAC;gBAC5B,MAAM;YACR,CAAC;YAED,oDAAoD;YACpD,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;gBACrD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1B,iDAAiD;gBACjD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBACtB,GAAG,CAAC,IAAI,CACN,sBAAsB,IAAI,CAAC,KAAK,CAAC,SAAS,sCAAsC,CACjF,CAAC;YACJ,CAAC;YAED,mDAAmD;YACnD,IAAI,QAAQ,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;gBACnC,MAAM;YACR,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACI,GAAG,CAAC,SAAoB;QAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;OAEG;IACI,KAAK;QACV,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAED;;OAEG;IACI,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IAC/B,sDAAsD;IAC9C,KAAK,GAA0B,EAAE,CAAC;IACzB,OAAO,CAAS;IAEjC,YAAmB,OAAO,GAAG,IAAI;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,KAAmB,EAAE,KAAa;QAC3C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAElC,4CAA4C;QAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,WAAW,SAAS,uCAAuC,CAAC,CAAC;YACtE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,0BAA0B;QAC1B,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACtC,qDAAqD;YACrD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAG,CAAC;YAClC,GAAG,CAAC,IAAI,CACN,uCAAuC,OAAO,CAAC,KAAK,CAAC,SAAS,gBAAgB,OAAO,CAAC,KAAK,EAAE,CAC9F,CAAC;QACJ,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAwB,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAE3C,6BAA6B;QAC7B,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAE3C,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,SAAS,SAAS,oCAAoC,KAAK,EAAE,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,SAAoB;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAC5B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAC7C,CAAC;IACJ,CAAC;IAED;;;OAGG;IACI,QAAQ,CAAC,WAAmB;QACjC,0DAA0D;QAC1D,yDAAyD;QACzD,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,WAAW,EAAE,CAAC;gBACtC,MAAM,GAAG,CAAC,CAAC;gBACX,MAAM;YACR,CAAC;YACD,2CAA2C;YAC3C,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;QAED,mCAAmC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACrD,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,SAAS,sBAAsB,CAAC,CAAC;YACnE,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,+BAA+B;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,GAAG,CAAC,SAAoB;QAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;OAEG;IACI,KAAK;QACV,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAED;;OAEG;IACI,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,92 @@
1
+ import type { HistoryEntry, MessageId } from "../message.js";
2
+ import { Message } from "../message.js";
3
+ import type { ILocalHistory } from "../message_channel.js";
4
+ import { ParticipantId } from "./utils.js";
5
+ /**
6
+ * Event emitter callback for repair events
7
+ */
8
+ export type RepairEventEmitter = (event: string, detail: unknown) => void;
9
+ /**
10
+ * Configuration for SDS-R repair protocol
11
+ */
12
+ export interface RepairConfig {
13
+ /** Minimum wait time before requesting repair (milliseconds) */
14
+ tMin?: number;
15
+ /** Maximum wait time for repair window (milliseconds) */
16
+ tMax?: number;
17
+ /** Number of response groups for load distribution */
18
+ numResponseGroups?: number;
19
+ /** Maximum buffer size for repair requests */
20
+ bufferSize?: number;
21
+ }
22
+ /**
23
+ * Default configuration values based on spec recommendations
24
+ */
25
+ export declare const DEFAULT_REPAIR_CONFIG: Required<RepairConfig>;
26
+ /**
27
+ * Manager for SDS-R repair protocol
28
+ * Handles repair request/response timing and coordination
29
+ */
30
+ export declare class RepairManager {
31
+ private readonly participantId;
32
+ private readonly config;
33
+ private readonly outgoingBuffer;
34
+ private readonly incomingBuffer;
35
+ private readonly eventEmitter?;
36
+ constructor(participantId: ParticipantId, config?: RepairConfig, eventEmitter?: RepairEventEmitter);
37
+ /**
38
+ * Calculate T_req - when to request repair for a missing message
39
+ * Per spec: T_req = current_time + hash(participant_id, message_id) % (T_max - T_min) + T_min
40
+ */
41
+ calculateTReq(messageId: MessageId, currentTime?: number): number;
42
+ /**
43
+ * Calculate T_resp - when to respond with a repair
44
+ * Per spec: T_resp = current_time + (distance * hash(message_id)) % T_max
45
+ * where distance = participant_id XOR sender_id
46
+ */
47
+ calculateTResp(senderId: ParticipantId, messageId: MessageId, currentTime?: number): number;
48
+ /**
49
+ * Determine if this participant is in the response group for a message
50
+ * Per spec: (hash(participant_id, message_id) % num_response_groups) ==
51
+ * (hash(sender_id, message_id) % num_response_groups)
52
+ */
53
+ isInResponseGroup(senderId: ParticipantId, messageId: MessageId): boolean;
54
+ /**
55
+ * Handle missing dependencies by adding them to outgoing repair buffer
56
+ * Called when causal dependencies are detected as missing
57
+ */
58
+ markDependenciesMissing(missingEntries: HistoryEntry[], currentTime?: number): void;
59
+ /**
60
+ * Handle receipt of a message - remove from repair buffers
61
+ * Called when a message is successfully received
62
+ */
63
+ markMessageReceived(messageId: MessageId): void;
64
+ /**
65
+ * Get repair requests that are eligible to be sent
66
+ * Returns up to maxRequests entries where T_req <= currentTime
67
+ */
68
+ getRepairRequests(maxRequests?: number, currentTime?: number): HistoryEntry[];
69
+ /**
70
+ * Process incoming repair requests from other participants
71
+ * Adds to incoming buffer if we can fulfill and are in response group
72
+ */
73
+ processIncomingRepairRequests(requests: HistoryEntry[], localHistory: ILocalHistory, currentTime?: number): void;
74
+ /**
75
+ * Sweep outgoing buffer for repairs that should be requested
76
+ * Returns entries where T_req <= currentTime
77
+ */
78
+ sweepOutgoingBuffer(maxRequests?: number, currentTime?: number): HistoryEntry[];
79
+ /**
80
+ * Sweep incoming buffer for repairs ready to be sent
81
+ * Returns messages that should be rebroadcast
82
+ */
83
+ sweepIncomingBuffer(localHistory: ILocalHistory, currentTime?: number): Message[];
84
+ /**
85
+ * Clear all buffers
86
+ */
87
+ clear(): void;
88
+ /**
89
+ * Update number of response groups (e.g., when participants change)
90
+ */
91
+ updateResponseGroups(numParticipants: number): void;
92
+ }
@@ -0,0 +1,209 @@
1
+ import { Logger } from "@waku/utils";
2
+ import { IncomingRepairBuffer, OutgoingRepairBuffer } from "./buffers.js";
3
+ import { bigintToNumber, calculateXorDistance, combinedHash, hashString } from "./utils.js";
4
+ const log = new Logger("sds:repair:manager");
5
+ /**
6
+ * Per SDS-R spec: One response group per 128 participants
7
+ */
8
+ const PARTICIPANTS_PER_RESPONSE_GROUP = 128;
9
+ /**
10
+ * Default configuration values based on spec recommendations
11
+ */
12
+ export const DEFAULT_REPAIR_CONFIG = {
13
+ tMin: 30000, // 30 seconds
14
+ tMax: 120000, // 120 seconds
15
+ numResponseGroups: 1, // Recommendation is 1 group per PARTICIPANTS_PER_RESPONSE_GROUP participants
16
+ bufferSize: 1000
17
+ };
18
+ /**
19
+ * Manager for SDS-R repair protocol
20
+ * Handles repair request/response timing and coordination
21
+ */
22
+ export class RepairManager {
23
+ participantId;
24
+ config;
25
+ outgoingBuffer;
26
+ incomingBuffer;
27
+ eventEmitter;
28
+ constructor(participantId, config = {}, eventEmitter) {
29
+ this.participantId = participantId;
30
+ this.config = { ...DEFAULT_REPAIR_CONFIG, ...config };
31
+ this.eventEmitter = eventEmitter;
32
+ this.outgoingBuffer = new OutgoingRepairBuffer(this.config.bufferSize);
33
+ this.incomingBuffer = new IncomingRepairBuffer(this.config.bufferSize);
34
+ log.info(`RepairManager initialized for participant ${participantId}`);
35
+ }
36
+ /**
37
+ * Calculate T_req - when to request repair for a missing message
38
+ * Per spec: T_req = current_time + hash(participant_id, message_id) % (T_max - T_min) + T_min
39
+ */
40
+ calculateTReq(messageId, currentTime = Date.now()) {
41
+ const hash = combinedHash(this.participantId, messageId);
42
+ const range = BigInt(this.config.tMax - this.config.tMin);
43
+ const offset = bigintToNumber(hash % range) + this.config.tMin;
44
+ return currentTime + offset;
45
+ }
46
+ /**
47
+ * Calculate T_resp - when to respond with a repair
48
+ * Per spec: T_resp = current_time + (distance * hash(message_id)) % T_max
49
+ * where distance = participant_id XOR sender_id
50
+ */
51
+ calculateTResp(senderId, messageId, currentTime = Date.now()) {
52
+ const distance = calculateXorDistance(this.participantId, senderId);
53
+ const messageHash = hashString(messageId);
54
+ const product = distance * messageHash;
55
+ const offset = bigintToNumber(product % BigInt(this.config.tMax));
56
+ return currentTime + offset;
57
+ }
58
+ /**
59
+ * Determine if this participant is in the response group for a message
60
+ * Per spec: (hash(participant_id, message_id) % num_response_groups) ==
61
+ * (hash(sender_id, message_id) % num_response_groups)
62
+ */
63
+ isInResponseGroup(senderId, messageId) {
64
+ if (!senderId) {
65
+ // Cannot determine response group without sender_id
66
+ return false;
67
+ }
68
+ const numGroups = BigInt(this.config.numResponseGroups);
69
+ if (numGroups <= BigInt(1)) {
70
+ // Single group, everyone is in it
71
+ return true;
72
+ }
73
+ const participantGroup = combinedHash(this.participantId, messageId) % numGroups;
74
+ const senderGroup = combinedHash(senderId, messageId) % numGroups;
75
+ return participantGroup === senderGroup;
76
+ }
77
+ /**
78
+ * Handle missing dependencies by adding them to outgoing repair buffer
79
+ * Called when causal dependencies are detected as missing
80
+ */
81
+ markDependenciesMissing(missingEntries, currentTime = Date.now()) {
82
+ for (const entry of missingEntries) {
83
+ // Calculate when to request this repair
84
+ const tReq = this.calculateTReq(entry.messageId, currentTime);
85
+ // Add to outgoing buffer - only log and emit event if actually added
86
+ const wasAdded = this.outgoingBuffer.add(entry, tReq);
87
+ if (wasAdded) {
88
+ log.info(`Added missing dependency ${entry.messageId} to repair buffer with T_req=${tReq}`);
89
+ // Emit event
90
+ this.eventEmitter?.("RepairRequestQueued", {
91
+ messageId: entry.messageId,
92
+ tReq
93
+ });
94
+ }
95
+ }
96
+ }
97
+ /**
98
+ * Handle receipt of a message - remove from repair buffers
99
+ * Called when a message is successfully received
100
+ */
101
+ markMessageReceived(messageId) {
102
+ // Remove from both buffers as we no longer need to request or respond
103
+ const wasInOutgoing = this.outgoingBuffer.has(messageId);
104
+ const wasInIncoming = this.incomingBuffer.has(messageId);
105
+ if (wasInOutgoing) {
106
+ this.outgoingBuffer.remove(messageId);
107
+ log.info(`Removed ${messageId} from outgoing repair buffer after receipt`);
108
+ }
109
+ if (wasInIncoming) {
110
+ this.incomingBuffer.remove(messageId);
111
+ log.info(`Removed ${messageId} from incoming repair buffer after receipt`);
112
+ }
113
+ }
114
+ /**
115
+ * Get repair requests that are eligible to be sent
116
+ * Returns up to maxRequests entries where T_req <= currentTime
117
+ */
118
+ getRepairRequests(maxRequests = 3, currentTime = Date.now()) {
119
+ return this.outgoingBuffer.getEligible(currentTime, maxRequests);
120
+ }
121
+ /**
122
+ * Process incoming repair requests from other participants
123
+ * Adds to incoming buffer if we can fulfill and are in response group
124
+ */
125
+ processIncomingRepairRequests(requests, localHistory, currentTime = Date.now()) {
126
+ for (const request of requests) {
127
+ // Remove from our own outgoing buffer (someone else is requesting it)
128
+ this.outgoingBuffer.remove(request.messageId);
129
+ // Check if we have this message
130
+ const message = localHistory.find((m) => m.messageId === request.messageId);
131
+ if (!message) {
132
+ log.info(`Cannot fulfill repair for ${request.messageId} - not in local history`);
133
+ continue;
134
+ }
135
+ // Check if we're in the response group
136
+ if (!request.senderId) {
137
+ log.warn(`Cannot determine response group for ${request.messageId} - missing sender_id`);
138
+ continue;
139
+ }
140
+ if (!this.isInResponseGroup(request.senderId, request.messageId)) {
141
+ log.info(`Not in response group for ${request.messageId}`);
142
+ continue;
143
+ }
144
+ // Calculate when to respond
145
+ const tResp = this.calculateTResp(request.senderId, request.messageId, currentTime);
146
+ // Add to incoming buffer - only log and emit event if actually added
147
+ const wasAdded = this.incomingBuffer.add(request, tResp);
148
+ if (wasAdded) {
149
+ log.info(`Will respond to repair request for ${request.messageId} at T_resp=${tResp}`);
150
+ // Emit event
151
+ this.eventEmitter?.("RepairResponseQueued", {
152
+ messageId: request.messageId,
153
+ tResp
154
+ });
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * Sweep outgoing buffer for repairs that should be requested
160
+ * Returns entries where T_req <= currentTime
161
+ */
162
+ sweepOutgoingBuffer(maxRequests = 3, currentTime = Date.now()) {
163
+ return this.getRepairRequests(maxRequests, currentTime);
164
+ }
165
+ /**
166
+ * Sweep incoming buffer for repairs ready to be sent
167
+ * Returns messages that should be rebroadcast
168
+ */
169
+ sweepIncomingBuffer(localHistory, currentTime = Date.now()) {
170
+ const ready = this.incomingBuffer.getReady(currentTime);
171
+ const messages = [];
172
+ for (const entry of ready) {
173
+ const message = localHistory.find((m) => m.messageId === entry.messageId);
174
+ if (message) {
175
+ messages.push(message);
176
+ log.info(`Sending repair for ${entry.messageId}`);
177
+ }
178
+ else {
179
+ log.warn(`Message ${entry.messageId} no longer in local history`);
180
+ }
181
+ }
182
+ return messages;
183
+ }
184
+ /**
185
+ * Clear all buffers
186
+ */
187
+ clear() {
188
+ this.outgoingBuffer.clear();
189
+ this.incomingBuffer.clear();
190
+ }
191
+ /**
192
+ * Update number of response groups (e.g., when participants change)
193
+ */
194
+ updateResponseGroups(numParticipants) {
195
+ if (numParticipants < 0 ||
196
+ !Number.isFinite(numParticipants) ||
197
+ !Number.isInteger(numParticipants)) {
198
+ throw new Error(`Invalid numParticipants: ${numParticipants}. Must be a positive integer.`);
199
+ }
200
+ if (numParticipants > Number.MAX_SAFE_INTEGER) {
201
+ log.warn(`numParticipants ${numParticipants} exceeds MAX_SAFE_INTEGER, using MAX_SAFE_INTEGER`);
202
+ numParticipants = Number.MAX_SAFE_INTEGER;
203
+ }
204
+ // Per spec: num_response_groups = max(1, num_participants / PARTICIPANTS_PER_RESPONSE_GROUP)
205
+ this.config.numResponseGroups = Math.max(1, Math.floor(numParticipants / PARTICIPANTS_PER_RESPONSE_GROUP));
206
+ log.info(`Updated response groups to ${this.config.numResponseGroups} for ${numParticipants} participants`);
207
+ }
208
+ }
209
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../../../src/message_channel/repair/repair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { MessageId } from "../message.js";
2
+ /**
3
+ * ParticipantId can be a string or converted to a numeric representation for XOR operations
4
+ */
5
+ export type ParticipantId = string;
6
+ /**
7
+ * Compute SHA256 hash and convert to integer for modulo operations
8
+ * Uses first 8 bytes of hash for the integer conversion
9
+ */
10
+ export declare function hashToInteger(input: string): bigint;
11
+ /**
12
+ * Compute combined hash for (participantId, messageId) and convert to integer
13
+ * This is used for T_req calculations and response group membership
14
+ */
15
+ export declare function combinedHash(participantId: ParticipantId, messageId: MessageId): bigint;
16
+ /**
17
+ * Convert ParticipantId to numeric representation for XOR operations
18
+ * TODO: Not per spec, further review needed
19
+ * The spec assumes participant IDs support XOR natively, but we're using
20
+ * SHA256 hash to ensure consistent numeric representation for string IDs
21
+ */
22
+ export declare function participantIdToNumeric(participantId: ParticipantId): bigint;
23
+ /**
24
+ * Calculate XOR distance between two participant IDs
25
+ * Used for T_resp calculations where distance affects response timing
26
+ */
27
+ export declare function calculateXorDistance(participantId1: ParticipantId, participantId2: ParticipantId): bigint;
28
+ /**
29
+ * Helper to convert bigint to number for timing calculations
30
+ * Ensures the result fits in JavaScript's number range
31
+ */
32
+ export declare function bigintToNumber(value: bigint): number;
33
+ /**
34
+ * Calculate hash for a single string (used for message_id in T_resp)
35
+ */
36
+ export declare function hashString(input: string): bigint;
37
+ /**
38
+ * Convert a hash result to hex string for debugging/logging
39
+ */
40
+ export declare function hashToHex(input: string): string;
@@ -0,0 +1,61 @@
1
+ import { sha256 } from "@noble/hashes/sha2";
2
+ import { bytesToHex } from "@waku/utils/bytes";
3
+ /**
4
+ * Compute SHA256 hash and convert to integer for modulo operations
5
+ * Uses first 8 bytes of hash for the integer conversion
6
+ */
7
+ export function hashToInteger(input) {
8
+ const hashBytes = sha256(new TextEncoder().encode(input));
9
+ // Use first 8 bytes for a 64-bit integer
10
+ const view = new DataView(hashBytes.buffer, 0, 8);
11
+ return view.getBigUint64(0, false); // big-endian
12
+ }
13
+ /**
14
+ * Compute combined hash for (participantId, messageId) and convert to integer
15
+ * This is used for T_req calculations and response group membership
16
+ */
17
+ export function combinedHash(participantId, messageId) {
18
+ const combined = `${participantId}${messageId}`;
19
+ return hashToInteger(combined);
20
+ }
21
+ /**
22
+ * Convert ParticipantId to numeric representation for XOR operations
23
+ * TODO: Not per spec, further review needed
24
+ * The spec assumes participant IDs support XOR natively, but we're using
25
+ * SHA256 hash to ensure consistent numeric representation for string IDs
26
+ */
27
+ export function participantIdToNumeric(participantId) {
28
+ return hashToInteger(participantId);
29
+ }
30
+ /**
31
+ * Calculate XOR distance between two participant IDs
32
+ * Used for T_resp calculations where distance affects response timing
33
+ */
34
+ export function calculateXorDistance(participantId1, participantId2) {
35
+ const numeric1 = participantIdToNumeric(participantId1);
36
+ const numeric2 = participantIdToNumeric(participantId2);
37
+ return numeric1 ^ numeric2;
38
+ }
39
+ /**
40
+ * Helper to convert bigint to number for timing calculations
41
+ * Ensures the result fits in JavaScript's number range
42
+ */
43
+ export function bigintToNumber(value) {
44
+ // For timing calculations, we modulo by MAX_SAFE_INTEGER to ensure it fits
45
+ const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
46
+ return Number(value % maxSafe);
47
+ }
48
+ /**
49
+ * Calculate hash for a single string (used for message_id in T_resp)
50
+ */
51
+ export function hashString(input) {
52
+ return hashToInteger(input);
53
+ }
54
+ /**
55
+ * Convert a hash result to hex string for debugging/logging
56
+ */
57
+ export function hashToHex(input) {
58
+ const hashBytes = sha256(new TextEncoder().encode(input));
59
+ return bytesToHex(hashBytes);
60
+ }
61
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/message_channel/repair/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAS/C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,yCAAyC;IACzC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,aAA4B,EAC5B,SAAoB;IAEpB,MAAM,QAAQ,GAAG,GAAG,aAAa,GAAG,SAAS,EAAE,CAAC;IAChD,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,aAA4B;IACjE,OAAO,aAAa,CAAC,aAAa,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,cAA6B,EAC7B,cAA6B;IAE7B,MAAM,QAAQ,GAAG,sBAAsB,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,sBAAsB,CAAC,cAAc,CAAC,CAAC;IACxD,OAAO,QAAQ,GAAG,QAAQ,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,2EAA2E;IAC3E,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AAC/B,CAAC"}