Merge pull request #26 from waku-org/proof-attached

This commit is contained in:
fryorcraken.eth 2022-09-30 14:47:57 +10:00 committed by GitHub
commit 378ad04c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 266 additions and 32 deletions

14
package-lock.json generated
View File

@ -41,7 +41,7 @@
"husky": "^7.0.4",
"ignore-loader": "^0.1.2",
"isomorphic-fetch": "^3.0.0",
"js-waku": "^0.29.0-7714812",
"js-waku": "^0.29.0-29436ea",
"jsdom": "^19.0.0",
"jsdom-global": "^3.0.2",
"karma": "^6.3.12",
@ -6648,9 +6648,9 @@
"license": "MIT"
},
"node_modules/js-waku": {
"version": "0.29.0-7714812",
"resolved": "https://registry.npmjs.org/js-waku/-/js-waku-0.29.0-7714812.tgz",
"integrity": "sha512-pHX7cvrLC8Yw0KxTa8KJlFcE9S5WLkBWqd0pWqBWGCG5mU1Q6LWBZv+rOYuhn9wkCCpjF5jWJvmTohzkGt91XA==",
"version": "0.29.0-29436ea",
"resolved": "https://registry.npmjs.org/js-waku/-/js-waku-0.29.0-29436ea.tgz",
"integrity": "sha512-I2rbC4X0Ho5jnLQp+5V7jqVBk0OY11+mR8zh2HFbPo3m3ifqYaASeMmdE7fFQ/LY0uUFx1qphJq/0X+agD91wA==",
"dev": true,
"dependencies": {
"@chainsafe/libp2p-gossipsub": "^4.1.1",
@ -15734,9 +15734,9 @@
"dev": true
},
"js-waku": {
"version": "0.29.0-7714812",
"resolved": "https://registry.npmjs.org/js-waku/-/js-waku-0.29.0-7714812.tgz",
"integrity": "sha512-pHX7cvrLC8Yw0KxTa8KJlFcE9S5WLkBWqd0pWqBWGCG5mU1Q6LWBZv+rOYuhn9wkCCpjF5jWJvmTohzkGt91XA==",
"version": "0.29.0-29436ea",
"resolved": "https://registry.npmjs.org/js-waku/-/js-waku-0.29.0-29436ea.tgz",
"integrity": "sha512-I2rbC4X0Ho5jnLQp+5V7jqVBk0OY11+mR8zh2HFbPo3m3ifqYaASeMmdE7fFQ/LY0uUFx1qphJq/0X+agD91wA==",
"dev": true,
"requires": {
"@chainsafe/libp2p-gossipsub": "^4.1.1",

View File

@ -81,7 +81,7 @@
"husky": "^7.0.4",
"ignore-loader": "^0.1.2",
"isomorphic-fetch": "^3.0.0",
"js-waku": "^0.29.0-7714812",
"js-waku": "^0.29.0-29436ea",
"jsdom": "^19.0.0",
"jsdom-global": "^3.0.2",
"karma": "^6.3.12",

View File

@ -1,14 +1,30 @@
import { expect } from "chai";
import { DecoderV0, EncoderV0 } from "js-waku/lib/waku_message/version_0";
import {
generatePrivateKey,
generateSymmetricKey,
getPublicKey,
} from "js-waku";
import {
DecoderV0,
EncoderV0,
MessageV0,
} from "js-waku/lib/waku_message/version_0";
import {
AsymDecoder,
AsymEncoder,
SymDecoder,
SymEncoder,
} from "js-waku/lib/waku_message/version_1";
import { RLNDecoder, RLNEncoder } from "./codec.js";
import { RlnMessage } from "./message.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 () {
describe("RLN codec with version 0", () => {
it("toWire", async function () {
const rlnInstance = await rln.create();
const memKeys = rlnInstance.generateMembershipKey();
const index = 0;
@ -27,13 +43,57 @@ describe("js-rln: encoder", () => {
new DecoderV0(TestContentTopic)
);
const bytes = await rlnEncoder.encode({ payload });
const protoResult = await rlnDecoder.decodeProto(bytes!);
const bytes = await rlnEncoder.toWire({ payload });
const msg = (await rlnDecoder.decode(protoResult!))!;
expect(bytes).to.not.be.undefined;
const protoResult = await rlnDecoder.fromWireToProtoObj(bytes!);
// Validate proof
expect(protoResult).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(protoResult!))!;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
expect(msg.epoch).to.not.be.undefined;
expect(msg.epoch).to.be.gt(0);
expect(msg.contentTopic).to.eq(TestContentTopic);
expect(msg.msg.version).to.eq(0);
expect(msg.payload).to.deep.eq(payload);
expect(msg.timestamp).to.not.be.undefined;
});
it("toProtoObj", 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(
rlnInstance,
new DecoderV0(TestContentTopic)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
proto!
)) as RlnMessage<MessageV0>;
expect(msg).to.not.be.undefined;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
expect(msg.epoch).to.not.be.undefined;
expect(msg.epoch).to.be.gt(0);
expect(msg.contentTopic).to.eq(TestContentTopic);
expect(msg.msg.version).to.eq(0);
@ -41,3 +101,169 @@ describe("js-rln: encoder", () => {
expect(msg.timestamp).to.not.be.undefined;
});
});
describe("RLN codec with version 1", () => {
it("Symmetric, toWire", 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 symKey = generateSymmetricKey();
const rlnEncoder = new RLNEncoder(
new SymEncoder(TestContentTopic, symKey),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new SymDecoder(TestContentTopic, symKey)
);
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!))!;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
expect(msg.epoch).to.not.be.undefined;
expect(msg.epoch).to.be.gt(0);
expect(msg.contentTopic).to.eq(TestContentTopic);
expect(msg.msg.version).to.eq(1);
expect(msg.payload).to.deep.eq(payload);
expect(msg.timestamp).to.not.be.undefined;
});
it("Symmetric, toProtoObj", 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 symKey = generateSymmetricKey();
const rlnEncoder = new RLNEncoder(
new SymEncoder(TestContentTopic, symKey),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new SymDecoder(TestContentTopic, symKey)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
proto!
)) as RlnMessage<MessageV0>;
expect(msg).to.not.be.undefined;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
expect(msg.epoch).to.not.be.undefined;
expect(msg.epoch).to.be.gt(0);
expect(msg.contentTopic).to.eq(TestContentTopic);
expect(msg.msg.version).to.eq(1);
expect(msg.payload).to.deep.eq(payload);
expect(msg.timestamp).to.not.be.undefined;
});
it("Asymmetric, toWire", 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 privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
const rlnEncoder = new RLNEncoder(
new AsymEncoder(TestContentTopic, publicKey),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new AsymDecoder(TestContentTopic, privateKey)
);
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!))!;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
expect(msg.epoch).to.not.be.undefined;
expect(msg.epoch).to.be.gt(0);
expect(msg.contentTopic).to.eq(TestContentTopic);
expect(msg.msg.version).to.eq(1);
expect(msg.payload).to.deep.eq(payload);
expect(msg.timestamp).to.not.be.undefined;
});
it("Asymmetric, toProtoObj", 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 privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
const rlnEncoder = new RLNEncoder(
new AsymEncoder(TestContentTopic, publicKey),
rlnInstance,
index,
memKeys
);
const rlnDecoder = new RLNDecoder(
rlnInstance,
new AsymDecoder(TestContentTopic, privateKey)
);
const proto = await rlnEncoder.toProtoObj({ payload });
expect(proto).to.not.be.undefined;
const msg = (await rlnDecoder.fromProtoObj(
proto!
)) as RlnMessage<MessageV0>;
expect(msg).to.not.be.undefined;
expect(msg.rateLimitProof).to.not.be.undefined;
expect(msg.verify()).to.be.true;
expect(msg.epoch).to.not.be.undefined;
expect(msg.epoch).to.be.gt(0);
expect(msg.contentTopic).to.eq(TestContentTopic);
expect(msg.msg.version).to.eq(1);
expect(msg.payload).to.deep.eq(payload);
expect(msg.timestamp).to.not.be.undefined;
});
});

