-
Notifications
You must be signed in to change notification settings - Fork 494
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@embark/snarks): Allow embark-snark to be used in the dapp
`embark-snark` has been updated such that it can be used, in conjunction with `embarkjs-snark`, in the console, and in the DApp. This could, for example, be used to build a dapp like https://tornado.cash. Please see the README for usage instructions. Updated tests were excluded in this PR as a consideration for time already spent on getting this library completed. Tests should be updated in a future PR.
- Loading branch information
Showing
28 changed files
with
1,049 additions
and
257 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* global module require */ | ||
|
||
const cloneDeep = require('lodash.clonedeep'); | ||
|
||
module.exports = (api) => { | ||
const env = api.env(); | ||
|
||
const base = {}; | ||
|
||
const browser = cloneDeep(base); | ||
Object.assign(browser, { | ||
ignore: [ | ||
'src/node' | ||
] | ||
}); | ||
|
||
const node = cloneDeep(base); | ||
|
||
const test = cloneDeep(node); | ||
|
||
switch (env) { | ||
case 'browser': | ||
return browser; | ||
case 'node': | ||
return node; | ||
case 'test': | ||
return test; | ||
default: | ||
return base; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
build-test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
engine-strict = true | ||
package-lock = false | ||
save-exact = true | ||
scripts-prepend-node-path = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# `embarkjs-snark` | ||
|
||
> zkSnarks plugin for embarkjs | ||
Exposes functions for interaction with zkSNARKS. See [`embark-snark` README](../../plugins/snark/README.md) for more information. | ||
|
||
Visit [embark.status.im](https://embark.status.im/) to get started with | ||
[Embark](https://github.com/embark-framework/embark). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
{ | ||
"name": "embarkjs-snark", | ||
"version": "5.1.1-nightly.2", | ||
"author": "Iuri Matias <iuri.matias@gmail.com>", | ||
"contributors": [ | ||
"Eric Mastro <eric.mastro@gmail.com> (https://github.com/emizzle/)" | ||
], | ||
"description": "zkSnarks plugin for embarkjs", | ||
"homepage": "https://github.com/embark-framework/embark/tree/master/packages/embarkjs/snark#readme", | ||
"bugs": "https://github.com/embark-framework/embark/issues", | ||
"keywords": [ | ||
"blockchain", | ||
"dapps", | ||
"ethereum", | ||
"zksnarks", | ||
"snarks", | ||
"zero knowledge" | ||
], | ||
"license": "MIT", | ||
"repository": { | ||
"directory": "packages/embarkjs/snark", | ||
"type": "git", | ||
"url": "https://github.com/embark-framework/embark.git" | ||
}, | ||
"main": "./dist/node/index.js", | ||
"types": "./dist/index.d.ts", | ||
"browser": { | ||
"./dist/node/index.js": "./dist/browser/index.js", | ||
"./dist/browser/embarkjs-snark.js": "./dist/browser/browser/embarkjs-snark.js" | ||
}, | ||
"browserslist": [ | ||
"last 1 version", | ||
"not dead", | ||
"> 0.2%" | ||
], | ||
"files": [ | ||
"dist" | ||
], | ||
"embark-collective": { | ||
"build:browser": true, | ||
"build:node": true, | ||
"typecheck": { | ||
"compilerOptions": { | ||
"module": "ESNext" | ||
} | ||
} | ||
}, | ||
"scripts": { | ||
"_build": "npm run solo -- build", | ||
"_typecheck": "npm run solo -- typecheck", | ||
"ci": "npm run qa", | ||
"clean": "npm run reset", | ||
"lint": "npm-run-all lint:*", | ||
"// lint:js": "eslint test/", | ||
"lint:ts": "tslint -c tslint.json \"src/**/*.ts\"", | ||
"qa": "npm-run-all _typecheck _build", | ||
"reset": "npx rimraf coverage dist embarkjs-*.tgz package", | ||
"solo": "embark-solo" | ||
}, | ||
"dependencies": { | ||
"@babel/runtime-corejs3": "7.8.4", | ||
"core-js": "3.6.4", | ||
"embark-core": "^5.1.1-nightly.2", | ||
"embarkjs": "^5.1.1-nightly.2", | ||
"fs-extra": "8.1.0", | ||
"snarkjs": "0.1.20" | ||
}, | ||
"devDependencies": { | ||
"embark-solo": "^5.1.1-nightly.2", | ||
"eslint": "6.2.2", | ||
"eslint-config-prettier": "6.1.0", | ||
"eslint-plugin-prettier": "3.1.0", | ||
"lodash.clonedeep": "4.5.0", | ||
"npm-run-all": "4.1.5", | ||
"rimraf": "3.0.0", | ||
"tslint": "5.20.1", | ||
"typescript": "3.7.2" | ||
}, | ||
"engines": { | ||
"node": ">=10.17.0", | ||
"npm": ">=6.11.3", | ||
"yarn": ">=1.19.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* global EmbarkJS */ | ||
import { CircuitSetup, PluginConfig } from ".."; | ||
import Circuit from "../circuit"; | ||
|
||
export default class EmbarkJsSnark { | ||
[key: string]: any; | ||
private buildDir: string; | ||
private buildDirUrl: string; | ||
private contractsJsonDirUrl: string; | ||
constructor(private setups: CircuitSetup[], private config: PluginConfig) { | ||
this.buildDir = this.config.buildDir || "public/snarks/"; | ||
this.buildDirUrl = this.config.buildDirUrl || "/snarks/"; | ||
this.contractsJsonDirUrl = this.config.contractsJsonDirUrl || "/snarks/contracts/"; | ||
} | ||
|
||
buildUrl(filepath: string) { | ||
return filepath.replace(this.buildDir || "public/", this.buildDirUrl || "/"); | ||
} | ||
|
||
public async init() { | ||
for (const setup of this.setups) { | ||
if (setup.config.exclude) { | ||
continue; | ||
} | ||
if (!setup.provingKey) { | ||
throw new Error("Error getting proving key: path not provided."); | ||
} | ||
if (!setup.verificationKey) { | ||
throw new Error("Error getting verification key: path not provided."); | ||
} | ||
if (!setup.compiledCircuit) { | ||
throw new Error("Error getting compiled circuit: path not provided."); | ||
} | ||
setup.compiledCircuit = await (await fetch(this.buildUrl(setup.compiledCircuit))).json(); | ||
setup.provingKey = await (await fetch(this.buildUrl(setup.provingKey))).json(); | ||
setup.verificationKey = await (await fetch(this.buildUrl(setup.verificationKey))).json(); | ||
const verifierContractJson = await (await fetch(`${this.contractsJsonDirUrl}${setup.verifierContractName}.json`)).json(); | ||
setup.verificationContract = new EmbarkJS.Blockchain.Contract(verifierContractJson); | ||
const nameTitleCase = setup.name.charAt(0).toUpperCase() + setup.name.substr(1).toLowerCase(); | ||
this[nameTitleCase] = new Circuit(setup); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { CircuitSetup } from "."; | ||
import * as snarkjs from "snarkjs"; | ||
const { unstringifyBigInts } = require("snarkjs/src/stringifybigint"); | ||
|
||
const LOG_PREFIX = "[embarkjs-snark]: "; | ||
|
||
export default class Circuit { | ||
constructor(private setup: CircuitSetup) { | ||
this.setup.provingKey = unstringifyBigInts(this.setup.provingKey); | ||
this.setup.verificationKey = unstringifyBigInts(this.setup.verificationKey); | ||
} | ||
|
||
/** | ||
* Given public signals and a proof to prove those public signals can be verified, | ||
* generates an array of inputs the can be used to call the verifyProof function | ||
* in the verification contract (Solidity). | ||
* | ||
* @remarks Derived from the {@link https://github.com/iden3/snarkjs/blob/f2e5bc56b33aedbbbf7fed38b3f234d3d2b1adb7/cli.js#L365-L392 | "generatecall" snarkjs cli function} | ||
* | ||
* @param publicSignals - public inputs to be verified using the proof | ||
* @param proof - the proof used to verify the inputs are valid | ||
* @returns an array of solidity inputs that can be used to call the "verifyProof" | ||
* function of the deployed vertificadtion contract | ||
*/ | ||
private generateSolidityInputs(publicSignals, proof): string[] { | ||
publicSignals = unstringifyBigInts(publicSignals); | ||
proof = unstringifyBigInts(proof); | ||
|
||
const p256 = (n) => { | ||
let nstr = n.toString(16); | ||
while (nstr.length < 64) { nstr = "0" + nstr; } | ||
nstr = `0x${nstr}`; | ||
return nstr; | ||
}; | ||
|
||
let inputs = ""; | ||
for (const publicSignal of publicSignals) { | ||
if (inputs !== "") { inputs = inputs + ","; } | ||
inputs = inputs + p256(publicSignal); | ||
} | ||
|
||
let S; | ||
if ((typeof proof.protocol === "undefined") || (proof.protocol === "original")) { | ||
S = [ | ||
[proof.pi_a[0], proof.pi_a[1]], | ||
[proof.pi_ap[0], proof.pi_ap[1]], | ||
[[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]], | ||
[proof.pi_bp[0], proof.pi_bp[1]], | ||
[proof.pi_c[0], proof.pi_c[1]], | ||
[proof.pi_cp[0], proof.pi_cp[1]], | ||
[proof.pi_h[0], proof.pi_h[1]], | ||
[proof.pi_kp[0], proof.pi_kp[1]] | ||
]; | ||
} else if ((proof.protocol === "groth") || (proof.protocol === "kimleeoh")) { | ||
S = [ | ||
[proof.pi_a[0], proof.pi_a[1]], | ||
[[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]], | ||
[proof.pi_c[0], proof.pi_c[1]] | ||
]; | ||
} else { | ||
throw new Error("InvalidProof"); | ||
} | ||
|
||
const two56ify = (arr: any[]): any[] => { | ||
return arr.map(n => { | ||
if (Array.isArray(n)) { | ||
return two56ify(n); | ||
} | ||
return p256(n); | ||
}); | ||
}; | ||
S = two56ify(S); | ||
S.push([inputs]); | ||
return S; | ||
} | ||
|
||
public async calculate(inputs: any) { | ||
const circuit = new snarkjs.Circuit(this.setup.compiledCircuit); | ||
const witness = circuit.calculateWitness(inputs); | ||
const { proof, publicSignals } = snarkjs[this.setup.config.protocol].genProof( | ||
this.setup.provingKey, | ||
witness | ||
); | ||
|
||
return { proof, publicSignals }; | ||
} | ||
|
||
public async verify(inputs: any) { | ||
console.log(`${LOG_PREFIX}NOTE -- Private inputs will **not** be sent to the blockchain. They are used to calculate the witness and generate a proof.`); | ||
if (!this.setup.verificationContract) { | ||
return console.error(`Error verifying inputs, verification contract for '${this.setup.name}' not found.`); | ||
} | ||
|
||
console.log(`${LOG_PREFIX}Calculating witness and generating proof...`); | ||
const { proof, publicSignals } = await this.calculate(inputs); | ||
const solidityInputs = this.generateSolidityInputs(publicSignals, proof); | ||
|
||
console.log(`${LOG_PREFIX}Verifying inputs on chain...`); | ||
return await this.setup.verificationContract.methods.verifyProof(...solidityInputs).call(); | ||
} | ||
|
||
public async verifyOffChain(inputs) { | ||
console.log(`${LOG_PREFIX}Calculating witness and generating proof...`); | ||
const { proof, publicSignals } = await this.calculate(inputs); | ||
|
||
console.log(`${LOG_PREFIX}Verifying inputs off chain...`); | ||
return snarkjs[this.setup.config.protocol].isValid(this.setup.verificationKey, proof, publicSignals); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import Circuit from "./circuit"; | ||
import * as fs from "fs-extra"; | ||
import { CircuitSetup } from "."; | ||
|
||
export default class EmbarkJsSnark { | ||
[key: string]: any; | ||
constructor(private setups: CircuitSetup[]) { } | ||
|
||
public async init() { | ||
|
||
for (const setup of this.setups) { | ||
if (setup.config.exclude) { | ||
continue; | ||
} | ||
if (!setup.provingKey) { | ||
throw new Error("Error getting proving key: path not provided."); | ||
} | ||
if (!setup.verificationKey) { | ||
throw new Error("Error getting verification key: path not provided."); | ||
} | ||
if (!setup.compiledCircuit) { | ||
throw new Error("Error getting compiled circuit: path not provided."); | ||
} | ||
setup.compiledCircuit = await fs.readJson(setup.compiledCircuit); | ||
setup.provingKey = await fs.readJson(setup.provingKey); | ||
setup.verificationKey = await fs.readJson(setup.verificationKey); | ||
const nameTitleCase = setup.name.charAt(0).toUpperCase() + setup.name.substr(1).toLowerCase(); | ||
this[nameTitleCase] = new Circuit(setup); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import EmbarkJsSnark from "./embarkjs-snark"; | ||
export default EmbarkJsSnark; | ||
export type SnarksProtocol = 'original' | 'groth' | 'kimleeoh'; | ||
|
||
export interface CircuitConfig { | ||
protocol?: SnarksProtocol; | ||
exclude?: boolean; | ||
} | ||
|
||
export interface PluginConfig { | ||
circuits: string[]; | ||
circuitsConfig: { | ||
[key: string]: CircuitConfig; | ||
}; | ||
buildDir: string; | ||
buildDirUrl: string; | ||
contractsBuildDir: string; | ||
contractsJsonDirUrl: string; | ||
} | ||
|
||
export interface CircuitSetup { | ||
provingKey?: string; | ||
verificationKey?: string; | ||
compiledCircuit?: string; | ||
config: CircuitConfig; | ||
filepath: string; | ||
name: string; | ||
verifierContractName?: string; | ||
verificationContract?: any; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
declare const EmbarkJS: any; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import * as embarkSnark from '..'; | ||
|
||
module.exports = embarkSnark; |
Oops, something went wrong.