@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.
- package/CHANGELOG.md +26 -0
- package/bundle/index.js +614 -25
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js.map +1 -1
- package/dist/message_channel/events.d.ts +27 -3
- package/dist/message_channel/events.js +6 -0
- package/dist/message_channel/events.js.map +1 -1
- package/dist/message_channel/index.d.ts +1 -1
- package/dist/message_channel/message.d.ts +17 -13
- package/dist/message_channel/message.js +19 -11
- package/dist/message_channel/message.js.map +1 -1
- package/dist/message_channel/message_channel.d.ts +31 -3
- package/dist/message_channel/message_channel.js +99 -10
- package/dist/message_channel/message_channel.js.map +1 -1
- package/dist/message_channel/repair/buffers.d.ts +106 -0
- package/dist/message_channel/repair/buffers.js +206 -0
- package/dist/message_channel/repair/buffers.js.map +1 -0
- package/dist/message_channel/repair/repair.d.ts +92 -0
- package/dist/message_channel/repair/repair.js +209 -0
- package/dist/message_channel/repair/repair.js.map +1 -0
- package/dist/message_channel/repair/utils.d.ts +40 -0
- package/dist/message_channel/repair/utils.js +61 -0
- package/dist/message_channel/repair/utils.js.map +1 -0
- package/package.json +93 -1
- package/src/index.ts +7 -1
- package/src/message_channel/events.ts +28 -3
- package/src/message_channel/index.ts +1 -1
- package/src/message_channel/message.ts +24 -13
- package/src/message_channel/message_channel.ts +151 -17
- package/src/message_channel/repair/buffers.ts +277 -0
- package/src/message_channel/repair/repair.ts +331 -0
- 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":"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"}
|
|
@@ -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"}
|