mirror of
https://github.com/logos-messaging/js-rln.git
synced 2026-01-05 07:03:09 +00:00
Merge pull request #16 from waku-org/feat/encoder
This commit is contained in:
commit
5e8e997f21
5016
package-lock.json
generated
5016
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -81,6 +81,7 @@
|
||||
"husky": "^7.0.4",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"js-waku": "^0.29.0-7714812",
|
||||
"jsdom": "^19.0.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"karma": "^6.3.12",
|
||||
|
||||
46
src/encoder.spec.ts
Normal file
46
src/encoder.spec.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { expect } from "chai";
|
||||
import {
|
||||
DecoderV0,
|
||||
EncoderV0,
|
||||
MessageV0,
|
||||
} from "js-waku/lib/waku_message/version_0";
|
||||
|
||||
import { RLNDecoder, RLNEncoder } from "./encoder.js";
|
||||
|
||||
import * as rln from "./index.js";
|
||||
|
||||
const TestContentTopic = "/test/1/waku-message/utf8";
|
||||
|
||||
describe("js-rln: encoder", () => {
|
||||
it("should attach a proof to a waku message", async function () {
|
||||
const rlnInstance = await rln.create();
|
||||
const memKeys = rlnInstance.generateMembershipKey();
|
||||
const index = 0;
|
||||
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
|
||||
rlnInstance.insertMember(memKeys.IDCommitment);
|
||||
|
||||
const rlnEncoder = new RLNEncoder(
|
||||
new EncoderV0(TestContentTopic),
|
||||
rlnInstance,
|
||||
index,
|
||||
memKeys
|
||||
);
|
||||
const rlnDecoder = new RLNDecoder(new DecoderV0(TestContentTopic));
|
||||
|
||||
const bytes = await rlnEncoder.encode({ payload });
|
||||
const protoResult = await rlnDecoder.decodeProto(bytes!);
|
||||
|
||||
const msg = (await rlnDecoder.decode(protoResult!))!;
|
||||
|
||||
// Validate proof
|
||||
const verifResult = rlnInstance.verifyProof(msg.rateLimitProof!);
|
||||
expect(verifResult).to.be.true;
|
||||
|
||||
const msgV0 = msg as MessageV0;
|
||||
expect(msgV0.contentTopic).to.eq(TestContentTopic);
|
||||
expect(msgV0.version).to.eq(0);
|
||||
expect(msgV0.payload).to.deep.eq(payload);
|
||||
expect(msgV0.timestamp).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
81
src/encoder.ts
Normal file
81
src/encoder.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import debug from "debug";
|
||||
import {proto_message, utils} from "js-waku";
|
||||
import {
|
||||
Decoder,
|
||||
Encoder,
|
||||
Message,
|
||||
ProtoMessage,
|
||||
} from "js-waku/lib/interfaces";
|
||||
|
||||
import { MembershipKey, RLNInstance } from "./rln.js";
|
||||
|
||||
const log = debug("waku:message:rln-encoder");
|
||||
|
||||
export class RLNEncoder implements Encoder {
|
||||
public contentTopic: string;
|
||||
private readonly idKey: Uint8Array;
|
||||
|
||||
constructor(
|
||||
private encoder: Encoder,
|
||||
private rlnInstance: RLNInstance,
|
||||
private index: number,
|
||||
membershipKey: MembershipKey
|
||||
) {
|
||||
if (index < 0) throw "invalid membership index";
|
||||
this.idKey = membershipKey.IDKey;
|
||||
this.contentTopic = encoder.contentTopic;
|
||||
}
|
||||
|
||||
async encode(message: Message): Promise<Uint8Array | undefined> {
|
||||
const protoMessage = await this.encodeProto(message);
|
||||
if (!protoMessage) return;
|
||||
return proto_message.WakuMessage.encode(protoMessage);
|
||||
}
|
||||
|
||||
async encodeProto(message: Message): Promise<ProtoMessage | undefined> {
|
||||
const protoMessage = await this.encoder.encodeProto(message);
|
||||
if (!protoMessage) return;
|
||||
|
||||
const signal = toRLNSignal(message);
|
||||
|
||||
console.time("proof_gen_timer");
|
||||
const proof = await this.rlnInstance.generateProof(
|
||||
signal,
|
||||
this.index,
|
||||
message.timestamp,
|
||||
this.idKey
|
||||
);
|
||||
console.timeEnd("proof_gen_timer");
|
||||
|
||||
protoMessage.rateLimitProof = proof;
|
||||
|
||||
return protoMessage;
|
||||
}
|
||||
}
|
||||
|
||||
export class RLNDecoder implements Decoder<Message> {
|
||||
public contentTopic: string;
|
||||
|
||||
constructor(private decoder: Decoder<Message>) {
|
||||
this.contentTopic = decoder.contentTopic;
|
||||
}
|
||||
|
||||
decodeProto(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
|
||||
const protoMessage = proto_message.WakuMessage.decode(bytes);
|
||||
log("Message decoded", protoMessage);
|
||||
return Promise.resolve(protoMessage);
|
||||
}
|
||||
|
||||
async decode(proto: ProtoMessage): Promise<Message | undefined> {
|
||||
const msg = await this.decoder.decode(proto);
|
||||
if (msg) {
|
||||
msg.rateLimitProof = proto.rateLimitProof;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
function toRLNSignal(msg: Message): Uint8Array {
|
||||
const contentTopicBytes = utils.utf8ToBytes(msg.contentTopic ?? "")
|
||||
return new Uint8Array([...(msg.payload ?? []), ...contentTopicBytes]);
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { assert, expect } from "chai";
|
||||
|
||||
import * as rln from "./index";
|
||||
import * as rln from "./index.js";
|
||||
|
||||
describe("js-rln", () => {
|
||||
it("should verify a proof", async function () {
|
||||
@ -50,11 +50,13 @@ describe("js-rln", () => {
|
||||
|
||||
try {
|
||||
// Modifying the proof so it's invalid
|
||||
const proofBytes = proof.toBytes();
|
||||
proofBytes[7] = Math.floor(Math.random() * 256) % 255;
|
||||
proof.proof[0] = 0;
|
||||
proof.proof[1] = 1;
|
||||
proof.proof[2] = 2;
|
||||
proof.proof[3] = 3;
|
||||
|
||||
// verify the proof
|
||||
const verifResult = rlnInstance.verifyProof(proofBytes);
|
||||
const verifResult = rlnInstance.verifyProof(proof);
|
||||
expect(verifResult).to.be.false;
|
||||
} catch (err) {
|
||||
//
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { MembershipKey, RateLimitProof, RLNInstance } from "./rln.js";
|
||||
import type { MembershipKey, Proof, RLNInstance } from "./rln.js";
|
||||
|
||||
// reexport the create function, dynamically imported from rln.ts
|
||||
export async function create(): Promise<RLNInstance> {
|
||||
@ -9,4 +9,4 @@ export async function create(): Promise<RLNInstance> {
|
||||
return await rlnModule.create();
|
||||
}
|
||||
|
||||
export { RLNInstance, MembershipKey, RateLimitProof };
|
||||
export { RLNInstance, MembershipKey, Proof };
|
||||
|
||||
36
src/rln.ts
36
src/rln.ts
@ -1,4 +1,5 @@
|
||||
import init, * as zerokitRLN from "@waku/zerokit-rln-wasm";
|
||||
import { RateLimitProof } from "js-waku/lib/interfaces";
|
||||
|
||||
import verificationKey from "./resources/verification_key.js";
|
||||
import * as wc from "./witness_calculator.js";
|
||||
@ -120,7 +121,7 @@ const shareYOffset = shareXOffset + 32;
|
||||
const nullifierOffset = shareYOffset + 32;
|
||||
const rlnIdentifierOffset = nullifierOffset + 32;
|
||||
|
||||
export class RateLimitProof {
|
||||
export class Proof implements RateLimitProof {
|
||||
readonly proof: Uint8Array;
|
||||
readonly merkleRoot: Uint8Array;
|
||||
readonly epoch: Uint8Array;
|
||||
@ -143,18 +144,18 @@ export class RateLimitProof {
|
||||
rlnIdentifierOffset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
toBytes(): Uint8Array {
|
||||
return concatenate(
|
||||
this.proof,
|
||||
this.merkleRoot,
|
||||
this.epoch,
|
||||
this.shareX,
|
||||
this.shareY,
|
||||
this.nullifier,
|
||||
this.rlnIdentifier
|
||||
);
|
||||
}
|
||||
function proofToBytes(p: RateLimitProof): Uint8Array {
|
||||
return concatenate(
|
||||
p.proof,
|
||||
p.merkleRoot,
|
||||
p.epoch,
|
||||
p.shareX,
|
||||
p.shareY,
|
||||
p.nullifier,
|
||||
p.rlnIdentifier
|
||||
);
|
||||
}
|
||||
|
||||
export class RLNInstance {
|
||||
@ -218,13 +219,16 @@ export class RLNInstance {
|
||||
rlnWitness
|
||||
);
|
||||
|
||||
return new RateLimitProof(proofBytes);
|
||||
return new Proof(proofBytes);
|
||||
}
|
||||
|
||||
verifyProof(proof: RateLimitProof | Uint8Array): boolean {
|
||||
if (proof instanceof RateLimitProof) {
|
||||
proof = proof.toBytes();
|
||||
let pBytes: Uint8Array;
|
||||
if (proof instanceof Uint8Array) {
|
||||
pBytes = proof;
|
||||
} else {
|
||||
pBytes = proofToBytes(proof);
|
||||
}
|
||||
return zerokitRLN.verifyProof(this.zkRLN, proof);
|
||||
return zerokitRLN.verifyProof(this.zkRLN, pBytes);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user