@waku/rln
Version:
RLN (Rate Limiting Nullifier) implementation for Waku
113 lines (110 loc) • 4.46 kB
JavaScript
import '../../../interfaces/dist/protocols.js';
import '../../../interfaces/dist/connection_manager.js';
import '../../../interfaces/dist/health_status.js';
import { hexToBytes } from '../../../utils/dist/bytes/index.js';
import { Logger } from '../../../utils/dist/logger.js';
import { MerkleRootTracker } from '../root_tracker.js';
import { BytesUtils } from '../utils/bytes.js';
import { RLNBaseContract } from './rln_base_contract.js';
import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
const log = new Logger("waku:rln:contract");
class RLNContract extends RLNBaseContract {
instance;
merkleRootTracker;
/**
* Asynchronous initializer for RLNContract.
* Allows injecting a mocked contract for testing purposes.
*/
static async init(rlnInstance, options) {
const rlnContract = new RLNContract(rlnInstance, options);
return rlnContract;
}
constructor(rlnInstance, options) {
super(options);
this.instance = rlnInstance;
const initialRoot = rlnInstance.zerokit.getMerkleRoot();
this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
}
processEvents(events) {
const toRemoveTable = new Map();
const toInsertTable = new Map();
events.forEach((evt) => {
if (!evt.args) {
return;
}
if (evt.event === "MembershipErased" ||
evt.event === "MembershipExpired") {
let index = evt.args.index;
if (!index) {
return;
}
if (typeof index === "number" || typeof index === "string") {
index = BigNumber.from(index);
}
else {
log.error("Index is not a number or string", {
index,
event: evt
});
return;
}
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
if (toRemoveVal != undefined) {
toRemoveVal.push(index.toNumber());
toRemoveTable.set(evt.blockNumber, toRemoveVal);
}
else {
toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
}
}
else if (evt.event === "MembershipRegistered") {
let eventsPerBlock = toInsertTable.get(evt.blockNumber);
if (eventsPerBlock == undefined) {
eventsPerBlock = [];
}
eventsPerBlock.push(evt);
toInsertTable.set(evt.blockNumber, eventsPerBlock);
}
});
this.removeMembers(this.instance, toRemoveTable);
this.insertMembers(this.instance, toInsertTable);
}
insertMembers(rlnInstance, toInsert) {
toInsert.forEach((events, blockNumber) => {
events.forEach((evt) => {
if (!evt.args)
return;
const _idCommitment = evt.args.idCommitment;
let index = evt.args.index;
if (!_idCommitment || !index) {
return;
}
if (typeof index === "number" || typeof index === "string") {
index = BigNumber.from(index);
}
const idCommitment = BytesUtils.zeroPadLE(hexToBytes(_idCommitment), 32);
rlnInstance.zerokit.insertMember(idCommitment);
const numericIndex = index.toNumber();
this._members.set(numericIndex, {
index,
idCommitment: _idCommitment
});
});
const currentRoot = rlnInstance.zerokit.getMerkleRoot();
this.merkleRootTracker.pushRoot(blockNumber, currentRoot);
});
}
removeMembers(rlnInstance, toRemove) {
const removeDescending = new Map([...toRemove].reverse());
removeDescending.forEach((indexes, blockNumber) => {
indexes.forEach((index) => {
if (this._members.has(index)) {
this._members.delete(index);
rlnInstance.zerokit.deleteMember(index);
}
});
this.merkleRootTracker.backFill(blockNumber);
});
}
}
export { RLNContract };