-
Notifications
You must be signed in to change notification settings - Fork 43
feat(sds): adds ephemeral messages, delivered message callback and event #2302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,17 @@ | ||
import { TypedEventEmitter } from "@libp2p/interface"; | ||
import { sha256 } from "@noble/hashes/sha256"; | ||
import { bytesToHex } from "@noble/hashes/utils"; | ||
import { proto_sds_message } from "@waku/proto"; | ||
|
||
import { DefaultBloomFilter } from "./bloom.js"; | ||
|
||
export enum MessageChannelEvent { | ||
MessageDelivered = "messageDelivered" | ||
} | ||
type MessageChannelEvents = { | ||
[MessageChannelEvent.MessageDelivered]: CustomEvent<string>; | ||
}; | ||
|
||
export type Message = proto_sds_message.SdsMessage; | ||
export type ChannelId = string; | ||
|
||
|
@@ -15,7 +23,14 @@ export const DEFAULT_BLOOM_FILTER_OPTIONS = { | |
const DEFAULT_CAUSAL_HISTORY_SIZE = 2; | ||
const DEFAULT_RECEIVED_MESSAGE_TIMEOUT = 1000 * 60 * 5; // 5 minutes | ||
|
||
export class MessageChannel { | ||
interface MessageChannelOptions { | ||
causalHistorySize?: number; | ||
receivedMessageTimeoutEnabled?: boolean; | ||
receivedMessageTimeout?: number; | ||
deliveredMessageCallback?: (messageId: string) => void; | ||
} | ||
|
||
export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> { | ||
private lamportTimestamp: number; | ||
private filter: DefaultBloomFilter; | ||
private outgoingBuffer: Message[]; | ||
|
@@ -26,23 +41,31 @@ export class MessageChannel { | |
private causalHistorySize: number; | ||
private acknowledgementCount: number; | ||
private timeReceived: Map<string, number>; | ||
private receivedMessageTimeoutEnabled: boolean; | ||
private receivedMessageTimeout: number; | ||
private deliveredMessageCallback?: (messageId: string) => void; | ||
|
||
public constructor( | ||
channelId: ChannelId, | ||
causalHistorySize: number = DEFAULT_CAUSAL_HISTORY_SIZE, | ||
private receivedMessageTimeoutEnabled: boolean = false, | ||
private receivedMessageTimeout: number = DEFAULT_RECEIVED_MESSAGE_TIMEOUT | ||
options: MessageChannelOptions = {} | ||
) { | ||
super(); | ||
this.channelId = channelId; | ||
this.lamportTimestamp = 0; | ||
this.filter = new DefaultBloomFilter(DEFAULT_BLOOM_FILTER_OPTIONS); | ||
this.outgoingBuffer = []; | ||
this.acknowledgements = new Map(); | ||
this.incomingBuffer = []; | ||
this.messageIdLog = []; | ||
this.causalHistorySize = causalHistorySize; | ||
this.causalHistorySize = | ||
options.causalHistorySize ?? DEFAULT_CAUSAL_HISTORY_SIZE; | ||
this.acknowledgementCount = this.getAcknowledgementCount(); | ||
this.timeReceived = new Map(); | ||
this.receivedMessageTimeoutEnabled = | ||
options.receivedMessageTimeoutEnabled ?? false; | ||
this.receivedMessageTimeout = | ||
options.receivedMessageTimeout ?? DEFAULT_RECEIVED_MESSAGE_TIMEOUT; | ||
this.deliveredMessageCallback = options.deliveredMessageCallback; | ||
} | ||
|
||
public static getMessageId(payload: Uint8Array): string { | ||
|
@@ -95,6 +118,36 @@ export class MessageChannel { | |
} | ||
} | ||
|
||
/** | ||
* Sends a short-lived message without synchronization or reliability requirements. | ||
* | ||
* Sends a message without a timestamp, causal history, or bloom filter. | ||
* Ephemeral messages are not added to the outgoing buffer. | ||
* Upon reception, ephemeral messages are delivered immediately without | ||
* checking for causal dependencies or including in the local log. | ||
* | ||
* See https://rfc.vac.dev/vac/raw/sds/#ephemeral-messages | ||
* | ||
* @param payload - The payload to send. | ||
* @param callback - A callback function that returns a boolean indicating whether the message was sent successfully. | ||
*/ | ||
public sendEphemeralMessage( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. generally curious why you separate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initially I tried that but decided that:
|
||
payload: Uint8Array, | ||
callback?: (message: Message) => boolean | ||
): void { | ||
const message: Message = { | ||
messageId: MessageChannel.getMessageId(payload), | ||
channelId: this.channelId, | ||
content: payload, | ||
lamportTimestamp: undefined, | ||
causalHistory: [], | ||
bloomFilter: undefined | ||
}; | ||
|
||
if (callback) { | ||
callback(message); | ||
} | ||
} | ||
/** | ||
* Process a received SDS message for this channel. | ||
* | ||
|
@@ -110,6 +163,11 @@ export class MessageChannel { | |
* @param message - The received SDS message. | ||
*/ | ||
public receiveMessage(message: Message): void { | ||
if (!message.lamportTimestamp) { | ||
// Messages with no timestamp are ephemeral messages and should be delivered immediately | ||
this.deliverMessage(message); | ||
return; | ||
} | ||
// review ack status | ||
this.reviewAckStatus(message); | ||
// add to bloom filter (skip for messages with empty content) | ||
|
@@ -241,13 +299,19 @@ export class MessageChannel { | |
|
||
// See https://rfc.vac.dev/vac/raw/sds/#deliver-message | ||
private deliverMessage(message: Message): void { | ||
this.notifyDeliveredMessage(message.messageId); | ||
|
||
const messageLamportTimestamp = message.lamportTimestamp ?? 0; | ||
if (messageLamportTimestamp > this.lamportTimestamp) { | ||
this.lamportTimestamp = messageLamportTimestamp; | ||
} | ||
|
||
if (message.content?.length === 0) { | ||
if ( | ||
message.content?.length === 0 || | ||
message.lamportTimestamp === undefined | ||
) { | ||
// Messages with empty content are sync messages. | ||
// Messages with no timestamp are ephemeral messages. | ||
// They are not added to the local log or bloom filter. | ||
return; | ||
} | ||
|
@@ -312,4 +376,15 @@ export class MessageChannel { | |
private getAcknowledgementCount(): number { | ||
return 2; | ||
} | ||
|
||
private notifyDeliveredMessage(messageId: string): void { | ||
if (this.deliveredMessageCallback) { | ||
this.deliveredMessageCallback(messageId); | ||
} | ||
this.dispatchEvent( | ||
new CustomEvent(MessageChannelEvent.MessageDelivered, { | ||
detail: messageId | ||
}) | ||
); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's make it strictly
2.7.0