chore!: bump to new @waku (#55)

* move to newer version

* fix import

* add content topic on the message

* update types

* address comments

* setup getter
This commit is contained in:
Sasha 2023-04-19 20:02:13 +02:00 committed by GitHub
parent fae4bea48d
commit 360dc829f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 2173 additions and 2605 deletions

4523
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@waku/rln",
"version": "0.0.13",
"version": "0.0.14",
"description": "Rate Limit Nullifier for js-waku",
"types": "./dist/index.d.ts",
"module": "./dist/index.js",
@ -83,7 +83,9 @@
"husky": "^7.0.4",
"ignore-loader": "^0.1.2",
"isomorphic-fetch": "^3.0.0",
"js-waku": "^0.29.0-29436ea",
"@waku/interfaces": "^0.0.11",
"@waku/message-encryption": "^0.0.14",
"@waku/core": "^0.0.16",
"jsdom": "^19.0.0",
"jsdom-global": "^3.0.2",
"karma": "^6.3.12",
@ -127,6 +129,7 @@
]
},
"dependencies": {
"@waku/utils": "^0.0.4",
"@waku/zerokit-rln-wasm": "^0.0.5",
"ethers": "^5.7.2"
}

View File

@ -1,28 +1,36 @@
import { expect } from "chai";
import {
createDecoder,
createEncoder,
DecodedMessage,
} from "@waku/core/lib/message/version_0";
import {
generatePrivateKey,
generateSymmetricKey,
getPublicKey,
} from "js-waku";
} from "@waku/message-encryption";
import {
DecoderV0,
EncoderV0,
MessageV0,
} from "js-waku/lib/waku_message/version_0";
createDecoder as createAsymDecoder,
createEncoder as createAsymEncoder,
} from "@waku/message-encryption/ecies";
import {
AsymDecoder,
AsymEncoder,
SymDecoder,
SymEncoder,
} from "js-waku/lib/waku_message/version_1";
createDecoder as createSymDecoder,
createEncoder as createSymEncoder,
} from "@waku/message-encryption/symmetric";
import { expect } from "chai";
import { RLNDecoder, RLNEncoder } from "./codec.js";
import {
createRLNDecoder,
createRLNEncoder,
RLNDecoder,
RLNEncoder,
} from "./codec.js";
import { epochBytesToInt } from "./epoch.js";
import { RlnMessage } from "./message.js";
import * as rln from "./index.js";
const TestContentTopic = "/test/1/waku-message/utf8";
const EMPTY_PUBSUB_TOPIC = "";
describe("RLN codec with version 0", () => {
it("toWire", async function () {
@ -33,23 +41,26 @@ describe("RLN codec with version 0", () => {
rlnInstance.insertMember(memKeys.IDCommitment);
const rlnEncoder = new RLNEncoder(
new EncoderV0(TestContentTopic),
const rlnEncoder = createRLNEncoder({
encoder: createEncoder({ contentTopic: TestContentTopic }),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
membershipKey: memKeys,
});
const rlnDecoder = createRLNDecoder({
rlnInstance,
new DecoderV0(TestContentTopic)
);
decoder: createDecoder(TestContentTopic),
});
const bytes = await rlnEncoder.toWire({ payload });
expect(bytes).to.not.be.undefined;
const protoResult = await rlnDecoder.fromWireToProtoObj(bytes!);
expect(protoResult).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(protoResult!))!;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
protoResult!
))!;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
@ -72,22 +83,23 @@ describe("RLN codec with version 0", () => {
rlnInstance.insertMember(memKeys.IDCommitment);
const rlnEncoder = new RLNEncoder(
new EncoderV0(TestContentTopic),
createEncoder({ contentTopic: TestContentTopic }),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new DecoderV0(TestContentTopic)
createDecoder(TestContentTopic)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
proto!
)) as RlnMessage<MessageV0>;
)) as RlnMessage<DecodedMessage>;
expect(msg).to.not.be.undefined;
expect(msg.rateLimitProof).to.not.be.undefined;
@ -116,14 +128,17 @@ describe("RLN codec with version 1", () => {
const symKey = generateSymmetricKey();
const rlnEncoder = new RLNEncoder(
new SymEncoder(TestContentTopic, symKey),
createSymEncoder({
contentTopic: TestContentTopic,
symKey,
}),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new SymDecoder(TestContentTopic, symKey)
createSymDecoder(TestContentTopic, symKey)
);
const bytes = await rlnEncoder.toWire({ payload });
@ -132,7 +147,10 @@ describe("RLN codec with version 1", () => {
const protoResult = await rlnDecoder.fromWireToProtoObj(bytes!);
expect(protoResult).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(protoResult!))!;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
protoResult!
))!;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
@ -157,22 +175,26 @@ describe("RLN codec with version 1", () => {
const symKey = generateSymmetricKey();
const rlnEncoder = new RLNEncoder(
new SymEncoder(TestContentTopic, symKey),
createSymEncoder({
contentTopic: TestContentTopic,
symKey,
}),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new SymDecoder(TestContentTopic, symKey)
createSymDecoder(TestContentTopic, symKey)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
proto!
)) as RlnMessage<MessageV0>;
)) as RlnMessage<DecodedMessage>;
expect(msg).to.not.be.undefined;
expect(msg.rateLimitProof).to.not.be.undefined;
@ -200,14 +222,17 @@ describe("RLN codec with version 1", () => {
const publicKey = getPublicKey(privateKey);
const rlnEncoder = new RLNEncoder(
new AsymEncoder(TestContentTopic, publicKey),
createAsymEncoder({
contentTopic: TestContentTopic,
publicKey,
}),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new AsymDecoder(TestContentTopic, privateKey)
createAsymDecoder(TestContentTopic, privateKey)
);
const bytes = await rlnEncoder.toWire({ payload });
@ -216,7 +241,10 @@ describe("RLN codec with version 1", () => {
const protoResult = await rlnDecoder.fromWireToProtoObj(bytes!);
expect(protoResult).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(protoResult!))!;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
protoResult!
))!;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
@ -242,22 +270,26 @@ describe("RLN codec with version 1", () => {
const publicKey = getPublicKey(privateKey);
const rlnEncoder = new RLNEncoder(
new AsymEncoder(TestContentTopic, publicKey),
createAsymEncoder({
contentTopic: TestContentTopic,
publicKey,
}),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new AsymDecoder(TestContentTopic, privateKey)
createAsymDecoder(TestContentTopic, privateKey)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
proto!
)) as RlnMessage<MessageV0>;
)) as RlnMessage<DecodedMessage>;
expect(msg).to.not.be.undefined;
expect(msg.rateLimitProof).to.not.be.undefined;
@ -284,22 +316,23 @@ describe("RLN Codec - epoch", () => {
rlnInstance.insertMember(memKeys.IDCommitment);
const rlnEncoder = new RLNEncoder(
new EncoderV0(TestContentTopic),
createEncoder({ contentTopic: TestContentTopic }),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new DecoderV0(TestContentTopic)
createDecoder(TestContentTopic)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
EMPTY_PUBSUB_TOPIC,
proto!
)) as RlnMessage<MessageV0>;
)) as RlnMessage<DecodedMessage>;
const epochBytes = proto!.rateLimitProof!.epoch;
const epoch = epochBytesToInt(epochBytes);

View File

@ -1,55 +1,49 @@
import type {
IDecodedMessage,
IDecoder,
IEncoder,
IMessage,
IProtoMessage,
IRateLimitProof,
} from "@waku/interfaces";
import debug from "debug";
import {
Decoder,
Encoder,
Message,
ProtoMessage,
RateLimitProof,
} from "js-waku/lib/interfaces";
import { RlnMessage, toRLNSignal } from "./message.js";
import { MembershipKey, RLNInstance } from "./rln.js";
const log = debug("waku:rln:encoder");
export class RLNEncoder implements Encoder {
public contentTopic: string;
export class RLNEncoder implements IEncoder {
private readonly idKey: Uint8Array;
constructor(
private encoder: Encoder,
private encoder: IEncoder,
private rlnInstance: RLNInstance,
private index: number,
membershipKey: MembershipKey
) {
if (index < 0) throw "invalid membership index";
this.idKey = membershipKey.IDKey;
this.contentTopic = encoder.contentTopic;
}
async toWire(message: Partial<Message>): Promise<Uint8Array | undefined> {
message.contentTopic = this.contentTopic;
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
message.rateLimitProof = await this.generateProof(message);
log("Proof generated", message.rateLimitProof);
return this.encoder.toWire(message);
}
async toProtoObj(
message: Partial<Message>
): Promise<ProtoMessage | undefined> {
message.contentTopic = this.contentTopic;
async toProtoObj(message: IMessage): Promise<IProtoMessage | undefined> {
const protoMessage = await this.encoder.toProtoObj(message);
if (!protoMessage) return;
protoMessage.contentTopic = this.contentTopic;
protoMessage.rateLimitProof = await this.generateProof(message);
log("Proof generated", protoMessage.rateLimitProof);
return protoMessage;
}
private async generateProof(
message: Partial<Message>
): Promise<RateLimitProof> {
const signal = toRLNSignal(message);
private async generateProof(message: IMessage): Promise<IRateLimitProof> {
const signal = toRLNSignal(this.contentTopic, message);
console.time("proof_gen_timer");
const proof = await this.rlnInstance.generateRLNProof(
@ -61,24 +55,67 @@ export class RLNEncoder implements Encoder {
console.timeEnd("proof_gen_timer");
return proof;
}
get contentTopic(): string {
return this.encoder.contentTopic;
}
get ephemeral(): boolean {
return this.encoder.ephemeral;
}
}
export class RLNDecoder<T extends Message> implements Decoder<RlnMessage<T>> {
constructor(private rlnInstance: RLNInstance, private decoder: Decoder<T>) {}
type RLNEncoderOptions = {
encoder: IEncoder;
rlnInstance: RLNInstance;
index: number;
membershipKey: MembershipKey;
};
export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
return new RLNEncoder(
options.encoder,
options.rlnInstance,
options.index,
options.membershipKey
);
};
export class RLNDecoder<T extends IDecodedMessage>
implements IDecoder<RlnMessage<T>>
{
constructor(private rlnInstance: RLNInstance, private decoder: IDecoder<T>) {}
get contentTopic(): string {
return this.decoder.contentTopic;
}
fromWireToProtoObj(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
fromWireToProtoObj(bytes: Uint8Array): Promise<IProtoMessage | undefined> {
const protoMessage = this.decoder.fromWireToProtoObj(bytes);
log("Message decoded", protoMessage);
return Promise.resolve(protoMessage);
}
async fromProtoObj(proto: ProtoMessage): Promise<RlnMessage<T> | undefined> {
const msg: T | undefined = await this.decoder.fromProtoObj(proto);
async fromProtoObj(
pubSubTopic: string,
proto: IProtoMessage
): Promise<RlnMessage<T> | undefined> {
const msg: T | undefined = await this.decoder.fromProtoObj(
pubSubTopic,
proto
);
if (!msg) return;
return new RlnMessage(this.rlnInstance, msg, proto.rateLimitProof);
}
}
type RLNDecoderOptions<T extends IDecodedMessage> = {
decoder: IDecoder<T>;
rlnInstance: RLNInstance;
};
export const createRLNDecoder = <T extends IDecodedMessage>(
options: RLNDecoderOptions<T>
): RLNDecoder<T> => {
return new RLNDecoder(options.rlnInstance, options.decoder);
};

View File

@ -1,24 +1,33 @@
import { utils } from "js-waku";
import { Message, RateLimitProof } from "js-waku/lib/interfaces";
import type {
IDecodedMessage,
IMessage,
IRateLimitProof,
} from "@waku/interfaces";
import * as utils from "@waku/utils/bytes";
import { epochBytesToInt } from "./epoch.js";
import { RLNInstance } from "./rln.js";
export function toRLNSignal(msg: Partial<Message>): Uint8Array {
const contentTopicBytes = utils.utf8ToBytes(msg.contentTopic ?? "");
export function toRLNSignal(contentTopic: string, msg: IMessage): Uint8Array {
const contentTopicBytes = utils.utf8ToBytes(contentTopic ?? "");
return new Uint8Array([...(msg.payload ?? []), ...contentTopicBytes]);
}
export class RlnMessage<T extends Message> implements Message {
export class RlnMessage<T extends IDecodedMessage> implements IDecodedMessage {
public pubSubTopic = "";
constructor(
public rlnInstance: RLNInstance,
public msg: T,
public rateLimitProof: RateLimitProof | undefined
public rateLimitProof: IRateLimitProof | undefined
) {}
public verify(): boolean | undefined {
return this.rateLimitProof
? this.rlnInstance.verifyWithRoots(this.rateLimitProof, toRLNSignal(this)) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed
? this.rlnInstance.verifyWithRoots(
this.rateLimitProof,
toRLNSignal(this.msg.contentTopic, this.msg)
) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed
: undefined;
}
@ -26,16 +35,16 @@ export class RlnMessage<T extends Message> implements Message {
return this.rateLimitProof
? this.rlnInstance.verifyWithNoRoot(
this.rateLimitProof,
toRLNSignal(this)
toRLNSignal(this.msg.contentTopic, this.msg)
) // this.rlnInstance.verifyRLNProof once issue status-im/nwaku#1248 is fixed
: undefined;
}
get payload(): Uint8Array | undefined {
get payload(): Uint8Array {
return this.msg.payload;
}
get contentTopic(): string | undefined {
get contentTopic(): string {
return this.msg.contentTopic;
}
@ -43,6 +52,10 @@ export class RlnMessage<T extends Message> implements Message {
return this.msg.timestamp;
}
get ephemeral(): boolean | undefined {
return this.msg.ephemeral;
}
get epoch(): number | undefined {
const bytes = this.msg.rateLimitProof?.epoch;
if (!bytes) return;

View File

@ -1,5 +1,5 @@
import type { IRateLimitProof } from "@waku/interfaces";
import init, * as zerokitRLN from "@waku/zerokit-rln-wasm";
import { RateLimitProof } from "js-waku/lib/interfaces";
import { writeUIntLE } from "./byte_utils.js";
import { dateToEpoch, epochIntToBytes } from "./epoch.js";
@ -89,7 +89,7 @@ const shareYOffset = shareXOffset + 32;
const nullifierOffset = shareYOffset + 32;
const rlnIdentifierOffset = nullifierOffset + 32;
export class Proof implements RateLimitProof {
export class Proof implements IRateLimitProof {
readonly proof: Uint8Array;
readonly merkleRoot: Uint8Array;
readonly epoch: Uint8Array;
@ -114,7 +114,7 @@ export class Proof implements RateLimitProof {
}
}
export function proofToBytes(p: RateLimitProof): Uint8Array {
export function proofToBytes(p: IRateLimitProof): Uint8Array {
return concatenate(
p.proof,
p.merkleRoot,
@ -175,7 +175,7 @@ export class RLNInstance {
index: number,
epoch: Uint8Array | Date | undefined,
idKey: Uint8Array
): Promise<RateLimitProof> {
): Promise<IRateLimitProof> {
if (epoch == undefined) {
epoch = epochIntToBytes(dateToEpoch(new Date()));
} else if (epoch instanceof Date) {
@ -206,7 +206,10 @@ export class RLNInstance {
return new Proof(proofBytes);
}
verifyRLNProof(proof: RateLimitProof | Uint8Array, msg: Uint8Array): boolean {
verifyRLNProof(
proof: IRateLimitProof | Uint8Array,
msg: Uint8Array
): boolean {
let pBytes: Uint8Array;
if (proof instanceof Uint8Array) {
pBytes = proof;
@ -224,7 +227,7 @@ export class RLNInstance {
}
verifyWithRoots(
proof: RateLimitProof | Uint8Array,
proof: IRateLimitProof | Uint8Array,
msg: Uint8Array
): boolean {
let pBytes: Uint8Array;
@ -248,7 +251,7 @@ export class RLNInstance {
}
verifyWithNoRoot(
proof: RateLimitProof | Uint8Array,
proof: IRateLimitProof | Uint8Array,
msg: Uint8Array
): boolean {
let pBytes: Uint8Array;