UNPKG

@waku/rln

Version:

RLN (Rate Limiting Nullifier) implementation for Waku

129 lines (126 loc) 6.02 kB
import { generateExtendedMembershipKey, generateSeededExtendedMembershipKey, insertMember, setLeavesFrom, deleteLeaf, getRoot, getSerializedRLNWitness, RLNWitnessToJson, generate_rln_proof_with_witness, verifyRLNProof, verifyWithRoots } from '../../../node_modules/@waku/zerokit-rln-wasm/rln_wasm.js'; import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './contract/constants.js'; import { IdentityCredential } from './identity.js'; import { Proof, proofToBytes } from './proof.js'; import { BytesUtils } from './utils/bytes.js'; import { epochIntToBytes, dateToEpoch } from './utils/epoch.js'; class Zerokit { zkRLN; witnessCalculator; _rateLimit; constructor(zkRLN, witnessCalculator, _rateLimit = DEFAULT_RATE_LIMIT) { this.zkRLN = zkRLN; this.witnessCalculator = witnessCalculator; this._rateLimit = _rateLimit; } get getZkRLN() { return this.zkRLN; } get getWitnessCalculator() { return this.witnessCalculator; } get rateLimit() { return this._rateLimit; } generateIdentityCredentials() { const memKeys = generateExtendedMembershipKey(this.zkRLN); // TODO: rename this function in zerokit rln-wasm return IdentityCredential.fromBytes(memKeys); } generateSeededIdentityCredential(seed) { const stringEncoder = new TextEncoder(); const seedBytes = stringEncoder.encode(seed); // TODO: rename this function in zerokit rln-wasm const memKeys = generateSeededExtendedMembershipKey(this.zkRLN, seedBytes); return IdentityCredential.fromBytes(memKeys); } insertMember(idCommitment) { insertMember(this.zkRLN, idCommitment); } insertMembers(index, ...idCommitments) { // serializes a seq of IDCommitments to a byte seq // the order of serialization is |id_commitment_len<8>|id_commitment<var>| const idCommitmentLen = BytesUtils.writeUIntLE(new Uint8Array(8), idCommitments.length, 0, 8); const idCommitmentBytes = BytesUtils.concatenate(idCommitmentLen, ...idCommitments); setLeavesFrom(this.zkRLN, index, idCommitmentBytes); } deleteMember(index) { deleteLeaf(this.zkRLN, index); } getMerkleRoot() { return getRoot(this.zkRLN); } serializeMessage(uint8Msg, memIndex, epoch, idKey, rateLimit) { // calculate message length const msgLen = BytesUtils.writeUIntLE(new Uint8Array(8), uint8Msg.length, 0, 8); const memIndexBytes = BytesUtils.writeUIntLE(new Uint8Array(8), memIndex, 0, 8); const rateLimitBytes = BytesUtils.writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8); // [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> | rate_limit<8> ] return BytesUtils.concatenate(idKey, memIndexBytes, epoch, msgLen, uint8Msg, rateLimitBytes); } async generateRLNProof(msg, index, epoch, idSecretHash, rateLimit) { if (epoch === undefined) { epoch = epochIntToBytes(dateToEpoch(new Date())); } else if (epoch instanceof Date) { epoch = epochIntToBytes(dateToEpoch(epoch)); } const effectiveRateLimit = rateLimit ?? this.rateLimit; if (epoch.length !== 32) throw new Error("invalid epoch"); if (idSecretHash.length !== 32) throw new Error("invalid id secret hash"); if (index < 0) throw new Error("index must be >= 0"); if (effectiveRateLimit < RATE_LIMIT_PARAMS.MIN_RATE || effectiveRateLimit > RATE_LIMIT_PARAMS.MAX_RATE) { throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`); } const serialized_msg = this.serializeMessage(msg, index, epoch, idSecretHash, effectiveRateLimit); const rlnWitness = getSerializedRLNWitness(this.zkRLN, serialized_msg); const inputs = RLNWitnessToJson(this.zkRLN, rlnWitness); const calculatedWitness = await this.witnessCalculator.calculateWitness(inputs, false); const proofBytes = generate_rln_proof_with_witness(this.zkRLN, calculatedWitness, rlnWitness); return new Proof(proofBytes); } verifyRLNProof(proof, msg, rateLimit) { let pBytes; if (proof instanceof Uint8Array) { pBytes = proof; } else { pBytes = proofToBytes(proof); } // calculate message length const msgLen = BytesUtils.writeUIntLE(new Uint8Array(8), msg.length, 0, 8); const rateLimitBytes = BytesUtils.writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8); return verifyRLNProof(this.zkRLN, BytesUtils.concatenate(pBytes, msgLen, msg, rateLimitBytes)); } verifyWithRoots(proof, msg, roots, rateLimit) { let pBytes; if (proof instanceof Uint8Array) { pBytes = proof; } else { pBytes = proofToBytes(proof); } // calculate message length const msgLen = BytesUtils.writeUIntLE(new Uint8Array(8), msg.length, 0, 8); const rateLimitBytes = BytesUtils.writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8); const rootsBytes = BytesUtils.concatenate(...roots); return verifyWithRoots(this.zkRLN, BytesUtils.concatenate(pBytes, msgLen, msg, rateLimitBytes), rootsBytes); } verifyWithNoRoot(proof, msg, rateLimit) { let pBytes; if (proof instanceof Uint8Array) { pBytes = proof; } else { pBytes = proofToBytes(proof); } // calculate message length const msgLen = BytesUtils.writeUIntLE(new Uint8Array(8), msg.length, 0, 8); const rateLimitBytes = BytesUtils.writeUIntLE(new Uint8Array(8), rateLimit ?? this.rateLimit, 0, 8); return verifyWithRoots(this.zkRLN, BytesUtils.concatenate(pBytes, msgLen, msg, rateLimitBytes), new Uint8Array()); } } export { Zerokit };