@waku/sds 0.0.4-d3a80d8.0 → 0.0.4-ef2a162.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/bundle/index.js +1 -6342
- package/dist/.tsbuildinfo +1 -1
- package/dist/{bloom_filter/bloom.js → bloom.js} +2 -2
- package/dist/bloom.js.map +1 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/sds.d.ts +108 -0
- package/dist/{message_channel/message_channel.js → sds.js} +111 -265
- package/dist/sds.js.map +1 -0
- package/package.json +1 -1
- package/src/{bloom_filter/bloom.ts → bloom.ts} +2 -2
- package/src/index.ts +1 -15
- package/src/{message_channel/message_channel.ts → sds.ts} +134 -311
- package/dist/bloom_filter/bloom.js.map +0 -1
- package/dist/message_channel/command_queue.d.ts +0 -29
- package/dist/message_channel/command_queue.js +0 -7
- package/dist/message_channel/command_queue.js.map +0 -1
- package/dist/message_channel/events.d.ts +0 -32
- package/dist/message_channel/events.js +0 -19
- package/dist/message_channel/events.js.map +0 -1
- package/dist/message_channel/index.d.ts +0 -3
- package/dist/message_channel/index.js +0 -4
- package/dist/message_channel/index.js.map +0 -1
- package/dist/message_channel/message_channel.d.ts +0 -142
- package/dist/message_channel/message_channel.js.map +0 -1
- package/src/message_channel/command_queue.ts +0 -33
- package/src/message_channel/events.ts +0 -41
- package/src/message_channel/index.ts +0 -3
- /package/dist/{bloom_filter/bloom.d.ts → bloom.d.ts} +0 -0
@@ -1,18 +1,20 @@
|
|
1
1
|
import { TypedEventEmitter } from "@libp2p/interface";
|
2
2
|
import { sha256 } from "@noble/hashes/sha256";
|
3
3
|
import { bytesToHex } from "@noble/hashes/utils";
|
4
|
-
import {
|
4
|
+
import { proto_sds_message } from "@waku/proto";
|
5
5
|
|
6
|
-
import { DefaultBloomFilter } from "
|
6
|
+
import { DefaultBloomFilter } from "./bloom.js";
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
export enum MessageChannelEvent {
|
9
|
+
MessageDelivered = "messageDelivered"
|
10
|
+
}
|
11
|
+
type MessageChannelEvents = {
|
12
|
+
[MessageChannelEvent.MessageDelivered]: CustomEvent<string>;
|
13
|
+
};
|
14
|
+
|
15
|
+
export type Message = proto_sds_message.SdsMessage;
|
16
|
+
export type HistoryEntry = proto_sds_message.HistoryEntry;
|
17
|
+
export type ChannelId = string;
|
16
18
|
|
17
19
|
export const DEFAULT_BLOOM_FILTER_OPTIONS = {
|
18
20
|
capacity: 10000,
|
@@ -22,46 +24,27 @@ export const DEFAULT_BLOOM_FILTER_OPTIONS = {
|
|
22
24
|
const DEFAULT_CAUSAL_HISTORY_SIZE = 2;
|
23
25
|
const DEFAULT_RECEIVED_MESSAGE_TIMEOUT = 1000 * 60 * 5; // 5 minutes
|
24
26
|
|
25
|
-
const log = new Logger("sds:message-channel");
|
26
|
-
|
27
27
|
interface MessageChannelOptions {
|
28
28
|
causalHistorySize?: number;
|
29
29
|
receivedMessageTimeoutEnabled?: boolean;
|
30
30
|
receivedMessageTimeout?: number;
|
31
|
+
deliveredMessageCallback?: (messageId: string) => void;
|
31
32
|
}
|
32
33
|
|
33
34
|
export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
34
|
-
public readonly channelId: ChannelId;
|
35
35
|
private lamportTimestamp: number;
|
36
36
|
private filter: DefaultBloomFilter;
|
37
37
|
private outgoingBuffer: Message[];
|
38
38
|
private acknowledgements: Map<string, number>;
|
39
39
|
private incomingBuffer: Message[];
|
40
40
|
private localHistory: { timestamp: number; historyEntry: HistoryEntry }[];
|
41
|
+
private channelId: ChannelId;
|
41
42
|
private causalHistorySize: number;
|
42
43
|
private acknowledgementCount: number;
|
43
44
|
private timeReceived: Map<string, number>;
|
44
45
|
private receivedMessageTimeoutEnabled: boolean;
|
45
46
|
private receivedMessageTimeout: number;
|
46
|
-
|
47
|
-
private tasks: Task[] = [];
|
48
|
-
private handlers: Handlers = {
|
49
|
-
[Command.Send]: async (
|
50
|
-
params: ParamsByAction[Command.Send]
|
51
|
-
): Promise<void> => {
|
52
|
-
await this._sendMessage(params.payload, params.callback);
|
53
|
-
},
|
54
|
-
[Command.Receive]: async (
|
55
|
-
params: ParamsByAction[Command.Receive]
|
56
|
-
): Promise<void> => {
|
57
|
-
this._receiveMessage(params.message);
|
58
|
-
},
|
59
|
-
[Command.SendEphemeral]: async (
|
60
|
-
params: ParamsByAction[Command.SendEphemeral]
|
61
|
-
): Promise<void> => {
|
62
|
-
await this._sendEphemeralMessage(params.payload, params.callback);
|
63
|
-
}
|
64
|
-
};
|
47
|
+
private deliveredMessageCallback?: (messageId: string) => void;
|
65
48
|
|
66
49
|
public constructor(
|
67
50
|
channelId: ChannelId,
|
@@ -83,6 +66,7 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
83
66
|
options.receivedMessageTimeoutEnabled ?? false;
|
84
67
|
this.receivedMessageTimeout =
|
85
68
|
options.receivedMessageTimeout ?? DEFAULT_RECEIVED_MESSAGE_TIMEOUT;
|
69
|
+
this.deliveredMessageCallback = options.deliveredMessageCallback;
|
86
70
|
}
|
87
71
|
|
88
72
|
public static getMessageId(payload: Uint8Array): string {
|
@@ -90,59 +74,20 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
90
74
|
}
|
91
75
|
|
92
76
|
/**
|
93
|
-
*
|
94
|
-
*
|
95
|
-
* This method should be called periodically by the library consumer to execute
|
96
|
-
* queued send/receive operations in the correct sequence.
|
97
|
-
*
|
98
|
-
* @example
|
99
|
-
* ```typescript
|
100
|
-
* const channel = new MessageChannel("my-channel");
|
101
|
-
*
|
102
|
-
* // Queue some operations
|
103
|
-
* await channel.sendMessage(payload, callback);
|
104
|
-
* channel.receiveMessage(incomingMessage);
|
105
|
-
*
|
106
|
-
* // Process all queued operations
|
107
|
-
* await channel.processTasks();
|
108
|
-
* ```
|
109
|
-
*
|
110
|
-
* @throws Will emit a 'taskError' event if any task fails, but continues processing remaining tasks
|
111
|
-
*/
|
112
|
-
public async processTasks(): Promise<void> {
|
113
|
-
while (this.tasks.length > 0) {
|
114
|
-
const item = this.tasks.shift();
|
115
|
-
if (!item) {
|
116
|
-
continue;
|
117
|
-
}
|
118
|
-
|
119
|
-
await this.executeTask(item);
|
120
|
-
}
|
121
|
-
}
|
122
|
-
|
123
|
-
/**
|
124
|
-
* Queues a message to be sent on this channel.
|
77
|
+
* Send a message to the SDS channel.
|
125
78
|
*
|
126
|
-
*
|
127
|
-
*
|
128
|
-
*
|
129
|
-
* @param payload - The message content as a byte array
|
130
|
-
* @param callback - Optional callback function called after the message is processed
|
131
|
-
* @returns Promise that resolves when the message is queued (not sent)
|
79
|
+
* Increments the lamport timestamp, constructs a `Message` object
|
80
|
+
* with the given payload, and adds it to the outgoing buffer.
|
132
81
|
*
|
133
|
-
*
|
134
|
-
*
|
135
|
-
*
|
136
|
-
*
|
82
|
+
* If the callback is successful, the message is also added to
|
83
|
+
* the bloom filter and message history. In the context of
|
84
|
+
* Waku, this likely means the message was published via
|
85
|
+
* light push or relay.
|
137
86
|
*
|
138
|
-
*
|
139
|
-
* console.log("Message processed:", processedMessage.messageId);
|
140
|
-
* return { success: true };
|
141
|
-
* });
|
87
|
+
* See https://rfc.vac.dev/vac/raw/sds/#send-message
|
142
88
|
*
|
143
|
-
*
|
144
|
-
*
|
145
|
-
* ```
|
89
|
+
* @param payload - The payload to send.
|
90
|
+
* @param callback - A callback function that returns a boolean indicating whether the message was sent successfully.
|
146
91
|
*/
|
147
92
|
public async sendMessage(
|
148
93
|
payload: Uint8Array,
|
@@ -151,13 +96,36 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
151
96
|
retrievalHint?: Uint8Array;
|
152
97
|
}>
|
153
98
|
): Promise<void> {
|
154
|
-
this.
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
99
|
+
this.lamportTimestamp++;
|
100
|
+
|
101
|
+
const messageId = MessageChannel.getMessageId(payload);
|
102
|
+
|
103
|
+
const message: Message = {
|
104
|
+
messageId,
|
105
|
+
channelId: this.channelId,
|
106
|
+
lamportTimestamp: this.lamportTimestamp,
|
107
|
+
causalHistory: this.localHistory
|
108
|
+
.slice(-this.causalHistorySize)
|
109
|
+
.map(({ historyEntry }) => historyEntry),
|
110
|
+
bloomFilter: this.filter.toBytes(),
|
111
|
+
content: payload
|
112
|
+
};
|
113
|
+
|
114
|
+
this.outgoingBuffer.push(message);
|
115
|
+
|
116
|
+
if (callback) {
|
117
|
+
const { success, retrievalHint } = await callback(message);
|
118
|
+
if (success) {
|
119
|
+
this.filter.insert(messageId);
|
120
|
+
this.localHistory.push({
|
121
|
+
timestamp: this.lamportTimestamp,
|
122
|
+
historyEntry: {
|
123
|
+
messageId,
|
124
|
+
retrievalHint
|
125
|
+
}
|
126
|
+
});
|
159
127
|
}
|
160
|
-
}
|
128
|
+
}
|
161
129
|
}
|
162
130
|
|
163
131
|
/**
|
@@ -173,58 +141,72 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
173
141
|
* @param payload - The payload to send.
|
174
142
|
* @param callback - A callback function that returns a boolean indicating whether the message was sent successfully.
|
175
143
|
*/
|
176
|
-
public
|
144
|
+
public sendEphemeralMessage(
|
177
145
|
payload: Uint8Array,
|
178
|
-
callback?: (message: Message) =>
|
179
|
-
):
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
146
|
+
callback?: (message: Message) => boolean
|
147
|
+
): void {
|
148
|
+
const message: Message = {
|
149
|
+
messageId: MessageChannel.getMessageId(payload),
|
150
|
+
channelId: this.channelId,
|
151
|
+
content: payload,
|
152
|
+
lamportTimestamp: undefined,
|
153
|
+
causalHistory: [],
|
154
|
+
bloomFilter: undefined
|
155
|
+
};
|
188
156
|
|
157
|
+
if (callback) {
|
158
|
+
callback(message);
|
159
|
+
}
|
160
|
+
}
|
189
161
|
/**
|
190
|
-
*
|
162
|
+
* Process a received SDS message for this channel.
|
191
163
|
*
|
192
|
-
*
|
193
|
-
*
|
164
|
+
* Review the acknowledgement status of messages in the outgoing buffer
|
165
|
+
* by inspecting the received message's bloom filter and causal history.
|
166
|
+
* Add the received message to the bloom filter.
|
167
|
+
* If the local history contains every message in the received message's
|
168
|
+
* causal history, deliver the message. Otherwise, add the message to the
|
169
|
+
* incoming buffer.
|
194
170
|
*
|
195
|
-
*
|
171
|
+
* See https://rfc.vac.dev/vac/raw/sds/#receive-message
|
196
172
|
*
|
197
|
-
* @
|
198
|
-
* ```typescript
|
199
|
-
* const channel = new MessageChannel("chat-room");
|
200
|
-
*
|
201
|
-
* // Receive a message from the network
|
202
|
-
* channel.receiveMessage(incomingMessage);
|
203
|
-
*
|
204
|
-
* // Process the received message
|
205
|
-
* await channel.processTasks();
|
206
|
-
* ```
|
173
|
+
* @param message - The received SDS message.
|
207
174
|
*/
|
208
175
|
public receiveMessage(message: Message): void {
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
176
|
+
if (!message.lamportTimestamp) {
|
177
|
+
// Messages with no timestamp are ephemeral messages and should be delivered immediately
|
178
|
+
this.deliverMessage(message);
|
179
|
+
return;
|
180
|
+
}
|
181
|
+
// review ack status
|
182
|
+
this.reviewAckStatus(message);
|
183
|
+
// add to bloom filter (skip for messages with empty content)
|
184
|
+
if (message.content?.length && message.content.length > 0) {
|
185
|
+
this.filter.insert(message.messageId);
|
186
|
+
}
|
187
|
+
// verify causal history
|
188
|
+
const dependenciesMet = message.causalHistory.every((historyEntry) =>
|
189
|
+
this.localHistory.some(
|
190
|
+
({ historyEntry: { messageId } }) =>
|
191
|
+
messageId === historyEntry.messageId
|
192
|
+
)
|
193
|
+
);
|
194
|
+
if (!dependenciesMet) {
|
195
|
+
this.incomingBuffer.push(message);
|
196
|
+
this.timeReceived.set(message.messageId, Date.now());
|
197
|
+
} else {
|
198
|
+
this.deliverMessage(message);
|
199
|
+
}
|
215
200
|
}
|
216
201
|
|
217
|
-
|
218
|
-
* Processes messages in the incoming buffer, delivering those with satisfied dependencies.
|
219
|
-
*
|
220
|
-
* @returns Array of history entries for messages still missing dependencies
|
221
|
-
*/
|
202
|
+
// https://rfc.vac.dev/vac/raw/sds/#periodic-incoming-buffer-sweep
|
222
203
|
public sweepIncomingBuffer(): HistoryEntry[] {
|
223
204
|
const { buffer, missing } = this.incomingBuffer.reduce<{
|
224
205
|
buffer: Message[];
|
225
|
-
missing:
|
206
|
+
missing: HistoryEntry[];
|
226
207
|
}>(
|
227
208
|
({ buffer, missing }, message) => {
|
209
|
+
// Check each message for missing dependencies
|
228
210
|
const missingDependencies = message.causalHistory.filter(
|
229
211
|
(messageHistoryEntry) =>
|
230
212
|
!this.localHistory.some(
|
@@ -233,13 +215,9 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
233
215
|
)
|
234
216
|
);
|
235
217
|
if (missingDependencies.length === 0) {
|
218
|
+
// Any message with no missing dependencies is delivered
|
219
|
+
// and removed from the buffer (implicitly by not adding it to the new incoming buffer)
|
236
220
|
this.deliverMessage(message);
|
237
|
-
this.safeSendEvent(MessageChannelEvent.MessageDelivered, {
|
238
|
-
detail: {
|
239
|
-
messageId: message.messageId,
|
240
|
-
sentOrReceived: "received"
|
241
|
-
}
|
242
|
-
});
|
243
221
|
return { buffer, missing };
|
244
222
|
}
|
245
223
|
|
@@ -254,23 +232,18 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
254
232
|
return { buffer, missing };
|
255
233
|
}
|
256
234
|
}
|
257
|
-
|
258
|
-
|
259
|
-
});
|
235
|
+
// Any message with missing dependencies stays in the buffer
|
236
|
+
// and the missing message IDs are returned for processing.
|
260
237
|
return {
|
261
238
|
buffer: buffer.concat(message),
|
262
|
-
missing
|
239
|
+
missing: missing.concat(missingDependencies)
|
263
240
|
};
|
264
241
|
},
|
265
|
-
{ buffer: new Array<Message>(), missing: new
|
242
|
+
{ buffer: new Array<Message>(), missing: new Array<HistoryEntry>() }
|
266
243
|
);
|
244
|
+
// Update the incoming buffer to only include messages with no missing dependencies
|
267
245
|
this.incomingBuffer = buffer;
|
268
|
-
|
269
|
-
this.safeSendEvent(MessageChannelEvent.MissedMessages, {
|
270
|
-
detail: Array.from(missing)
|
271
|
-
});
|
272
|
-
|
273
|
-
return Array.from(missing);
|
246
|
+
return missing;
|
274
247
|
}
|
275
248
|
|
276
249
|
// https://rfc.vac.dev/vac/raw/sds/#periodic-outgoing-buffer-sweep
|
@@ -278,6 +251,7 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
278
251
|
unacknowledged: Message[];
|
279
252
|
possiblyAcknowledged: Message[];
|
280
253
|
} {
|
254
|
+
// Partition all messages in the outgoing buffer into unacknowledged and possibly acknowledged messages
|
281
255
|
return this.outgoingBuffer.reduce<{
|
282
256
|
unacknowledged: Message[];
|
283
257
|
possiblyAcknowledged: Message[];
|
@@ -311,7 +285,7 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
311
285
|
*
|
312
286
|
* @param callback - A callback function that returns a boolean indicating whether the message was sent successfully.
|
313
287
|
*/
|
314
|
-
public
|
288
|
+
public sendSyncMessage(
|
315
289
|
callback?: (message: Message) => Promise<boolean>
|
316
290
|
): Promise<boolean> {
|
317
291
|
this.lamportTimestamp++;
|
@@ -330,165 +304,15 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
330
304
|
};
|
331
305
|
|
332
306
|
if (callback) {
|
333
|
-
|
334
|
-
await callback(message);
|
335
|
-
this.safeSendEvent(MessageChannelEvent.SyncSent, {
|
336
|
-
detail: message
|
337
|
-
});
|
338
|
-
return true;
|
339
|
-
} catch (error) {
|
340
|
-
log.error("Callback execution failed in sendSyncMessage:", error);
|
341
|
-
throw error;
|
342
|
-
}
|
343
|
-
}
|
344
|
-
return false;
|
345
|
-
}
|
346
|
-
|
347
|
-
private _receiveMessage(message: Message): void {
|
348
|
-
const isDuplicate =
|
349
|
-
message.content &&
|
350
|
-
message.content.length > 0 &&
|
351
|
-
this.timeReceived.has(message.messageId);
|
352
|
-
|
353
|
-
if (isDuplicate) {
|
354
|
-
return;
|
355
|
-
}
|
356
|
-
|
357
|
-
if (!message.lamportTimestamp) {
|
358
|
-
this.deliverMessage(message);
|
359
|
-
return;
|
360
|
-
}
|
361
|
-
if (message.content?.length === 0) {
|
362
|
-
this.safeSendEvent(MessageChannelEvent.SyncReceived, {
|
363
|
-
detail: message
|
364
|
-
});
|
365
|
-
} else {
|
366
|
-
this.safeSendEvent(MessageChannelEvent.MessageReceived, {
|
367
|
-
detail: message
|
368
|
-
});
|
369
|
-
}
|
370
|
-
this.reviewAckStatus(message);
|
371
|
-
if (message.content?.length && message.content.length > 0) {
|
372
|
-
this.filter.insert(message.messageId);
|
373
|
-
}
|
374
|
-
const dependenciesMet = message.causalHistory.every((historyEntry) =>
|
375
|
-
this.localHistory.some(
|
376
|
-
({ historyEntry: { messageId } }) =>
|
377
|
-
messageId === historyEntry.messageId
|
378
|
-
)
|
379
|
-
);
|
380
|
-
if (!dependenciesMet) {
|
381
|
-
this.incomingBuffer.push(message);
|
382
|
-
this.timeReceived.set(message.messageId, Date.now());
|
383
|
-
} else {
|
384
|
-
this.deliverMessage(message);
|
385
|
-
this.safeSendEvent(MessageChannelEvent.MessageDelivered, {
|
386
|
-
detail: {
|
387
|
-
messageId: message.messageId,
|
388
|
-
sentOrReceived: "received"
|
389
|
-
}
|
390
|
-
});
|
391
|
-
}
|
392
|
-
}
|
393
|
-
|
394
|
-
private async executeTask<A extends Command>(item: Task<A>): Promise<void> {
|
395
|
-
try {
|
396
|
-
const handler = this.handlers[item.command];
|
397
|
-
await handler(item.params as ParamsByAction[A]);
|
398
|
-
} catch (error) {
|
399
|
-
log.error(`Task execution failed for command ${item.command}:`, error);
|
400
|
-
this.dispatchEvent(
|
401
|
-
new CustomEvent("taskError", {
|
402
|
-
detail: { command: item.command, error, params: item.params }
|
403
|
-
})
|
404
|
-
);
|
405
|
-
}
|
406
|
-
}
|
407
|
-
|
408
|
-
private safeSendEvent<T extends MessageChannelEvent>(
|
409
|
-
event: T,
|
410
|
-
eventInit?: CustomEventInit
|
411
|
-
): void {
|
412
|
-
try {
|
413
|
-
this.dispatchEvent(new CustomEvent(event, eventInit));
|
414
|
-
} catch (error) {
|
415
|
-
log.error(`Failed to dispatch event ${event}:`, error);
|
416
|
-
}
|
417
|
-
}
|
418
|
-
|
419
|
-
private async _sendMessage(
|
420
|
-
payload: Uint8Array,
|
421
|
-
callback?: (message: Message) => Promise<{
|
422
|
-
success: boolean;
|
423
|
-
retrievalHint?: Uint8Array;
|
424
|
-
}>
|
425
|
-
): Promise<void> {
|
426
|
-
this.lamportTimestamp++;
|
427
|
-
|
428
|
-
const messageId = MessageChannel.getMessageId(payload);
|
429
|
-
|
430
|
-
const message: Message = {
|
431
|
-
messageId,
|
432
|
-
channelId: this.channelId,
|
433
|
-
lamportTimestamp: this.lamportTimestamp,
|
434
|
-
causalHistory: this.localHistory
|
435
|
-
.slice(-this.causalHistorySize)
|
436
|
-
.map(({ historyEntry }) => historyEntry),
|
437
|
-
bloomFilter: this.filter.toBytes(),
|
438
|
-
content: payload
|
439
|
-
};
|
440
|
-
|
441
|
-
this.outgoingBuffer.push(message);
|
442
|
-
|
443
|
-
if (callback) {
|
444
|
-
try {
|
445
|
-
const { success, retrievalHint } = await callback(message);
|
446
|
-
if (success) {
|
447
|
-
this.filter.insert(messageId);
|
448
|
-
this.localHistory.push({
|
449
|
-
timestamp: this.lamportTimestamp,
|
450
|
-
historyEntry: {
|
451
|
-
messageId,
|
452
|
-
retrievalHint
|
453
|
-
}
|
454
|
-
});
|
455
|
-
this.timeReceived.set(messageId, Date.now());
|
456
|
-
this.safeSendEvent(MessageChannelEvent.MessageSent, {
|
457
|
-
detail: message
|
458
|
-
});
|
459
|
-
}
|
460
|
-
} catch (error) {
|
461
|
-
log.error("Callback execution failed in _sendMessage:", error);
|
462
|
-
throw error;
|
463
|
-
}
|
464
|
-
}
|
465
|
-
}
|
466
|
-
|
467
|
-
private async _sendEphemeralMessage(
|
468
|
-
payload: Uint8Array,
|
469
|
-
callback?: (message: Message) => Promise<boolean>
|
470
|
-
): Promise<void> {
|
471
|
-
const message: Message = {
|
472
|
-
messageId: MessageChannel.getMessageId(payload),
|
473
|
-
channelId: this.channelId,
|
474
|
-
content: payload,
|
475
|
-
lamportTimestamp: undefined,
|
476
|
-
causalHistory: [],
|
477
|
-
bloomFilter: undefined
|
478
|
-
};
|
479
|
-
|
480
|
-
if (callback) {
|
481
|
-
try {
|
482
|
-
await callback(message);
|
483
|
-
} catch (error) {
|
484
|
-
log.error("Callback execution failed in _sendEphemeralMessage:", error);
|
485
|
-
throw error;
|
486
|
-
}
|
307
|
+
return callback(message);
|
487
308
|
}
|
309
|
+
return Promise.resolve(false);
|
488
310
|
}
|
489
311
|
|
490
312
|
// See https://rfc.vac.dev/vac/raw/sds/#deliver-message
|
491
313
|
private deliverMessage(message: Message, retrievalHint?: Uint8Array): void {
|
314
|
+
this.notifyDeliveredMessage(message.messageId);
|
315
|
+
|
492
316
|
const messageLamportTimestamp = message.lamportTimestamp ?? 0;
|
493
317
|
if (messageLamportTimestamp > this.lamportTimestamp) {
|
494
318
|
this.lamportTimestamp = messageLamportTimestamp;
|
@@ -528,23 +352,17 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
528
352
|
// to determine the acknowledgement status of messages in the outgoing buffer.
|
529
353
|
// See https://rfc.vac.dev/vac/raw/sds/#review-ack-status
|
530
354
|
private reviewAckStatus(receivedMessage: Message): void {
|
355
|
+
// the participant MUST mark all messages in the received causal_history as acknowledged.
|
531
356
|
receivedMessage.causalHistory.forEach(({ messageId }) => {
|
532
357
|
this.outgoingBuffer = this.outgoingBuffer.filter(
|
533
|
-
({ messageId: outgoingMessageId }) =>
|
534
|
-
if (outgoingMessageId !== messageId) {
|
535
|
-
return true;
|
536
|
-
}
|
537
|
-
this.safeSendEvent(MessageChannelEvent.MessageAcknowledged, {
|
538
|
-
detail: messageId
|
539
|
-
});
|
540
|
-
return false;
|
541
|
-
}
|
358
|
+
({ messageId: outgoingMessageId }) => outgoingMessageId !== messageId
|
542
359
|
);
|
543
360
|
this.acknowledgements.delete(messageId);
|
544
361
|
if (!this.filter.lookup(messageId)) {
|
545
362
|
this.filter.insert(messageId);
|
546
363
|
}
|
547
364
|
});
|
365
|
+
// the participant MUST mark all messages included in the bloom_filter as possibly acknowledged
|
548
366
|
if (!receivedMessage.bloomFilter) {
|
549
367
|
return;
|
550
368
|
}
|
@@ -562,12 +380,6 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
562
380
|
const count = (this.acknowledgements.get(message.messageId) ?? 0) + 1;
|
563
381
|
if (count < this.acknowledgementCount) {
|
564
382
|
this.acknowledgements.set(message.messageId, count);
|
565
|
-
this.safeSendEvent(MessageChannelEvent.PartialAcknowledgement, {
|
566
|
-
detail: {
|
567
|
-
messageId: message.messageId,
|
568
|
-
count
|
569
|
-
}
|
570
|
-
});
|
571
383
|
return true;
|
572
384
|
}
|
573
385
|
this.acknowledgements.delete(message.messageId);
|
@@ -579,4 +391,15 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
579
391
|
private getAcknowledgementCount(): number {
|
580
392
|
return 2;
|
581
393
|
}
|
394
|
+
|
395
|
+
private notifyDeliveredMessage(messageId: string): void {
|
396
|
+
if (this.deliveredMessageCallback) {
|
397
|
+
this.deliveredMessageCallback(messageId);
|
398
|
+
}
|
399
|
+
this.dispatchEvent(
|
400
|
+
new CustomEvent(MessageChannelEvent.MessageDelivered, {
|
401
|
+
detail: messageId
|
402
|
+
})
|
403
|
+
);
|
404
|
+
}
|
582
405
|
}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"bloom.js","sourceRoot":"","sources":["../../src/bloom_filter/bloom.ts"],"names":[],"mappings":"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"}
|
@@ -1,29 +0,0 @@
|
|
1
|
-
import type { Message } from "./events.js";
|
2
|
-
export declare enum Command {
|
3
|
-
Send = "send",
|
4
|
-
Receive = "receive",
|
5
|
-
SendEphemeral = "sendEphemeral"
|
6
|
-
}
|
7
|
-
export interface ParamsByAction {
|
8
|
-
[Command.Send]: {
|
9
|
-
payload: Uint8Array;
|
10
|
-
callback?: (message: Message) => Promise<{
|
11
|
-
success: boolean;
|
12
|
-
retrievalHint?: Uint8Array;
|
13
|
-
}>;
|
14
|
-
};
|
15
|
-
[Command.Receive]: {
|
16
|
-
message: Message;
|
17
|
-
};
|
18
|
-
[Command.SendEphemeral]: {
|
19
|
-
payload: Uint8Array;
|
20
|
-
callback?: (message: Message) => Promise<boolean>;
|
21
|
-
};
|
22
|
-
}
|
23
|
-
export type Task<A extends Command = Command> = {
|
24
|
-
command: A;
|
25
|
-
params: ParamsByAction[A];
|
26
|
-
};
|
27
|
-
export type Handlers = {
|
28
|
-
[A in Command]: (params: ParamsByAction[A]) => Promise<void>;
|
29
|
-
};
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"command_queue.js","sourceRoot":"","sources":["../../src/message_channel/command_queue.ts"],"names":[],"mappings":"AAEA,MAAM,CAAN,IAAY,OAIX;AAJD,WAAY,OAAO;IACjB,wBAAa,CAAA;IACb,8BAAmB,CAAA;IACnB,0CAA+B,CAAA;AACjC,CAAC,EAJW,OAAO,KAAP,OAAO,QAIlB"}
|
@@ -1,32 +0,0 @@
|
|
1
|
-
import { proto_sds_message } from "@waku/proto";
|
2
|
-
export declare enum MessageChannelEvent {
|
3
|
-
MessageSent = "messageSent",
|
4
|
-
MessageDelivered = "messageDelivered",
|
5
|
-
MessageReceived = "messageReceived",
|
6
|
-
MessageAcknowledged = "messageAcknowledged",
|
7
|
-
PartialAcknowledgement = "partialAcknowledgement",
|
8
|
-
MissedMessages = "missedMessages",
|
9
|
-
SyncSent = "syncSent",
|
10
|
-
SyncReceived = "syncReceived"
|
11
|
-
}
|
12
|
-
export type Message = proto_sds_message.SdsMessage;
|
13
|
-
export type HistoryEntry = proto_sds_message.HistoryEntry;
|
14
|
-
export type ChannelId = string;
|
15
|
-
export declare function encodeMessage(message: Message): Uint8Array;
|
16
|
-
export declare function decodeMessage(data: Uint8Array): Message;
|
17
|
-
export type MessageChannelEvents = {
|
18
|
-
[MessageChannelEvent.MessageSent]: CustomEvent<Message>;
|
19
|
-
[MessageChannelEvent.MessageDelivered]: CustomEvent<{
|
20
|
-
messageId: string;
|
21
|
-
sentOrReceived: "sent" | "received";
|
22
|
-
}>;
|
23
|
-
[MessageChannelEvent.MessageReceived]: CustomEvent<Message>;
|
24
|
-
[MessageChannelEvent.MessageAcknowledged]: CustomEvent<string>;
|
25
|
-
[MessageChannelEvent.PartialAcknowledgement]: CustomEvent<{
|
26
|
-
messageId: string;
|
27
|
-
count: number;
|
28
|
-
}>;
|
29
|
-
[MessageChannelEvent.MissedMessages]: CustomEvent<HistoryEntry[]>;
|
30
|
-
[MessageChannelEvent.SyncSent]: CustomEvent<Message>;
|
31
|
-
[MessageChannelEvent.SyncReceived]: CustomEvent<Message>;
|
32
|
-
};
|