View File

@ -1,10 +1,11 @@
import debug from "debug";
import { proto_message, utils } from "js-waku";
import { utils } from "js-waku";
import {
Decoder,
Encoder,
Message,
ProtoMessage,
RateLimitProof,
} from "js-waku/lib/interfaces";
import { RlnMessage } from "./message.js";
@ -27,16 +28,26 @@ export class RLNEncoder implements Encoder {
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 toWire(message: Partial<Message>): Promise<Uint8Array | undefined> {
message.rateLimitProof = await this.generateProof(message);
return this.encoder.toWire(message);
}
async encodeProto(message: Message): Promise<ProtoMessage | undefined> {
const protoMessage = await this.encoder.encodeProto(message);
async toProtoObj(
message: Partial<Message>
): Promise<ProtoMessage | undefined> {
const protoMessage = await this.encoder.toProtoObj(message);
if (!protoMessage) return;
protoMessage.rateLimitProof = await this.generateProof(message);
return protoMessage;
}
private async generateProof(
message: Partial<Message>
): Promise<RateLimitProof> {
const signal = toRLNSignal(message);
console.time("proof_gen_timer");
@ -47,10 +58,7 @@ export class RLNEncoder implements Encoder {
this.idKey
);
console.timeEnd("proof_gen_timer");
protoMessage.rateLimitProof = proof;
return protoMessage;
return proof;
}
}
@ -61,20 +69,20 @@ export class RLNDecoder<T extends Message> implements Decoder<RlnMessage<T>> {
return this.decoder.contentTopic;
}
decodeProto(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
const protoMessage = proto_message.WakuMessage.decode(bytes);
fromWireToProtoObj(bytes: Uint8Array): Promise<ProtoMessage | undefined> {
const protoMessage = this.decoder.fromWireToProtoObj(bytes);
log("Message decoded", protoMessage);
return Promise.resolve(protoMessage);
}
async decode(proto: ProtoMessage): Promise<RlnMessage<T> | undefined> {
const msg: T | undefined = await this.decoder.decode(proto);
async fromProtoObj(proto: ProtoMessage): Promise<RlnMessage<T> | undefined> {
const msg: T | undefined = await this.decoder.fromProtoObj(proto);
if (!msg) return;
return new RlnMessage(this.rlnInstance, msg, proto.rateLimitProof);
}
}
function toRLNSignal(msg: Message): Uint8Array {
function toRLNSignal(msg: Partial<Message>): Uint8Array {
const contentTopicBytes = utils.utf8ToBytes(msg.contentTopic ?? "");
return new Uint8Array([...(msg.payload ?? []), ...contentTopicBytes]);
}

View File

@ -7,7 +7,7 @@ export class RlnMessage<T extends Message> implements Message {
constructor(
public rlnInstance: RLNInstance,
public msg: T,
public rateLimitProof?: RateLimitProof
public rateLimitProof: RateLimitProof | undefined
) {}
public verify(): boolean | undefined {