mirror of
https://github.com/logos-messaging/js-rln.git
synced 2026-01-03 14:13:08 +00:00
feat: use Proxy contract address, add fixes to RLN Contract (#74)
* use Proxy contract address, add fixes to RLN Contract * whitelist word * update tesst * up mock * up * fixes to contract, keystore and utils * whitelist word * update tests * move to hash map * up mock * up
This commit is contained in:
parent
89283768c3
commit
fa49e29856
@ -20,7 +20,9 @@
|
||||
"kdfparams",
|
||||
"ciphertext",
|
||||
"cipherparams",
|
||||
"codegen"
|
||||
"codegen",
|
||||
"hexlify",
|
||||
"Arraylike"
|
||||
],
|
||||
"flagWords": [],
|
||||
"ignorePaths": [
|
||||
|
||||
@ -63,6 +63,6 @@ export const RLN_STORAGE_ABI = [
|
||||
|
||||
export const SEPOLIA_CONTRACT = {
|
||||
chainId: 11155111,
|
||||
address: "0xF1935b338321013f11068abCafC548A7B0db732C",
|
||||
address: "0xF471d71E9b1455bBF4b85d475afb9BB0954A29c4",
|
||||
abi: RLN_REGISTRY_ABI,
|
||||
};
|
||||
|
||||
@ -202,25 +202,25 @@ describe("Keystore", () => {
|
||||
const expectedHash =
|
||||
"9DB2B4718A97485B9F70F68D1CC19F4E10F0B4CE943418838E94956CB8E57548";
|
||||
const identity = {
|
||||
IDTrapdoor: [
|
||||
IDTrapdoor: new Uint8Array([
|
||||
211, 23, 66, 42, 179, 130, 131, 111, 201, 205, 244, 34, 27, 238, 244,
|
||||
216, 131, 240, 188, 45, 193, 172, 4, 168, 225, 225, 43, 197, 114, 176,
|
||||
126, 9,
|
||||
],
|
||||
IDNullifier: [
|
||||
]),
|
||||
IDNullifier: new Uint8Array([
|
||||
238, 168, 239, 65, 73, 63, 105, 19, 132, 62, 213, 205, 191, 255, 209, 9,
|
||||
178, 155, 239, 201, 131, 125, 233, 136, 246, 217, 9, 237, 55, 89, 81,
|
||||
42,
|
||||
],
|
||||
IDSecretHash: [
|
||||
]),
|
||||
IDSecretHash: new Uint8Array([
|
||||
150, 54, 194, 28, 18, 216, 138, 253, 95, 139, 120, 109, 98, 129, 146,
|
||||
101, 41, 194, 36, 36, 96, 152, 152, 89, 151, 160, 118, 15, 222, 124,
|
||||
187, 4,
|
||||
],
|
||||
IDCommitment: [
|
||||
]),
|
||||
IDCommitment: new Uint8Array([
|
||||
112, 216, 27, 89, 188, 135, 203, 19, 168, 211, 117, 13, 231, 135, 229,
|
||||
58, 94, 20, 246, 8, 33, 65, 238, 37, 112, 97, 65, 241, 255, 93, 171, 15,
|
||||
],
|
||||
]),
|
||||
IDCommitmentBigInt: buildBigIntFromUint8Array(
|
||||
new Uint8Array([
|
||||
112, 216, 27, 89, 188, 135, 203, 19, 168, 211, 117, 13, 231, 135, 229,
|
||||
|
||||
@ -245,13 +245,23 @@ export class Keystore {
|
||||
// TODO: add runtime validation of nwaku credentials
|
||||
return {
|
||||
identity: {
|
||||
IDCommitment: _.get(obj, "identityCredential.idCommitment"),
|
||||
IDTrapdoor: _.get(obj, "identityCredential.idTrapdoor"),
|
||||
IDNullifier: _.get(obj, "identityCredential.idNullifier"),
|
||||
IDCommitmentBigInt: buildBigIntFromUint8Array(
|
||||
new Uint8Array(_.get(obj, "identityCredential.idCommitment", []))
|
||||
IDCommitment: Keystore.fromArraylikeToBytes(
|
||||
_.get(obj, "identityCredential.idCommitment", [])
|
||||
),
|
||||
IDTrapdoor: Keystore.fromArraylikeToBytes(
|
||||
_.get(obj, "identityCredential.idTrapdoor", [])
|
||||
),
|
||||
IDNullifier: Keystore.fromArraylikeToBytes(
|
||||
_.get(obj, "identityCredential.idNullifier", [])
|
||||
),
|
||||
IDCommitmentBigInt: buildBigIntFromUint8Array(
|
||||
Keystore.fromArraylikeToBytes(
|
||||
_.get(obj, "identityCredential.idCommitment", [])
|
||||
)
|
||||
),
|
||||
IDSecretHash: Keystore.fromArraylikeToBytes(
|
||||
_.get(obj, "identityCredential.idSecretHash", [])
|
||||
),
|
||||
IDSecretHash: _.get(obj, "identityCredential.idSecretHash"),
|
||||
},
|
||||
membership: {
|
||||
treeIndex: _.get(obj, "treeIndex"),
|
||||
@ -265,6 +275,23 @@ export class Keystore {
|
||||
}
|
||||
}
|
||||
|
||||
private static fromArraylikeToBytes(obj: {
|
||||
[key: number]: number;
|
||||
}): Uint8Array {
|
||||
const bytes = [];
|
||||
|
||||
let index = 0;
|
||||
let lastElement = obj[index];
|
||||
|
||||
while (lastElement !== undefined) {
|
||||
bytes.push(lastElement);
|
||||
index += 1;
|
||||
lastElement = obj[index];
|
||||
}
|
||||
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
// follows nwaku implementation
|
||||
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L111
|
||||
private static computeMembershipHash(info: MembershipInfo): MembershipHash {
|
||||
|
||||
@ -15,7 +15,7 @@ describe("RLN Contract abstraction", () => {
|
||||
|
||||
const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address);
|
||||
const rlnContract = new rln.RLNContract(rlnInstance, {
|
||||
address: rln.SEPOLIA_CONTRACT.address,
|
||||
registryAddress: rln.SEPOLIA_CONTRACT.address,
|
||||
provider: voidSigner,
|
||||
});
|
||||
|
||||
@ -39,7 +39,7 @@ describe("RLN Contract abstraction", () => {
|
||||
const rlnInstance = await rln.create();
|
||||
const voidSigner = new ethers.VoidSigner(rln.SEPOLIA_CONTRACT.address);
|
||||
const rlnContract = new rln.RLNContract(rlnInstance, {
|
||||
address: rln.SEPOLIA_CONTRACT.address,
|
||||
registryAddress: rln.SEPOLIA_CONTRACT.address,
|
||||
provider: voidSigner,
|
||||
});
|
||||
|
||||
@ -49,12 +49,12 @@ describe("RLN Contract abstraction", () => {
|
||||
topics: [],
|
||||
} as unknown as ethers.EventFilter;
|
||||
rlnContract["registryContract"] = {
|
||||
register: () =>
|
||||
"register(uint16,uint256)": () =>
|
||||
Promise.resolve({ wait: () => Promise.resolve(undefined) }),
|
||||
} as unknown as ethers.Contract;
|
||||
const contractSpy = chai.spy.on(
|
||||
rlnContract["registryContract"],
|
||||
"register"
|
||||
"register(uint16,uint256)"
|
||||
);
|
||||
|
||||
await rlnContract.registerWithSignature(rlnInstance, mockSignature);
|
||||
@ -66,8 +66,8 @@ describe("RLN Contract abstraction", () => {
|
||||
function mockEvent(): ethers.Event {
|
||||
return {
|
||||
args: {
|
||||
pubkey: "0x9e7d3f8f8c7a1d2bef96a2e8dbb8e7c1ea9a9ab78d6b3c6c3c",
|
||||
index: 1,
|
||||
idCommitment: "0x9e7d3f8f8c7a1d2bef96a2e8dbb8e7c1ea9a9ab78d6b3c6c3c",
|
||||
index: ethers.BigNumber.from(1),
|
||||
},
|
||||
} as unknown as ethers.Event;
|
||||
}
|
||||
|
||||
@ -5,17 +5,23 @@ import { IdentityCredential, RLNInstance } from "./rln.js";
|
||||
import { MerkleRootTracker } from "./root_tracker.js";
|
||||
|
||||
type Member = {
|
||||
pubkey: string;
|
||||
index: number;
|
||||
idCommitment: string;
|
||||
index: ethers.BigNumber;
|
||||
};
|
||||
|
||||
type Provider = ethers.Signer | ethers.providers.Provider;
|
||||
|
||||
type ContractOptions = {
|
||||
address: string;
|
||||
type RLNContractOptions = {
|
||||
provider: Provider;
|
||||
registryAddress: string;
|
||||
};
|
||||
|
||||
type RLNStorageOptions = {
|
||||
storageIndex?: number;
|
||||
};
|
||||
|
||||
type RLNContractInitOptions = RLNContractOptions & RLNStorageOptions;
|
||||
|
||||
type FetchMembersOptions = {
|
||||
fromBlock?: number;
|
||||
fetchRange?: number;
|
||||
@ -31,11 +37,11 @@ export class RLNContract {
|
||||
private storageContract: undefined | ethers.Contract;
|
||||
private _membersFilter: undefined | ethers.EventFilter;
|
||||
|
||||
private _members: Member[] = [];
|
||||
private _members: Map<number, Member> = new Map();
|
||||
|
||||
public static async init(
|
||||
rlnInstance: RLNInstance,
|
||||
options: ContractOptions
|
||||
options: RLNContractInitOptions
|
||||
): Promise<RLNContract> {
|
||||
const rlnContract = new RLNContract(rlnInstance, options);
|
||||
|
||||
@ -48,25 +54,34 @@ export class RLNContract {
|
||||
|
||||
constructor(
|
||||
rlnInstance: RLNInstance,
|
||||
{ address, provider }: ContractOptions
|
||||
{ registryAddress, provider }: RLNContractOptions
|
||||
) {
|
||||
const initialRoot = rlnInstance.getMerkleRoot();
|
||||
|
||||
this.registryContract = new ethers.Contract(
|
||||
address,
|
||||
registryAddress,
|
||||
RLN_REGISTRY_ABI,
|
||||
provider
|
||||
);
|
||||
this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
|
||||
}
|
||||
|
||||
private async initStorageContract(provider: Provider): Promise<void> {
|
||||
const index = await this.registryContract.usingStorageIndex();
|
||||
const address = await this.registryContract.storages(index);
|
||||
private async initStorageContract(
|
||||
provider: Provider,
|
||||
options: RLNStorageOptions = {}
|
||||
): Promise<void> {
|
||||
const storageIndex = options?.storageIndex
|
||||
? options.storageIndex
|
||||
: await this.registryContract.usingStorageIndex();
|
||||
const storageAddress = await this.registryContract.storages(storageIndex);
|
||||
|
||||
this.storageIndex = index;
|
||||
if (!storageAddress || storageAddress === ethers.constants.AddressZero) {
|
||||
throw Error("No RLN Storage initialized on registry contract.");
|
||||
}
|
||||
|
||||
this.storageIndex = storageIndex;
|
||||
this.storageContract = new ethers.Contract(
|
||||
address,
|
||||
storageAddress,
|
||||
RLN_STORAGE_ABI,
|
||||
provider
|
||||
);
|
||||
@ -77,13 +92,16 @@ export class RLNContract {
|
||||
|
||||
public get contract(): ethers.Contract {
|
||||
if (!this.storageContract) {
|
||||
throw Error("Storage contract was not initialized.");
|
||||
throw Error("Storage contract was not initialized");
|
||||
}
|
||||
return this.storageContract as ethers.Contract;
|
||||
}
|
||||
|
||||
public get members(): Member[] {
|
||||
return this._members;
|
||||
const sortedMembers = Array.from(this._members.values()).sort(
|
||||
(left, right) => left.index.toNumber() - right.index.toNumber()
|
||||
);
|
||||
return sortedMembers;
|
||||
}
|
||||
|
||||
private get membersFilter(): ethers.EventFilter {
|
||||
@ -115,13 +133,13 @@ export class RLNContract {
|
||||
}
|
||||
|
||||
if (evt.removed) {
|
||||
const index: number = evt.args.index;
|
||||
const index: ethers.BigNumber = evt.args.index;
|
||||
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
|
||||
if (toRemoveVal != undefined) {
|
||||
toRemoveVal.push(index);
|
||||
toRemoveVal.push(index.toNumber());
|
||||
toRemoveTable.set(evt.blockNumber, toRemoveVal);
|
||||
} else {
|
||||
toRemoveTable.set(evt.blockNumber, [index]);
|
||||
toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
|
||||
}
|
||||
} else {
|
||||
let eventsPerBlock = toInsertTable.get(evt.blockNumber);
|
||||
@ -144,18 +162,23 @@ export class RLNContract {
|
||||
): void {
|
||||
toInsert.forEach((events: ethers.Event[], blockNumber: number) => {
|
||||
events.forEach((evt) => {
|
||||
if (!evt.args) {
|
||||
const _idCommitment = evt?.args?.idCommitment;
|
||||
const index: ethers.BigNumber = evt?.args?.index;
|
||||
|
||||
if (!_idCommitment || !index) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pubkey = evt.args.pubkey;
|
||||
const index = evt.args.index;
|
||||
const idCommitment = ethers.utils.zeroPad(
|
||||
ethers.utils.arrayify(pubkey),
|
||||
ethers.utils.arrayify(_idCommitment),
|
||||
32
|
||||
);
|
||||
rlnInstance.insertMember(idCommitment);
|
||||
this.members.push({ index, pubkey });
|
||||
this._members.set(index.toNumber(), {
|
||||
index,
|
||||
idCommitment:
|
||||
_idCommitment?._hex || ethers.utils.hexlify(idCommitment),
|
||||
});
|
||||
});
|
||||
|
||||
const currentRoot = rlnInstance.getMerkleRoot();
|
||||
@ -170,9 +193,8 @@ export class RLNContract {
|
||||
const removeDescending = new Map([...toRemove].sort().reverse());
|
||||
removeDescending.forEach((indexes: number[], blockNumber: number) => {
|
||||
indexes.forEach((index) => {
|
||||
const idx = this.members.findIndex((m) => m.index === index);
|
||||
if (idx > -1) {
|
||||
this.members.splice(idx, 1);
|
||||
if (this._members.has(index)) {
|
||||
this._members.delete(index);
|
||||
}
|
||||
rlnInstance.deleteMember(index);
|
||||
});
|
||||
@ -183,14 +205,14 @@ export class RLNContract {
|
||||
|
||||
public subscribeToMembers(rlnInstance: RLNInstance): void {
|
||||
this.contract.on(this.membersFilter, (_pubkey, _index, event) =>
|
||||
this.processEvents(rlnInstance, event)
|
||||
this.processEvents(rlnInstance, [event])
|
||||
);
|
||||
}
|
||||
|
||||
public async registerWithSignature(
|
||||
rlnInstance: RLNInstance,
|
||||
signature: string
|
||||
): Promise<ethers.Event | undefined> {
|
||||
): Promise<Member | undefined> {
|
||||
const identityCredential =
|
||||
await rlnInstance.generateSeededIdentityCredential(signature);
|
||||
|
||||
@ -199,23 +221,36 @@ export class RLNContract {
|
||||
|
||||
public async registerWithKey(
|
||||
credential: IdentityCredential
|
||||
): Promise<ethers.Event | undefined> {
|
||||
if (!this.storageIndex) {
|
||||
): Promise<Member | undefined> {
|
||||
if (this.storageIndex === undefined) {
|
||||
throw Error(
|
||||
"Cannot register credential, no storage contract index found."
|
||||
);
|
||||
}
|
||||
const txRegisterResponse: ethers.ContractTransaction =
|
||||
await this.registryContract.register(
|
||||
await this.registryContract["register(uint16,uint256)"](
|
||||
this.storageIndex,
|
||||
credential.IDCommitmentBigInt,
|
||||
{
|
||||
gasLimit: 100000,
|
||||
}
|
||||
{ gasLimit: 100000 }
|
||||
);
|
||||
const txRegisterReceipt = await txRegisterResponse.wait();
|
||||
|
||||
return txRegisterReceipt?.events?.[0];
|
||||
// assumption: register(uint16,uint256) emits one event
|
||||
const memberRegistered = txRegisterReceipt?.events?.[0];
|
||||
|
||||
if (!memberRegistered) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const decodedData = this.contract.interface.decodeEventLog(
|
||||
"MemberRegistered",
|
||||
memberRegistered.data
|
||||
);
|
||||
|
||||
return {
|
||||
idCommitment: decodedData.idCommitment,
|
||||
index: decodedData.index,
|
||||
};
|
||||
}
|
||||
|
||||
public roots(): Uint8Array[] {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user