Set clocks (#284)

* add clock functions

* remove bind
This commit is contained in:
Felicio Mununga 2022-06-30 15:11:13 +02:00 committed by GitHub
parent 0677fedc0e
commit 2f3ac73e5e
No known key found for this signature in database
GPG Key ID: 0EB8D75C775AB6F1
6 changed files with 85 additions and 7 deletions

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -114,6 +114,7 @@ export function communityPermissions_AccessToJSON(
}
export interface CommunityDescription {
// fixme?: bigint
clock: number
members: { [key: string]: CommunityMember }
permissions: CommunityPermissions | undefined

View File

@ -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
}

View File

@ -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
}