Skip to content

Commit 5334a7f

Browse files
jm-cliusfryorcrakenweboko
authored
feat: add SDS-Repair (SDS-R) to the SDS implementation (#2698)
* wip * feat: integrate sds-r with message channels * feat: add SDS-R events * fix: fixed buffer handling incoming and outgoing * fix: more buffer fixes * fix: remove some magic numbers * fix: buffer optimisation, backwards compatible senderId * fix: fix implementation guide, remove unrelated claude file * fix: further buffer optimisations * fix: linting errors * fix: suggestions from code review Co-authored-by: Sasha <118575614+weboko@users.noreply.github.com> Co-authored-by: fryorcraken <110212804+fryorcraken@users.noreply.github.com> * fix: remove implementation guide * fix: build errors, remove override, improve buffer * fix: consistent use of MessageId and ParticipantId * fix: switch to conditionally constructed from conditionally executed --------- Co-authored-by: fryorcraken <commits@fryorcraken.xyz> Co-authored-by: Sasha <118575614+weboko@users.noreply.github.com> Co-authored-by: fryorcraken <110212804+fryorcraken@users.noreply.github.com>
1 parent 115cdd2 commit 5334a7f

13 files changed

Lines changed: 1210 additions & 37 deletions

File tree

packages/proto/src/generated/sds_message.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { Uint8ArrayList } from 'uint8arraylist'
1313
export interface HistoryEntry {
1414
messageId: string
1515
retrievalHint?: Uint8Array
16+
senderId?: string
1617
}
1718

1819
export namespace HistoryEntry {
@@ -35,6 +36,11 @@ export namespace HistoryEntry {
3536
w.bytes(obj.retrievalHint)
3637
}
3738

39+
if (obj.senderId != null) {
40+
w.uint32(26)
41+
w.string(obj.senderId)
42+
}
43+
3844
if (opts.lengthDelimited !== false) {
3945
w.ldelim()
4046
}
@@ -57,6 +63,10 @@ export namespace HistoryEntry {
5763
obj.retrievalHint = reader.bytes()
5864
break
5965
}
66+
case 3: {
67+
obj.senderId = reader.string()
68+
break
69+
}
6070
default: {
6171
reader.skipType(tag & 7)
6272
break
@@ -87,6 +97,7 @@ export interface SdsMessage {
8797
lamportTimestamp?: bigint
8898
causalHistory: HistoryEntry[]
8999
bloomFilter?: Uint8Array
100+
repairRequest: HistoryEntry[]
90101
content?: Uint8Array
91102
}
92103

@@ -132,6 +143,13 @@ export namespace SdsMessage {
132143
w.bytes(obj.bloomFilter)
133144
}
134145

146+
if (obj.repairRequest != null) {
147+
for (const value of obj.repairRequest) {
148+
w.uint32(106)
149+
HistoryEntry.codec().encode(value, w)
150+
}
151+
}
152+
135153
if (obj.content != null) {
136154
w.uint32(162)
137155
w.bytes(obj.content)
@@ -145,7 +163,8 @@ export namespace SdsMessage {
145163
senderId: '',
146164
messageId: '',
147165
channelId: '',
148-
causalHistory: []
166+
causalHistory: [],
167+
repairRequest: []
149168
}
150169

151170
const end = length == null ? reader.len : reader.pos + length
@@ -184,6 +203,16 @@ export namespace SdsMessage {
184203
obj.bloomFilter = reader.bytes()
185204
break
186205
}
206+
case 13: {
207+
if (opts.limits?.repairRequest != null && obj.repairRequest.length === opts.limits.repairRequest) {
208+
throw new MaxLengthError('Decode error - map field "repairRequest" had too many elements')
209+
}
210+
211+
obj.repairRequest.push(HistoryEntry.codec().decode(reader, reader.uint32(), {
212+
limits: opts.limits?.repairRequest$
213+
}))
214+
break
215+
}
187216
case 20: {
188217
obj.content = reader.bytes()
189218
break

packages/proto/src/lib/sds_message.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ syntax = "proto3";
33
message HistoryEntry {
44
string message_id = 1; // Unique identifier of the SDS message, as defined in `Message`
55
optional bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS message; For example, A Waku deterministic message hash or routing payload hash
6+
7+
optional string sender_id = 3; // Participant ID of original message sender. Only populated if using optional SDS Repair extension
68
}
79

810
message SdsMessage {
@@ -12,5 +14,8 @@ message SdsMessage {
1214
optional uint64 lamport_timestamp = 10; // Logical timestamp for causal ordering in channel
1315
repeated HistoryEntry causal_history = 11; // List of preceding message IDs that this message causally depends on. Generally 2 or 3 message IDs are included.
1416
optional bytes bloom_filter = 12; // Bloom filter representing received message IDs in channel
17+
18+
repeated HistoryEntry repair_request = 13; // Capped list of history entries missing from sender's causal history. Only populated if using the optional SDS Repair extension.
19+
1520
optional bytes content = 20; // Actual content of the message
1621
}

packages/sds/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ export {
1414
type HistoryEntry,
1515
type ChannelId,
1616
type MessageChannelEvents,
17-
type SenderId,
17+
type ParticipantId,
1818
type MessageId
1919
} from "./message_channel/index.js";
2020

21+
/**
22+
* @deprecated Use ParticipantId instead. SenderId has been renamed to ParticipantId
23+
* to better reflect that it represents a channel participant, not just a message sender.
24+
*/
25+
export type { ParticipantId as SenderId } from "./message_channel/index.js";
26+
2127
export { BloomFilter };

packages/sds/src/message_channel/events.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HistoryEntry, Message, MessageId } from "./message.js";
1+
import { HistoryEntry, Message, MessageId, ParticipantId } from "./message.js";
22

33
export enum MessageChannelEvent {
44
OutMessageSent = "sds:out:message-sent",
@@ -10,7 +10,13 @@ export enum MessageChannelEvent {
1010
OutSyncSent = "sds:out:sync-sent",
1111
InSyncReceived = "sds:in:sync-received",
1212
InMessageLost = "sds:in:message-irretrievably-lost",
13-
ErrorTask = "sds:error-task"
13+
ErrorTask = "sds:error-task",
14+
// SDS-R Repair Events
15+
RepairRequestQueued = "sds:repair:request-queued",
16+
RepairRequestSent = "sds:repair:request-sent",
17+
RepairRequestReceived = "sds:repair:request-received",
18+
RepairResponseQueued = "sds:repair:response-queued",
19+
RepairResponseSent = "sds:repair:response-sent"
1420
}
1521

1622
export type MessageChannelEvents = {
@@ -26,5 +32,24 @@ export type MessageChannelEvents = {
2632
[MessageChannelEvent.InMessageLost]: CustomEvent<HistoryEntry[]>;
2733
[MessageChannelEvent.OutSyncSent]: CustomEvent<Message>;
2834
[MessageChannelEvent.InSyncReceived]: CustomEvent<Message>;
29-
[MessageChannelEvent.ErrorTask]: CustomEvent<any>;
35+
[MessageChannelEvent.ErrorTask]: CustomEvent<unknown>;
36+
[MessageChannelEvent.RepairRequestQueued]: CustomEvent<{
37+
messageId: MessageId;
38+
tReq: number;
39+
}>;
40+
[MessageChannelEvent.RepairRequestSent]: CustomEvent<{
41+
messageIds: MessageId[];
42+
carrierMessageId: MessageId;
43+
}>;
44+
[MessageChannelEvent.RepairRequestReceived]: CustomEvent<{
45+
messageIds: MessageId[];
46+
fromSenderId?: ParticipantId;
47+
}>;
48+
[MessageChannelEvent.RepairResponseQueued]: CustomEvent<{
49+
messageId: MessageId;
50+
tResp: number;
51+
}>;
52+
[MessageChannelEvent.RepairResponseSent]: CustomEvent<{
53+
messageId: MessageId;
54+
}>;
3055
};

packages/sds/src/message_channel/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export {
88
HistoryEntry,
99
Message,
1010
MessageId,
11-
SenderId,
11+
ParticipantId,
1212
SyncMessage,
1313
isContentMessage,
1414
isEphemeralMessage,

packages/sds/src/message_channel/message.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe("Message serialization", () => {
4444
[{ messageId: depMessageId, retrievalHint: depRetrievalHint }],
4545
0n,
4646
undefined,
47+
undefined,
4748
undefined
4849
);
4950

@@ -54,6 +55,39 @@ describe("Message serialization", () => {
5455
{ messageId: depMessageId, retrievalHint: depRetrievalHint }
5556
]);
5657
});
58+
59+
it("Repair Request", () => {
60+
const repairMessageId = "missing-message";
61+
const repairRetrievalHint = utf8ToBytes("missing-retrieval");
62+
const repairSenderId = "original-sender";
63+
const message = new Message(
64+
"123",
65+
"my-channel",
66+
"me",
67+
[],
68+
0n,
69+
undefined,
70+
undefined,
71+
[
72+
{
73+
messageId: repairMessageId,
74+
retrievalHint: repairRetrievalHint,
75+
senderId: repairSenderId
76+
}
77+
]
78+
);
79+
80+
const bytes = message.encode();
81+
const decMessage = Message.decode(bytes);
82+
83+
expect(decMessage!.repairRequest).to.deep.equal([
84+
{
85+
messageId: repairMessageId,
86+
retrievalHint: repairRetrievalHint,
87+
senderId: repairSenderId
88+
}
89+
]);
90+
});
5791
});
5892

5993
describe("ContentMessage comparison with < operator", () => {

packages/sds/src/message_channel/message.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ import { Logger } from "@waku/utils";
44
export type MessageId = string;
55
export type HistoryEntry = proto_sds_message.HistoryEntry;
66
export type ChannelId = string;
7-
export type SenderId = string;
7+
export type ParticipantId = string;
88

99
const log = new Logger("sds:message");
1010

1111
export class Message implements proto_sds_message.SdsMessage {
1212
public constructor(
13-
public messageId: string,
13+
public messageId: MessageId,
1414
public channelId: string,
15-
public senderId: string,
15+
public senderId: ParticipantId,
1616
public causalHistory: proto_sds_message.HistoryEntry[],
1717
public lamportTimestamp?: bigint | undefined,
1818
public bloomFilter?: Uint8Array<ArrayBufferLike> | undefined,
1919
public content?: Uint8Array<ArrayBufferLike> | undefined,
20+
public repairRequest: proto_sds_message.HistoryEntry[] = [],
2021
/**
2122
* Not encoded, set after it is sent, used to include in follow-up messages
2223
*/
@@ -38,7 +39,8 @@ export class Message implements proto_sds_message.SdsMessage {
3839
causalHistory,
3940
lamportTimestamp,
4041
bloomFilter,
41-
content
42+
content,
43+
repairRequest
4244
} = proto_sds_message.SdsMessage.decode(data);
4345

4446
if (testContentMessage({ lamportTimestamp, content })) {
@@ -49,7 +51,8 @@ export class Message implements proto_sds_message.SdsMessage {
4951
causalHistory,
5052
lamportTimestamp!,
5153
bloomFilter,
52-
content!
54+
content!,
55+
repairRequest
5356
);
5457
}
5558

@@ -61,7 +64,8 @@ export class Message implements proto_sds_message.SdsMessage {
6164
causalHistory,
6265
undefined,
6366
bloomFilter,
64-
content!
67+
content!,
68+
repairRequest
6569
);
6670
}
6771

@@ -73,7 +77,8 @@ export class Message implements proto_sds_message.SdsMessage {
7377
causalHistory,
7478
lamportTimestamp!,
7579
bloomFilter,
76-
undefined
80+
undefined,
81+
repairRequest
7782
);
7883
}
7984
log.error(
@@ -90,13 +95,14 @@ export class Message implements proto_sds_message.SdsMessage {
9095

9196
export class SyncMessage extends Message {
9297
public constructor(
93-
public messageId: string,
98+
public messageId: MessageId,
9499
public channelId: string,
95-
public senderId: string,
100+
public senderId: ParticipantId,
96101
public causalHistory: proto_sds_message.HistoryEntry[],
97102
public lamportTimestamp: bigint,
98103
public bloomFilter: Uint8Array<ArrayBufferLike> | undefined,
99104
public content: undefined,
105+
public repairRequest: proto_sds_message.HistoryEntry[] = [],
100106
/**
101107
* Not encoded, set after it is sent, used to include in follow-up messages
102108
*/
@@ -110,6 +116,7 @@ export class SyncMessage extends Message {
110116
lamportTimestamp,
111117
bloomFilter,
112118
content,
119+
repairRequest,
113120
retrievalHint
114121
);
115122
}
@@ -134,13 +141,14 @@ export function isSyncMessage(
134141

135142
export class EphemeralMessage extends Message {
136143
public constructor(
137-
public messageId: string,
144+
public messageId: MessageId,
138145
public channelId: string,
139-
public senderId: string,
146+
public senderId: ParticipantId,
140147
public causalHistory: proto_sds_message.HistoryEntry[],
141148
public lamportTimestamp: undefined,
142149
public bloomFilter: Uint8Array<ArrayBufferLike> | undefined,
143150
public content: Uint8Array<ArrayBufferLike>,
151+
public repairRequest: proto_sds_message.HistoryEntry[] = [],
144152
/**
145153
* Not encoded, set after it is sent, used to include in follow-up messages
146154
*/
@@ -157,6 +165,7 @@ export class EphemeralMessage extends Message {
157165
lamportTimestamp,
158166
bloomFilter,
159167
content,
168+
repairRequest,
160169
retrievalHint
161170
);
162171
}
@@ -182,13 +191,14 @@ function testEphemeralMessage(message: {
182191

183192
export class ContentMessage extends Message {
184193
public constructor(
185-
public messageId: string,
194+
public messageId: MessageId,
186195
public channelId: string,
187-
public senderId: string,
196+
public senderId: ParticipantId,
188197
public causalHistory: proto_sds_message.HistoryEntry[],
189198
public lamportTimestamp: bigint,
190199
public bloomFilter: Uint8Array<ArrayBufferLike> | undefined,
191200
public content: Uint8Array<ArrayBufferLike>,
201+
public repairRequest: proto_sds_message.HistoryEntry[] = [],
192202
/**
193203
* Not encoded, set after it is sent, used to include in follow-up messages
194204
*/
@@ -205,6 +215,7 @@ export class ContentMessage extends Message {
205215
lamportTimestamp,
206216
bloomFilter,
207217
content,
218+
repairRequest,
208219
retrievalHint
209220
);
210221
}

packages/sds/src/message_channel/message_channel.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ describe("MessageChannel", function () {
162162
.slice(-causalHistorySize - 1, -1)
163163
.map((message) => ({
164164
messageId: MessageChannel.getMessageId(utf8ToBytes(message)),
165-
retrievalHint: undefined
165+
retrievalHint: undefined,
166+
senderId: "alice"
166167
}));
167168
expect(causalHistory).to.deep.equal(expectedCausalHistory);
168169
});
@@ -298,6 +299,7 @@ describe("MessageChannel", function () {
298299
1n,
299300
undefined,
300301
payload,
302+
undefined,
301303
testRetrievalHint
302304
),
303305
testRetrievalHint

0 commit comments

Comments
 (0)