mirror of
https://github.com/logos-messaging/js-waku.git
synced 2026-01-02 13:53:12 +00:00
fix: should not self-acknowledge messages (#2528)
* introduce `MessageId` type * fix deprecated import * test: own messages are not used for acks * fix: own messages are not used for ack * fix: own messages are not used for ack * test: do not self-possible-ack * doc: long term solution is SDS protocol change
This commit is contained in:
parent
52e5c34520
commit
459fe96fe6
@ -11,6 +11,7 @@ export enum MessageChannelEvent {
|
|||||||
SyncReceived = "syncReceived"
|
SyncReceived = "syncReceived"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MessageId = string;
|
||||||
export type Message = proto_sds_message.SdsMessage;
|
export type Message = proto_sds_message.SdsMessage;
|
||||||
export type HistoryEntry = proto_sds_message.HistoryEntry;
|
export type HistoryEntry = proto_sds_message.HistoryEntry;
|
||||||
export type ChannelId = string;
|
export type ChannelId = string;
|
||||||
@ -26,13 +27,13 @@ export function decodeMessage(data: Uint8Array): Message {
|
|||||||
export type MessageChannelEvents = {
|
export type MessageChannelEvents = {
|
||||||
[MessageChannelEvent.MessageSent]: CustomEvent<Message>;
|
[MessageChannelEvent.MessageSent]: CustomEvent<Message>;
|
||||||
[MessageChannelEvent.MessageDelivered]: CustomEvent<{
|
[MessageChannelEvent.MessageDelivered]: CustomEvent<{
|
||||||
messageId: string;
|
messageId: MessageId;
|
||||||
sentOrReceived: "sent" | "received";
|
sentOrReceived: "sent" | "received";
|
||||||
}>;
|
}>;
|
||||||
[MessageChannelEvent.MessageReceived]: CustomEvent<Message>;
|
[MessageChannelEvent.MessageReceived]: CustomEvent<Message>;
|
||||||
[MessageChannelEvent.MessageAcknowledged]: CustomEvent<string>;
|
[MessageChannelEvent.MessageAcknowledged]: CustomEvent<MessageId>;
|
||||||
[MessageChannelEvent.PartialAcknowledgement]: CustomEvent<{
|
[MessageChannelEvent.PartialAcknowledgement]: CustomEvent<{
|
||||||
messageId: string;
|
messageId: MessageId;
|
||||||
count: number;
|
count: number;
|
||||||
}>;
|
}>;
|
||||||
[MessageChannelEvent.MissedMessages]: CustomEvent<HistoryEntry[]>;
|
[MessageChannelEvent.MissedMessages]: CustomEvent<HistoryEntry[]>;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { expect } from "chai";
|
|||||||
|
|
||||||
import { DefaultBloomFilter } from "../bloom_filter/bloom.js";
|
import { DefaultBloomFilter } from "../bloom_filter/bloom.js";
|
||||||
|
|
||||||
import { HistoryEntry, Message } from "./events.js";
|
import { HistoryEntry, Message, MessageId } from "./events.js";
|
||||||
import {
|
import {
|
||||||
DEFAULT_BLOOM_FILTER_OPTIONS,
|
DEFAULT_BLOOM_FILTER_OPTIONS,
|
||||||
MessageChannel
|
MessageChannel
|
||||||
@ -293,6 +293,21 @@ describe("MessageChannel", function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not mark messages in causal history as acknowledged if it's our own message", async () => {
|
||||||
|
for (const m of messagesA) {
|
||||||
|
await sendMessage(channelA, utf8ToBytes(m), async (message) => {
|
||||||
|
await receiveMessage(channelA, message); // same channel used on purpose
|
||||||
|
return { success: true };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await channelA.processTasks();
|
||||||
|
|
||||||
|
// All messages remain in the buffer
|
||||||
|
expect((channelA as any).outgoingBuffer.length).to.equal(
|
||||||
|
messagesA.length
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("should track probabilistic acknowledgements of messages received in bloom filter", async () => {
|
it("should track probabilistic acknowledgements of messages received in bloom filter", async () => {
|
||||||
const acknowledgementCount = (channelA as any).acknowledgementCount;
|
const acknowledgementCount = (channelA as any).acknowledgementCount;
|
||||||
|
|
||||||
@ -326,7 +341,7 @@ describe("MessageChannel", function () {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const acknowledgements: ReadonlyMap<string, number> = (channelA as any)
|
const acknowledgements: ReadonlyMap<MessageId, number> = (channelA as any)
|
||||||
.acknowledgements;
|
.acknowledgements;
|
||||||
// Other than the message IDs which were included in causal history,
|
// Other than the message IDs which were included in causal history,
|
||||||
// the remaining messages sent by channel A should be considered possibly acknowledged
|
// the remaining messages sent by channel A should be considered possibly acknowledged
|
||||||
@ -376,6 +391,20 @@ describe("MessageChannel", function () {
|
|||||||
).to.equal(true);
|
).to.equal(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not track probabilistic acknowledgements of messages received in bloom filter of own messages", async () => {
|
||||||
|
for (const m of messagesA) {
|
||||||
|
await sendMessage(channelA, utf8ToBytes(m), async (message) => {
|
||||||
|
await receiveMessage(channelA, message);
|
||||||
|
return { success: true };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const acknowledgements: ReadonlyMap<MessageId, number> = (channelA as any)
|
||||||
|
.acknowledgements;
|
||||||
|
|
||||||
|
expect(acknowledgements.size).to.equal(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Sweeping incoming buffer", () => {
|
describe("Sweeping incoming buffer", () => {
|
||||||
@ -566,7 +595,7 @@ describe("MessageChannel", function () {
|
|||||||
|
|
||||||
const localLog = (channelA as any).localHistory as {
|
const localLog = (channelA as any).localHistory as {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
messageId: string;
|
messageId: MessageId;
|
||||||
}[];
|
}[];
|
||||||
expect(localLog.length).to.equal(0);
|
expect(localLog.length).to.equal(0);
|
||||||
});
|
});
|
||||||
@ -585,7 +614,7 @@ describe("MessageChannel", function () {
|
|||||||
|
|
||||||
const localLog = (channelB as any).localHistory as {
|
const localLog = (channelB as any).localHistory as {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
messageId: string;
|
messageId: MessageId;
|
||||||
}[];
|
}[];
|
||||||
expect(localLog.length).to.equal(0);
|
expect(localLog.length).to.equal(0);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { TypedEventEmitter } from "@libp2p/interface";
|
import { TypedEventEmitter } from "@libp2p/interface";
|
||||||
import { sha256 } from "@noble/hashes/sha256";
|
import { sha256 } from "@noble/hashes/sha2";
|
||||||
import { bytesToHex } from "@noble/hashes/utils";
|
import { bytesToHex } from "@noble/hashes/utils";
|
||||||
import { Logger } from "@waku/utils";
|
import { Logger } from "@waku/utils";
|
||||||
|
|
||||||
@ -11,7 +11,8 @@ import {
|
|||||||
HistoryEntry,
|
HistoryEntry,
|
||||||
Message,
|
Message,
|
||||||
MessageChannelEvent,
|
MessageChannelEvent,
|
||||||
MessageChannelEvents
|
MessageChannelEvents,
|
||||||
|
type MessageId
|
||||||
} from "./events.js";
|
} from "./events.js";
|
||||||
|
|
||||||
export const DEFAULT_BLOOM_FILTER_OPTIONS = {
|
export const DEFAULT_BLOOM_FILTER_OPTIONS = {
|
||||||
@ -35,14 +36,16 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|||||||
private lamportTimestamp: number;
|
private lamportTimestamp: number;
|
||||||
private filter: DefaultBloomFilter;
|
private filter: DefaultBloomFilter;
|
||||||
private outgoingBuffer: Message[];
|
private outgoingBuffer: Message[];
|
||||||
private acknowledgements: Map<string, number>;
|
private acknowledgements: Map<MessageId, number>;
|
||||||
private incomingBuffer: Message[];
|
private incomingBuffer: Message[];
|
||||||
private localHistory: { timestamp: number; historyEntry: HistoryEntry }[];
|
private localHistory: { timestamp: number; historyEntry: HistoryEntry }[];
|
||||||
private causalHistorySize: number;
|
private timeReceived: Map<MessageId, number>;
|
||||||
private acknowledgementCount: number;
|
// TODO: To be removed once sender id is added to SDS protocol
|
||||||
private timeReceived: Map<string, number>;
|
private outgoingMessages: Set<MessageId>;
|
||||||
private receivedMessageTimeoutEnabled: boolean;
|
private readonly causalHistorySize: number;
|
||||||
private receivedMessageTimeout: number;
|
private readonly acknowledgementCount: number;
|
||||||
|
private readonly receivedMessageTimeoutEnabled: boolean;
|
||||||
|
private readonly receivedMessageTimeout: number;
|
||||||
|
|
||||||
private tasks: Task[] = [];
|
private tasks: Task[] = [];
|
||||||
private handlers: Handlers = {
|
private handlers: Handlers = {
|
||||||
@ -83,9 +86,10 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|||||||
options.receivedMessageTimeoutEnabled ?? false;
|
options.receivedMessageTimeoutEnabled ?? false;
|
||||||
this.receivedMessageTimeout =
|
this.receivedMessageTimeout =
|
||||||
options.receivedMessageTimeout ?? DEFAULT_RECEIVED_MESSAGE_TIMEOUT;
|
options.receivedMessageTimeout ?? DEFAULT_RECEIVED_MESSAGE_TIMEOUT;
|
||||||
|
this.outgoingMessages = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getMessageId(payload: Uint8Array): string {
|
public static getMessageId(payload: Uint8Array): MessageId {
|
||||||
return bytesToHex(sha256(payload));
|
return bytesToHex(sha256(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,6 +358,15 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isOwnOutgoingMessage =
|
||||||
|
message.content &&
|
||||||
|
message.content.length > 0 &&
|
||||||
|
this.outgoingMessages.has(MessageChannel.getMessageId(message.content));
|
||||||
|
|
||||||
|
if (isOwnOutgoingMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!message.lamportTimestamp) {
|
if (!message.lamportTimestamp) {
|
||||||
this.deliverMessage(message);
|
this.deliverMessage(message);
|
||||||
return;
|
return;
|
||||||
@ -427,6 +440,8 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|||||||
|
|
||||||
const messageId = MessageChannel.getMessageId(payload);
|
const messageId = MessageChannel.getMessageId(payload);
|
||||||
|
|
||||||
|
this.outgoingMessages.add(messageId);
|
||||||
|
|
||||||
const message: Message = {
|
const message: Message = {
|
||||||
messageId,
|
messageId,
|
||||||
channelId: this.channelId,
|
channelId: this.channelId,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user