mirror of
https://github.com/privacy-scaling-explorations/zk-kit.git
synced 2026-04-22 03:00:15 -04:00
proof compatible with circom 2.0
This commit is contained in:
4411
packages/identity/package-lock.json
generated
4411
packages/identity/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@libsem/identity",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.17",
|
||||
"description": "Library for managing identites for Semaphore and Rln protocols.",
|
||||
"main": "dist/index.node.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@libsem/types": "^1.0.0",
|
||||
"@libsem/types": "^1.0.5",
|
||||
"bigint-conversion": "^2.1.12",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"crypto": "^1.0.1",
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { genRandomIdentity, genIdentityFromSignedMessage, genRandomNumber } from "./strategies"
|
||||
import { genRandomIdentity, genIdentityFromMessage, genRandomNumber } from "./strategies"
|
||||
import * as bigintConversion from "bigint-conversion"
|
||||
import * as ciromlibjs from "circomlibjs"
|
||||
import { Identity } from "@libsem/types"
|
||||
import { Identity, SerializedIdentity } from "@libsem/types"
|
||||
|
||||
const poseidonHash = (data: Array<bigint>): bigint => {
|
||||
return ciromlibjs.poseidon(data)
|
||||
}
|
||||
|
||||
enum Strategy {
|
||||
export enum Strategy {
|
||||
RANDOM,
|
||||
SIGNED_MESSAGE,
|
||||
MESSAGE,
|
||||
SERIALIZED
|
||||
}
|
||||
|
||||
class ZkIdentity {
|
||||
private identityTrapdoor: bigint;
|
||||
private identityNullifier: bigint;
|
||||
private secret: Array<bigint> = [];
|
||||
private identityTrapdoor: bigint
|
||||
private identityNullifier: bigint
|
||||
private secret: Array<bigint> = []
|
||||
/**
|
||||
* Generates new ZkIdentity
|
||||
* @param strategy strategy for identity generation
|
||||
* @param metadata additional data needed to create identity for given strategy
|
||||
* @returns
|
||||
*/
|
||||
constructor(strategy: Strategy = Strategy.RANDOM, metadata: any = {}) {
|
||||
constructor(strategy: Strategy = Strategy.RANDOM, metadata?: string | SerializedIdentity) {
|
||||
if (strategy === Strategy.RANDOM) {
|
||||
const { identityTrapdoor, identityNullifier } = genRandomIdentity();
|
||||
this.identityTrapdoor = identityTrapdoor;
|
||||
this.identityNullifier = identityNullifier;
|
||||
} else if (strategy === Strategy.SIGNED_MESSAGE) {
|
||||
const { identityTrapdoor, identityNullifier } = genIdentityFromSignedMessage(metadata);
|
||||
this.identityTrapdoor = identityTrapdoor;
|
||||
this.identityNullifier = identityNullifier;
|
||||
const { identityTrapdoor, identityNullifier } = genRandomIdentity()
|
||||
this.identityTrapdoor = identityTrapdoor
|
||||
this.identityNullifier = identityNullifier
|
||||
} else if (strategy === Strategy.MESSAGE) {
|
||||
const { identityTrapdoor, identityNullifier } = genIdentityFromMessage(metadata as string)
|
||||
this.identityTrapdoor = identityTrapdoor
|
||||
this.identityNullifier = identityNullifier
|
||||
} else if (strategy === Strategy.SERIALIZED) {
|
||||
const { identityTrapdoor, identityNullifier } = metadata;
|
||||
this.identityNullifier = identityNullifier;
|
||||
this.identityTrapdoor = identityTrapdoor;
|
||||
const { identityNullifier, identityTrapdoor } = metadata as SerializedIdentity
|
||||
this.identityNullifier = bigintConversion.hexToBigint(identityNullifier)
|
||||
this.identityTrapdoor = bigintConversion.hexToBigint(identityTrapdoor)
|
||||
} else throw new Error("provided strategy is not supported")
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ class ZkIdentity {
|
||||
* @returns
|
||||
*/
|
||||
static genFromSerialized(serialisedIdentity: string): ZkIdentity {
|
||||
const data = JSON.parse(serialisedIdentity);
|
||||
if(data.length !== 2) throw new Error('Format is wrong');
|
||||
const data = JSON.parse(serialisedIdentity)
|
||||
if (data.length !== 2) throw new Error("Format is wrong")
|
||||
return new ZkIdentity(Strategy.SERIALIZED, {
|
||||
identityNullifier: bigintConversion.hexToBigint(data[0]),
|
||||
identityTrapdoor: bigintConversion.hexToBigint(data[1])
|
||||
});
|
||||
identityNullifier: data[0],
|
||||
identityTrapdoor: data[1]
|
||||
})
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -59,16 +59,16 @@ class ZkIdentity {
|
||||
getIdentity(): Identity {
|
||||
return {
|
||||
identityNullifier: this.identityNullifier,
|
||||
identityTrapdoor: this.identityTrapdoor,
|
||||
identityTrapdoor: this.identityTrapdoor
|
||||
}
|
||||
}
|
||||
|
||||
getNullifier(): bigint {
|
||||
return this.identityNullifier;
|
||||
return this.identityNullifier
|
||||
}
|
||||
|
||||
getSecret(): Array<bigint> {
|
||||
return this.secret;
|
||||
return this.secret
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ class ZkIdentity {
|
||||
* @returns secret
|
||||
*/
|
||||
genRandomSecret(parts = 2) {
|
||||
this.secret = [];
|
||||
this.secret = []
|
||||
for (let i = 0; i < parts; i++) {
|
||||
this.secret.push(genRandomNumber())
|
||||
}
|
||||
@@ -96,9 +96,9 @@ class ZkIdentity {
|
||||
* @returns identity commitment
|
||||
*/
|
||||
genIdentityCommitmentFromSecret(): bigint {
|
||||
if(!this.secret.length) throw new Error('Secret is not generated');
|
||||
const secretHash = poseidonHash(this.secret);
|
||||
return poseidonHash([secretHash]);
|
||||
if (!this.secret.length) throw new Error("Secret is not generated")
|
||||
const secretHash = poseidonHash(this.secret)
|
||||
return poseidonHash([secretHash])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,4 +121,4 @@ class ZkIdentity {
|
||||
}
|
||||
}
|
||||
|
||||
export default ZkIdentity;
|
||||
export default ZkIdentity
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ZkIdentity from "./identity"
|
||||
import ZkIdentity, { Strategy } from "./identity"
|
||||
import { Identity } from "@libsem/types"
|
||||
|
||||
export { ZkIdentity, Identity }
|
||||
export { ZkIdentity, Identity, Strategy }
|
||||
|
||||
@@ -23,16 +23,14 @@ const genRandomIdentity = (): Identity => {
|
||||
* @param metadata { signedMessage } from which to create identity
|
||||
* @returns Identity
|
||||
*/
|
||||
const genIdentityFromSignedMessage = (metadata: any): Identity => {
|
||||
const genIdentityFromMessage = (message: string): Identity => {
|
||||
const sha256 = (message: string): string => {
|
||||
const hash = _sha256.create()
|
||||
hash.update(message)
|
||||
return hash.hex()
|
||||
}
|
||||
|
||||
const { signedMessage } = metadata
|
||||
|
||||
const messageHash = sha256(signedMessage)
|
||||
const messageHash = sha256(message)
|
||||
const identityNullifier = bigintConversion.hexToBigint(sha256(`${messageHash}identity_nullifier`))
|
||||
const identityTrapdoor = bigintConversion.hexToBigint(sha256(`${messageHash}identity_trapdoor`))
|
||||
|
||||
@@ -42,4 +40,4 @@ const genIdentityFromSignedMessage = (metadata: any): Identity => {
|
||||
}
|
||||
}
|
||||
|
||||
export { genRandomIdentity, genIdentityFromSignedMessage, genRandomNumber }
|
||||
export { genRandomIdentity, genIdentityFromMessage, genRandomNumber }
|
||||
|
||||
@@ -1,52 +1,66 @@
|
||||
import { ZkIdentity } from "../src"
|
||||
import { Identity } from "../../types"
|
||||
import { Strategy, ZkIdentity } from "../src"
|
||||
|
||||
describe("Semaphore identity", () => {
|
||||
describe("Create identity", () => {
|
||||
it("Should create a Semaphore identity", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
expect(typeof identity).toEqual("object");
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
|
||||
expect(typeof identity).toEqual("object")
|
||||
})
|
||||
|
||||
it("Should create a Semaphore identity with a message strategy", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity(Strategy.MESSAGE, "message")
|
||||
|
||||
expect(typeof identity).toEqual("object")
|
||||
})
|
||||
|
||||
it("Should generate secret from identity", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
identity.genSecretFromIdentity();
|
||||
const identitySecret = identity.getSecret();
|
||||
expect(identitySecret.length).toEqual(2);
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
identity.genSecretFromIdentity()
|
||||
const identitySecret = identity.getSecret()
|
||||
|
||||
expect(identitySecret.length).toEqual(2)
|
||||
expect(typeof identitySecret).toEqual("object")
|
||||
})
|
||||
|
||||
it("Should generate random secret", async () => {
|
||||
const secretParts = 5;
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
identity.genRandomSecret(secretParts);
|
||||
const identitySecret = identity.getSecret();
|
||||
const secretParts = 5
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
identity.genRandomSecret(secretParts)
|
||||
const identitySecret = identity.getSecret()
|
||||
|
||||
expect(identitySecret.length).toEqual(5)
|
||||
expect(typeof identitySecret).toEqual("object")
|
||||
})
|
||||
|
||||
it("Should generate identity commitment from identity", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
const identityCommitment: bigint = identity.genIdentityCommitment();
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
const identityCommitment: bigint = identity.genIdentityCommitment()
|
||||
|
||||
expect(typeof identityCommitment).toEqual("bigint")
|
||||
})
|
||||
|
||||
it("Should generate identity commitment from random secret", async () => {
|
||||
const secretParts = 5
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
identity.genRandomSecret(secretParts);
|
||||
const identityCommitment: bigint = identity.genIdentityCommitmentFromSecret();
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
identity.genRandomSecret(secretParts)
|
||||
const identityCommitment: bigint = identity.genIdentityCommitmentFromSecret()
|
||||
|
||||
expect(typeof identityCommitment).toEqual("bigint")
|
||||
})
|
||||
|
||||
it("Should serialize identity", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
const serialized: string = identity.serializeIdentity();
|
||||
expect(typeof serialized).toEqual("string");
|
||||
})
|
||||
it("Should unserialize identity", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity();
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
const serialized: string = identity.serializeIdentity()
|
||||
const unserialized: ZkIdentity = ZkIdentity.genFromSerialized(serialized);
|
||||
|
||||
expect(typeof serialized).toEqual("string")
|
||||
})
|
||||
|
||||
it("Should unserialize identity", async () => {
|
||||
const identity: ZkIdentity = new ZkIdentity()
|
||||
const serialized: string = identity.serializeIdentity()
|
||||
const unserialized: ZkIdentity = ZkIdentity.genFromSerialized(serialized)
|
||||
|
||||
expect(unserialized).toStrictEqual(identity)
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
52276
packages/protocols/package-lock.json
generated
52276
packages/protocols/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@libsem/protocols",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.15",
|
||||
"description": "Client library for generating and verifying Semaphore & Rln ZK proofs.",
|
||||
"main": "dist/index.node.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
@@ -25,7 +25,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@libsem/types": "^1.0.0",
|
||||
"@libsem/types": "^1.0.5",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"ethers": "^5.4.7",
|
||||
"incrementalquintree": "^1.0.7",
|
||||
|
||||
@@ -2,6 +2,6 @@ import Semaphore from "./semaphore"
|
||||
import Rln from "./rln"
|
||||
import NRln from "./nRln"
|
||||
import { generateMerkleProof, genExternalNullifier, genSignalHash } from "./utils"
|
||||
import { Identity, MerkleProof, IProof } from "@libsem/types"
|
||||
import { Identity, MerkleProof, FullProof } from "@libsem/types"
|
||||
|
||||
export { Semaphore, Rln, NRln, generateMerkleProof, genExternalNullifier, genSignalHash, Identity, MerkleProof, IProof }
|
||||
export { Semaphore, Rln, NRln, generateMerkleProof, genExternalNullifier, genSignalHash, Identity, MerkleProof, FullProof }
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* eslint @typescript-eslint/no-var-requires: "off" */
|
||||
const { groth16 } = require("snarkjs")
|
||||
import { SNARK_FIELD_SIZE } from "./utils"
|
||||
import { IProof } from "@libsem/types"
|
||||
import { FullProof } from "@libsem/types"
|
||||
import * as fs from "fs";
|
||||
// import * as wcBuilder from "./witness_calculator";
|
||||
const wc = require("./witness_calculator");
|
||||
|
||||
export class ZkProtocol {
|
||||
@@ -16,12 +15,18 @@ export class ZkProtocol {
|
||||
* @returns creates and saves witness to witnessFileName
|
||||
*/
|
||||
async buildWnts(input: any, wasmFilePath: string, witnessFileName: string) {
|
||||
const buffer: Buffer = fs.readFileSync(wasmFilePath);
|
||||
const buffer = fs.readFileSync(wasmFilePath);
|
||||
|
||||
wc(buffer).then(async witnessCalculator => {
|
||||
const buff= await witnessCalculator.calculateWTNSBin(input, 0);
|
||||
fs.writeFileSync(witnessFileName, buff);
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
wc(buffer)
|
||||
.then(async witnessCalculator => {
|
||||
const buff= await witnessCalculator.calculateWTNSBin(input, 0);
|
||||
fs.writeFileSync(witnessFileName, buff);
|
||||
resolve(witnessFileName);
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Generates full proof
|
||||
@@ -30,10 +35,10 @@ export class ZkProtocol {
|
||||
* @param finalZkeyPath path to final zkey file
|
||||
* @returns zero knowledge proof
|
||||
*/
|
||||
async genProof(grothInput: any, wasmFilePath: string, finalZkeyPath: string): Promise<IProof> {
|
||||
async genProof(grothInput: any, wasmFilePath: string, finalZkeyPath: string): Promise<FullProof> {
|
||||
await this.buildWnts(grothInput, wasmFilePath, 'witness.wtns');
|
||||
const { proof, publicSignals } = await groth16.prove(finalZkeyPath, 'witness.wtns', null);
|
||||
//TODO cleanup witness
|
||||
fs.unlinkSync('witness.wtns');
|
||||
return { proof, publicSignals };
|
||||
}
|
||||
|
||||
@@ -43,7 +48,7 @@ export class ZkProtocol {
|
||||
* @param fullProof proof
|
||||
* @returns Is provided proof valid
|
||||
*/
|
||||
verifyProof(vKey: string, fullProof: IProof): Promise<boolean> {
|
||||
verifyProof(vKey: string, fullProof: FullProof): Promise<boolean> {
|
||||
const { proof, publicSignals } = fullProof
|
||||
return groth16.verify(vKey, publicSignals, proof)
|
||||
}
|
||||
@@ -53,7 +58,7 @@ export class ZkProtocol {
|
||||
* @param fullProof
|
||||
* @returns Proof
|
||||
*/
|
||||
packToSolidityProof(fullProof: IProof) {
|
||||
packToSolidityProof(fullProof: FullProof) {
|
||||
const { proof, publicSignals } = fullProof
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Rln } from "../src"
|
||||
import { ZkIdentity } from "../../identity/src"
|
||||
import { MerkleProof, IProof } from "../../types"
|
||||
import { MerkleProof, FullProof } from "../../types"
|
||||
import { genSignalHash, genExternalNullifier, generateMerkleProof, poseidonHash } from "../src/utils"
|
||||
import * as path from "path"
|
||||
import * as fs from "fs"
|
||||
@@ -33,7 +33,7 @@ describe("Rln", () => {
|
||||
const rlnIdentifier: bigint = Rln.genIdentifier()
|
||||
|
||||
const merkleProof: MerkleProof = generateMerkleProof(15, BigInt(0), 5, commitments, identityCommitment)
|
||||
const witness: IProof = Rln.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||
const witness: FullProof = Rln.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||
|
||||
expect(typeof witness).toBe("object")
|
||||
})
|
||||
@@ -56,7 +56,7 @@ describe("Rln", () => {
|
||||
const rlnIdentifier: bigint = Rln.genIdentifier()
|
||||
|
||||
const merkleProof: MerkleProof = generateMerkleProof(15, BigInt(0), 2, commitments, identityCommitment)
|
||||
const witness: IProof = Rln.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||
const witness: FullProof = Rln.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||
|
||||
const [y, nullifier] = Rln.calculateOutput(secretHash, BigInt(epoch), rlnIdentifier, signalHash)
|
||||
const publicSignals = [y, merkleProof.root, nullifier, signalHash, epoch, rlnIdentifier]
|
||||
@@ -67,7 +67,7 @@ describe("Rln", () => {
|
||||
const wasmFilePath: string = path.join("./zkeyFiles", "rln", "rln.wasm")
|
||||
const finalZkeyPath: string = path.join("./zkeyFiles", "rln", "rln_final.zkey")
|
||||
|
||||
const fullProof: IProof = await Rln.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||
const fullProof: FullProof = await Rln.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||
const res: boolean = await Rln.verifyProof(vKey, { proof: fullProof.proof, publicSignals })
|
||||
|
||||
expect(res).toBe(true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ZkIdentity } from "../../identity/src"
|
||||
import { MerkleProof, IProof } from "../../types"
|
||||
import { MerkleProof, FullProof } from "../../types"
|
||||
import { genSignalHash, genExternalNullifier, generateMerkleProof } from "../src/utils"
|
||||
import * as path from "path"
|
||||
import * as fs from "fs"
|
||||
@@ -32,7 +32,7 @@ describe("Semaphore", () => {
|
||||
commitments.push(identityCommitment)
|
||||
|
||||
const merkleProof: MerkleProof = generateMerkleProof(20, BigInt(0), 5, commitments, identityCommitment)
|
||||
const witness: IProof = Semaphore.genWitness(identity.getIdentity(), merkleProof, externalNullifier, signal)
|
||||
const witness: FullProof = Semaphore.genWitness(identity.getIdentity(), merkleProof, externalNullifier, signal)
|
||||
|
||||
expect(typeof witness).toBe("object")
|
||||
})
|
||||
@@ -50,7 +50,7 @@ describe("Semaphore", () => {
|
||||
commitments.push(identityCommitment)
|
||||
|
||||
const merkleProof: MerkleProof = generateMerkleProof(20, BigInt(0), 5, commitments, identityCommitment)
|
||||
const witness: IProof = Semaphore.genWitness(identity.getIdentity(), merkleProof, externalNullifier, signal)
|
||||
const witness: FullProof = Semaphore.genWitness(identity.getIdentity(), merkleProof, externalNullifier, signal)
|
||||
|
||||
const publicSignals: Array<bigint | string> = [
|
||||
merkleProof.root,
|
||||
@@ -65,12 +65,8 @@ describe("Semaphore", () => {
|
||||
const wasmFilePath: string = path.join("./zkeyFiles", "semaphore", "semaphore.wasm")
|
||||
const finalZkeyPath: string = path.join("./zkeyFiles", "semaphore", "semaphore_final.zkey")
|
||||
|
||||
|
||||
|
||||
const fullProof: IProof = await Semaphore.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||
console.log('Auto generated: ', fullProof.publicSignals);
|
||||
console.log('Mine: ', publicSignals);
|
||||
const res: boolean = await Semaphore.verifyProof(vKey, { proof: fullProof.proof, publicSignals: fullProof.publicSignals })
|
||||
const fullProof: FullProof = await Semaphore.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||
const res: boolean = await Semaphore.verifyProof(vKey, { proof: fullProof.proof, publicSignals })
|
||||
|
||||
expect(res).toBe(true)
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@libsem/types",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.5",
|
||||
"description": "Common type definitions for Semaphore modules.",
|
||||
"main": "src/index.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -18,6 +18,5 @@
|
||||
"devDependencies": {
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-node": "^10.3.0"
|
||||
},
|
||||
"gitHead": "7cab1c709124f10610b04995603086d29a7eb18b"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,16 @@ export interface Identity {
|
||||
identityTrapdoor: bigint
|
||||
}
|
||||
|
||||
export interface IProof {
|
||||
proof: any
|
||||
export type Proof = {
|
||||
pi_a: Array<string>,
|
||||
pi_b: [ [Array<string>], [Array<string>], [Array<string>] ],
|
||||
pi_c: Array<string>
|
||||
protocol: string,
|
||||
curve: string
|
||||
}
|
||||
|
||||
export interface FullProof {
|
||||
proof: Proof
|
||||
publicSignals: Array<bigint | string>
|
||||
}
|
||||
|
||||
@@ -13,3 +21,5 @@ export interface MerkleProof {
|
||||
indices: Array<any>
|
||||
pathElements: Array<any>
|
||||
}
|
||||
|
||||
export type SerializedIdentity = { identityNullifier: string, identityTrapdoor: string };
|
||||
|
||||
Reference in New Issue
Block a user