parent
0677fedc0e
commit
2f3ac73e5e
|
@ -10,6 +10,7 @@ import {
|
|||
} from '../protos/chat-message'
|
||||
import { EmojiReaction } from '../protos/emoji-reaction'
|
||||
import { generateKeyFromPassword } from '../utils/generate-key-from-password'
|
||||
import { getNextClock } from '../utils/get-next-clock'
|
||||
import { idToContentTopic } from '../utils/id-to-content-topic'
|
||||
import { getReactions } from './community/get-reactions'
|
||||
|
||||
|
@ -36,6 +37,7 @@ type FetchedMessage = { messageId: string; timestamp?: Date }
|
|||
|
||||
export class Chat {
|
||||
private readonly client: Client
|
||||
#clock: bigint
|
||||
|
||||
public readonly uuid: string
|
||||
public readonly id: string
|
||||
|
@ -72,6 +74,7 @@ export class Chat {
|
|||
this.symmetricKey = options.symmetricKey
|
||||
this.description = options.description
|
||||
|
||||
this.#clock = BigInt(Date.now())
|
||||
this.chatCallbacks = new Set()
|
||||
this.#messages = new Map()
|
||||
this.#editTextEvents = new Map()
|
||||
|
@ -243,7 +246,7 @@ export class Chat {
|
|||
this.emitChange(description)
|
||||
}
|
||||
|
||||
public handleNewMessage = (newMessage: ChatMessage, timestamp?: Date) => {
|
||||
public handleNewMessage = (newMessage: ChatMessage, timestamp: Date) => {
|
||||
// fetching in progress
|
||||
if (this.#fetchingMessages) {
|
||||
this.#oldestFetchedMessage = this.getOldestFetchedMessage(
|
||||
|
@ -432,7 +435,7 @@ export class Chat {
|
|||
|
||||
// TODO: protos does not support optional fields :-(
|
||||
const payload = ChatMessageProto.encode({
|
||||
clock: BigInt(Date.now()),
|
||||
clock: this.setClock(this.#clock),
|
||||
timestamp: BigInt(Date.now()),
|
||||
text,
|
||||
responseTo: responseTo ?? '',
|
||||
|
@ -465,7 +468,7 @@ export class Chat {
|
|||
|
||||
public sendImageMessage = async (image: ImageMessage) => {
|
||||
const payload = ChatMessageProto.encode({
|
||||
clock: BigInt(Date.now()),
|
||||
clock: this.setClock(this.#clock),
|
||||
timestamp: BigInt(Date.now()),
|
||||
text: '',
|
||||
responseTo: '',
|
||||
|
@ -516,7 +519,7 @@ export class Chat {
|
|||
}
|
||||
|
||||
const payload = EditMessage.encode({
|
||||
clock: BigInt(Date.now()),
|
||||
clock: this.setClock(this.#clock),
|
||||
text,
|
||||
messageId,
|
||||
chatId: this.id,
|
||||
|
@ -550,7 +553,7 @@ export class Chat {
|
|||
}
|
||||
|
||||
const payload = DeleteMessage.encode({
|
||||
clock: BigInt(Date.now()),
|
||||
clock: this.setClock(this.#clock),
|
||||
messageId,
|
||||
chatId: this.id,
|
||||
grant: new Uint8Array([]),
|
||||
|
@ -584,7 +587,7 @@ export class Chat {
|
|||
)
|
||||
|
||||
const payload = EmojiReaction.encode({
|
||||
clock: BigInt(Date.now()),
|
||||
clock: this.setClock(this.#clock),
|
||||
chatId: this.id,
|
||||
messageType: 'COMMUNITY_CHAT' as MessageType,
|
||||
messageId,
|
||||
|
@ -636,4 +639,10 @@ export class Chat {
|
|||
|
||||
return message
|
||||
}
|
||||
|
||||
public setClock = (currentClock?: bigint): bigint => {
|
||||
this.#clock = getNextClock(currentClock)
|
||||
|
||||
return this.#clock
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { CommunityRequestToJoin } from '../../protos/communities'
|
|||
import { MessageType } from '../../protos/enums'
|
||||
import { compressPublicKey } from '../../utils/compress-public-key'
|
||||
import { generateKeyFromPassword } from '../../utils/generate-key-from-password'
|
||||
import { getNextClock } from '../../utils/get-next-clock'
|
||||
import { idToContentTopic } from '../../utils/id-to-content-topic'
|
||||
import { Chat } from '../chat'
|
||||
import { Member } from '../member'
|
||||
|
@ -19,6 +20,7 @@ import type { Client } from '../client'
|
|||
|
||||
export class Community {
|
||||
private client: Client
|
||||
#clock: bigint
|
||||
|
||||
/** Compressed. */
|
||||
public publicKey: string
|
||||
|
@ -36,6 +38,7 @@ export class Community {
|
|||
this.publicKey = publicKey
|
||||
this.id = publicKey.replace(/^0[xX]/, '')
|
||||
|
||||
this.#clock = BigInt(Date.now())
|
||||
this.chats = new Map()
|
||||
this.#members = new Map()
|
||||
this.#callbacks = new Set()
|
||||
|
@ -252,7 +255,7 @@ export class Community {
|
|||
|
||||
public requestToJoin = async (chatId = '') => {
|
||||
const payload = CommunityRequestToJoin.encode({
|
||||
clock: BigInt(Date.now()),
|
||||
clock: this.setClock(this.#clock),
|
||||
chatId,
|
||||
communityId: hexToBytes(this.id),
|
||||
ensName: '',
|
||||
|
@ -276,4 +279,10 @@ export class Community {
|
|||
public isMember = (signerPublicKey: string): boolean => {
|
||||
return this.getMember(signerPublicKey) !== undefined
|
||||
}
|
||||
|
||||
public setClock = (currentClock?: bigint): bigint => {
|
||||
this.#clock = getNextClock(currentClock)
|
||||
|
||||
return this.#clock
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { EmojiReaction } from '../../protos/emoji-reaction'
|
||||
import { PinMessage } from '../../protos/pin-message'
|
||||
import { ProtocolMessage } from '../../protos/protocol-message'
|
||||
import { isClockValid } from '../../utils/is-clock-valid'
|
||||
import { payloadToId } from '../../utils/payload-to-id'
|
||||
import { recoverPublicKey } from '../../utils/recover-public-key'
|
||||
import { getChatUuid } from './get-chat-uuid'
|
||||
|
@ -27,6 +28,7 @@ export function handleWakuMessage(
|
|||
community: Community
|
||||
): void {
|
||||
// decode (layers)
|
||||
// validate
|
||||
if (!wakuMessage.payload) {
|
||||
return
|
||||
}
|
||||
|
@ -35,6 +37,10 @@ export function handleWakuMessage(
|
|||
return
|
||||
}
|
||||
|
||||
if (!wakuMessage.timestamp) {
|
||||
return
|
||||
}
|
||||
|
||||
let messageToDecode = wakuMessage.payload
|
||||
let decodedProtocol
|
||||
try {
|
||||
|
@ -78,12 +84,18 @@ export function handleWakuMessage(
|
|||
// decode
|
||||
const decodedPayload = CommunityDescription.decode(messageToDecode)
|
||||
|
||||
// validate
|
||||
if (!isClockValid(BigInt(decodedPayload.clock), messageTimestamp)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!community.isOwner(signerPublicKey)) {
|
||||
return
|
||||
}
|
||||
|
||||
// handle (state and callback)
|
||||
community.handleDescription(decodedPayload)
|
||||
community.setClock(BigInt(decodedPayload.clock))
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -92,6 +104,10 @@ export function handleWakuMessage(
|
|||
// decode
|
||||
const decodedPayload = ChatMessage.decode(messageToDecode)
|
||||
|
||||
if (!isClockValid(BigInt(decodedPayload.clock), messageTimestamp)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (decodedPayload.messageType) {
|
||||
case MessageType.COMMUNITY_CHAT: {
|
||||
if (!community.isMember(signerPublicKey)) {
|
||||
|
@ -114,6 +130,7 @@ export function handleWakuMessage(
|
|||
|
||||
// handle
|
||||
chat.handleNewMessage(chatMessage, messageTimestamp)
|
||||
chat.setClock(decodedPayload.clock)
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -129,6 +146,10 @@ export function handleWakuMessage(
|
|||
case ApplicationMetadataMessage.Type.TYPE_EDIT_MESSAGE: {
|
||||
const decodedPayload = EditMessage.decode(messageToDecode)
|
||||
|
||||
if (!isClockValid(BigInt(decodedPayload.clock), messageTimestamp)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (decodedPayload.messageType) {
|
||||
case MessageType.COMMUNITY_CHAT: {
|
||||
if (!community.isMember(signerPublicKey)) {
|
||||
|
@ -149,6 +170,7 @@ export function handleWakuMessage(
|
|||
decodedPayload.clock,
|
||||
signerPublicKey
|
||||
)
|
||||
chat.setClock(decodedPayload.clock)
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -164,6 +186,10 @@ export function handleWakuMessage(
|
|||
case ApplicationMetadataMessage.Type.TYPE_DELETE_MESSAGE: {
|
||||
const decodedPayload = DeleteMessage.decode(messageToDecode)
|
||||
|
||||
if (!isClockValid(BigInt(decodedPayload.clock), messageTimestamp)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (decodedPayload.messageType) {
|
||||
case MessageType.COMMUNITY_CHAT: {
|
||||
if (!community.isMember(signerPublicKey)) {
|
||||
|
@ -183,6 +209,7 @@ export function handleWakuMessage(
|
|||
decodedPayload.clock,
|
||||
signerPublicKey
|
||||
)
|
||||
chat.setClock(decodedPayload.clock)
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -198,6 +225,10 @@ export function handleWakuMessage(
|
|||
case ApplicationMetadataMessage.Type.TYPE_PIN_MESSAGE: {
|
||||
const decodedPayload = PinMessage.decode(messageToDecode)
|
||||
|
||||
if (!isClockValid(BigInt(decodedPayload.clock), messageTimestamp)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (decodedPayload.messageType) {
|
||||
case MessageType.COMMUNITY_CHAT: {
|
||||
if (!community.isMember(signerPublicKey)) {
|
||||
|
@ -217,6 +248,7 @@ export function handleWakuMessage(
|
|||
decodedPayload.clock,
|
||||
decodedPayload.pinned
|
||||
)
|
||||
chat.setClock(decodedPayload.clock)
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -232,6 +264,10 @@ export function handleWakuMessage(
|
|||
case ApplicationMetadataMessage.Type.TYPE_EMOJI_REACTION: {
|
||||
const decodedPayload = EmojiReaction.decode(messageToDecode)
|
||||
|
||||
if (!isClockValid(BigInt(decodedPayload.clock), messageTimestamp)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (decodedPayload.messageType) {
|
||||
case MessageType.COMMUNITY_CHAT: {
|
||||
if (!community.isMember(signerPublicKey)) {
|
||||
|
@ -252,6 +288,7 @@ export function handleWakuMessage(
|
|||
decodedPayload.clock,
|
||||
signerPublicKey
|
||||
)
|
||||
chat.setClock(decodedPayload.clock)
|
||||
|
||||
break
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ export function communityPermissions_AccessToJSON(
|
|||
}
|
||||
|
||||
export interface CommunityDescription {
|
||||
// fixme?: bigint
|
||||
clock: number
|
||||
members: { [key: string]: CommunityMember }
|
||||
permissions: CommunityPermissions | undefined
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export const getNextClock = (currentClock = 0n): bigint => {
|
||||
const now = BigInt(Date.now()) // timestamp
|
||||
const nextClock = currentClock < now ? now : currentClock + 1n
|
||||
|
||||
return nextClock
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
const MAX_OFFSET = BigInt(120 * 1000)
|
||||
|
||||
export function isClockValid(
|
||||
messageClock: bigint,
|
||||
messageTimestamp: Date
|
||||
): boolean {
|
||||
if (messageClock <= 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (messageClock > BigInt(messageTimestamp.getTime()) + MAX_OFFSET) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
Loading…
Reference in New Issue