proof compatible with circom 2.0

Former-commit-id: c08404e17262ae81673fc8ca0162f820d4133f09 [formerly f509e36ebd]
Former-commit-id: c7668073ffdf53124764d499a2ca2d57ebce3d2d
This commit is contained in:
Andrija Novakovic
2021-10-25 15:04:57 +02:00
16 changed files with 133 additions and 16464 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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

View File

@@ -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 }

View File

@@ -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 }

View File

@@ -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

View File

@@ -1 +1 @@
8640e04fa85409d7a45c56f1fb37414426cbeb01
1dfdaadeabddaaed88cda1009e5e6a57c5807df9

View File

@@ -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",

View File

@@ -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 }

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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 };