From e66619fbe15ffdf3fa27e96d382c4e3c593f3a01 Mon Sep 17 00:00:00 2001 From: Tanner Shaw Date: Tue, 15 Aug 2023 14:27:29 -0500 Subject: [PATCH] feature(messages) Checking internal nullifer collisions on messages --- package-lock.json | 8 +-- package.json | 2 +- src/data/db.ts | 4 +- src/data/messages.ts | 125 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 117 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3108087..deace5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "body-parser": "^1.20.2", "cors": "^2.8.5", "discreetly-claimcodes": "^1.1.5", - "discreetly-interfaces": "^0.1.29", + "discreetly-interfaces": "^0.1.31", "dotenv": "^16.3.1", "express": "^4.18.2", "express-basic-auth": "^1.2.1", @@ -3466,9 +3466,9 @@ "integrity": "sha512-pQueoGtBJk/FrTfGzepjqYfTLaymS+4t11byI4OcfjWQOagRsD7dtavGcowTVQ7Ib/vjKna5T+71WcgWZaAWuA==" }, "node_modules/discreetly-interfaces": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/discreetly-interfaces/-/discreetly-interfaces-0.1.29.tgz", - "integrity": "sha512-3R63KkmB+wFKFFzD9DixX3VDoLCYkDuMQZucAItmXbjE+0tFgmrK683a1/WBI9VkBhARip6HsVNrjzaGqdR1Aw==", + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/discreetly-interfaces/-/discreetly-interfaces-0.1.31.tgz", + "integrity": "sha512-XRuQee/hmX0I3NkXkNZEzZ+6ZLLVyjjyv4HzcN2H1HJWnhmBsb0BozHifYd22Xk83LuXp7rojnK9qrAHwy1k9g==", "dependencies": { "poseidon-lite": "^0.2.0", "rlnjs": "^3.1.4" diff --git a/package.json b/package.json index b830f82..d7c5d24 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "body-parser": "^1.20.2", "cors": "^2.8.5", "discreetly-claimcodes": "^1.1.5", - "discreetly-interfaces": "^0.1.29", + "discreetly-interfaces": "^0.1.31", "dotenv": "^16.3.1", "express": "^4.18.2", "express-basic-auth": "^1.2.1", diff --git a/src/data/db.ts b/src/data/db.ts index 81db126..91f6f91 100644 --- a/src/data/db.ts +++ b/src/data/db.ts @@ -170,11 +170,11 @@ export async function createRoom( const mockUsers: string[] = genMockUsers(approxNumMockUsers); const roomData = { where: { - roomId: genId(serverConfig.id, name).toString() + roomId: genId(serverConfig.id as bigint, name).toString() }, update: {}, create: { - roomId: genId(serverConfig.id, name).toString(), + roomId: genId(serverConfig.id as bigint, name).toString(), name: name, rateLimit: rateLimit, userMessageLimit: userMessageLimit, diff --git a/src/data/messages.ts b/src/data/messages.ts index cf517db..7487365 100644 --- a/src/data/messages.ts +++ b/src/data/messages.ts @@ -1,16 +1,108 @@ -import { getRoomByID } from './db'; -import { PrismaClient } from '@prisma/client'; -import { MessageI } from 'discreetly-interfaces'; +import { getRoomByID } from "./db"; +import { PrismaClient } from "@prisma/client"; +import { MessageI } from "discreetly-interfaces"; +import { shamirRecovery } from "../crypto/shamirRecovery"; +import { RLNFullProof } from "rlnjs"; const prisma = new PrismaClient(); +export type StrBigInt = string | bigint; + +export type Proof = { + pi_a: StrBigInt[]; + pi_b: StrBigInt[][]; + pi_c: StrBigInt[]; + protocol: string; + curve: string; +}; + +export type RLNPublicSignals = { + x: StrBigInt; + externalNullifier: StrBigInt; + y: StrBigInt; + root: StrBigInt; + nullifier: StrBigInt; +}; + +export type RLNSNARKProof = { + proof: Proof; + publicSignals: RLNPublicSignals; +}; + +interface CollisionCheckResult { + collision: boolean; + secret?: bigint; + oldMessage?: MessageI; +} + +export async function checkRLNCollision( + roomId: string, + message: MessageI +): Promise { + return new Promise((res, rej) => { + prisma.rooms + .findFirst({ + where: { roomId }, + include: { + epochs: { + where: { epoch: String(message.epoch) }, + include: { + messages: { + where: { messageId: message.messageId }, + }, + }, + }, + }, + }) + .then((oldMessage) => { + if (!message.proof) { + throw new Error("Proof not provided"); + } + if (!oldMessage) { + res({ collision: false } as CollisionCheckResult); + } else { + const oldMessageProof = JSON.parse( + oldMessage.epochs[0].messages[0].proof + ) as RLNFullProof; + const oldMessagex2 = BigInt( + oldMessageProof.snarkProof.publicSignals.x + ); + const oldMessagey2 = BigInt( + oldMessageProof.snarkProof.publicSignals.y + ); + + let proof: RLNFullProof; + + if (typeof message.proof === "string") { + proof = JSON.parse(message.proof) as RLNFullProof; + } else { + proof = message.proof as RLNFullProof; + } + const [x1, y1] = [ + BigInt(proof.snarkProof.publicSignals.x), + BigInt(proof.snarkProof.publicSignals.y), + ]; + const [x2, y2] = [oldMessagex2, oldMessagey2]; + + const secret = shamirRecovery(x1, x2, y1, y2); + + res({ + collision: true, + secret, + oldMessage: oldMessage.epochs[0].messages[0] as MessageI, + } as CollisionCheckResult); + } + }); + }); +} + function addMessageToRoom(roomId: string, message: MessageI): Promise { if (!message.epoch) { - throw new Error('Epoch not provided'); + throw new Error("Epoch not provided"); } return prisma.rooms.update({ where: { - roomId: roomId + roomId: roomId, }, data: { epochs: { @@ -18,15 +110,15 @@ function addMessageToRoom(roomId: string, message: MessageI): Promise { epoch: String(message.epoch), messages: { create: { - message: message.message ? message.message.toString() : '', - messageId: message.messageId ? message.messageId.toString() : '', + message: message.message ? message.message.toString() : "", + messageId: message.messageId ? message.messageId.toString() : "", proof: JSON.stringify(message.proof), - roomId: roomId - } - } - } - } - } + roomId: roomId, + }, + }, + }, + }, + }, }); } @@ -34,7 +126,10 @@ export function createMessage(roomId: string, message: MessageI): boolean { getRoomByID(roomId) .then((room) => { if (room) { - // Todo This should check that there is no duplicate messageId with in this room and epoch, if there is, we need to return an error and reconstruct the secret from both messages, and ban the user + // Todo This should check that there is no duplicate messageId with in this room and epoch, + // if there is, we need to return an error and + // reconstruct the secret from both messages, and ban the user + addMessageToRoom(roomId, message) .then((roomToUpdate) => { console.log(roomToUpdate); @@ -45,7 +140,7 @@ export function createMessage(roomId: string, message: MessageI): boolean { return false; }); } else { - console.log('Room not found'); + console.log("Room not found"); return false; } })