js-rln/src/codec.ts

127 lines
3.2 KiB
TypeScript
Raw Normal View History

import type {
IDecodedMessage,
IDecoder,
IEncoder,
IMessage,
IProtoMessage,
IRateLimitProof,
} from "@waku/interfaces";
2022-09-26 12:08:28 -04:00
import debug from "debug";
2022-09-25 11:40:49 -04:00
2022-10-01 08:02:13 -04:00
import { RlnMessage, toRLNSignal } from "./message.js";
import { IdentityCredential, RLNInstance } from "./rln.js";
2022-09-26 12:08:28 -04:00
const log = debug("waku:rln:encoder");
2022-09-25 11:40:49 -04:00
export class RLNEncoder implements IEncoder {
private readonly idSecretHash: Uint8Array;
2022-09-26 12:08:28 -04:00
2022-09-25 11:40:49 -04:00
constructor(
private encoder: IEncoder,
2022-09-25 11:40:49 -04:00
private rlnInstance: RLNInstance,
private index: number,
identityCredential: IdentityCredential
2022-09-25 11:40:49 -04:00
) {
if (index < 0) throw "invalid membership index";
this.idSecretHash = identityCredential.IDSecretHash;
2022-09-25 11:40:49 -04:00
}
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
message.rateLimitProof = await this.generateProof(message);
2022-10-10 07:22:53 -05:00
log("Proof generated", message.rateLimitProof);
return this.encoder.toWire(message);
2022-09-25 11:40:49 -04:00
}
async toProtoObj(message: IMessage): Promise<IProtoMessage | undefined> {
const protoMessage = await this.encoder.toProtoObj(message);
2022-09-25 11:40:49 -04:00
if (!protoMessage) return;
protoMessage.contentTopic = this.contentTopic;
protoMessage.rateLimitProof = await this.generateProof(message);
2022-10-10 08:50:50 -05:00
log("Proof generated", protoMessage.rateLimitProof);
return protoMessage;
}
private async generateProof(message: IMessage): Promise<IRateLimitProof> {
const signal = toRLNSignal(this.contentTopic, message);
2022-10-01 08:02:13 -04:00
const proof = await this.rlnInstance.generateRLNProof(
2022-09-25 11:40:49 -04:00
signal,
this.index,
message.timestamp,
this.idSecretHash
2022-09-25 11:40:49 -04:00
);
return proof;
2022-09-25 11:40:49 -04:00
}
get pubsubTopic(): string {
return this.encoder.pubsubTopic;
}
get contentTopic(): string {
return this.encoder.contentTopic;
}
get ephemeral(): boolean {
return this.encoder.ephemeral;
}
2022-09-25 11:40:49 -04:00
}
2022-09-26 12:08:28 -04:00
type RLNEncoderOptions = {
encoder: IEncoder;
rlnInstance: RLNInstance;
index: number;
credential: IdentityCredential;
};
export const createRLNEncoder = (options: RLNEncoderOptions): RLNEncoder => {
return new RLNEncoder(
options.encoder,
options.rlnInstance,
options.index,
options.credential
);
};
export class RLNDecoder<T extends IDecodedMessage>
implements IDecoder<RlnMessage<T>>
{
constructor(private rlnInstance: RLNInstance, private decoder: IDecoder<T>) {}
2022-09-26 12:08:28 -04:00
get pubsubTopic(): string {
return this.decoder.pubsubTopic;
}
2022-09-28 14:23:10 +10:00
get contentTopic(): string {
return this.decoder.contentTopic;
2022-09-26 12:08:28 -04:00
}
fromWireToProtoObj(bytes: Uint8Array): Promise<IProtoMessage | undefined> {
const protoMessage = this.decoder.fromWireToProtoObj(bytes);
2022-09-26 12:08:28 -04:00
log("Message decoded", protoMessage);
return Promise.resolve(protoMessage);
}
async fromProtoObj(
pubsubTopic: string,
proto: IProtoMessage
): Promise<RlnMessage<T> | undefined> {
const msg: T | undefined = await this.decoder.fromProtoObj(
pubsubTopic,
proto
);
2022-09-28 14:23:10 +10:00
if (!msg) return;
return new RlnMessage(this.rlnInstance, msg, proto.rateLimitProof);
2022-09-26 12:08:28 -04:00
}
}
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);
};