From 7fb5cf01ef46bcc1388e8ee6216aa58632b4db3e Mon Sep 17 00:00:00 2001 From: dark64 Date: Wed, 26 Jan 2022 14:39:08 +0100 Subject: [PATCH] wip --- zokrates_core/src/ir/serialize.rs | 2 +- zokrates_js/Cargo.toml | 4 +- zokrates_js/index.d.ts | 131 +++++++---- zokrates_js/src/lib.rs | 17 +- zokrates_js/tests/tests.js | 376 ++++++++++++------------------ zokrates_js/wrapper.js | 167 ++++++++----- 6 files changed, 358 insertions(+), 339 deletions(-) diff --git a/zokrates_core/src/ir/serialize.rs b/zokrates_core/src/ir/serialize.rs index 8b805e37..4e973052 100644 --- a/zokrates_core/src/ir/serialize.rs +++ b/zokrates_core/src/ir/serialize.rs @@ -68,8 +68,8 @@ impl ProgEnum { pub fn curve(&self) -> &'static str { match self { - ProgEnum::Bls12_381Program(_) => Bls12_377Field::name(), ProgEnum::Bn128Program(_) => Bn128Field::name(), + ProgEnum::Bls12_381Program(_) => Bls12_381Field::name(), ProgEnum::Bls12_377Program(_) => Bls12_377Field::name(), ProgEnum::Bw6_761Program(_) => Bw6_761Field::name(), } diff --git a/zokrates_js/Cargo.toml b/zokrates_js/Cargo.toml index be3d94e7..1e2b3baf 100644 --- a/zokrates_js/Cargo.toml +++ b/zokrates_js/Cargo.toml @@ -18,5 +18,5 @@ zokrates_field = { path = "../zokrates_field", default-features = false, feature zokrates_abi = { path = "../zokrates_abi" } console_error_panic_hook = "0.1.6" -[package.metadata.wasm-pack.profile.release] -wasm-opt = false \ No newline at end of file +# [package.metadata.wasm-pack.profile.release] +# wasm-opt = false \ No newline at end of file diff --git a/zokrates_js/index.d.ts b/zokrates_js/index.d.ts index 02b21852..74b017f6 100644 --- a/zokrates_js/index.d.ts +++ b/zokrates_js/index.d.ts @@ -1,5 +1,4 @@ -declare module 'zokrates-js' { - +declare module "zokrates-js" { export type Curve = "bn128" | "bls12_381" | "bls12_377" | "bw6_761"; export type Backend = "bellman" | "ark"; export type Scheme = "g16" | "gm17" | "marlin"; @@ -11,79 +10,129 @@ declare module 'zokrates-js' { export type G2Affine = [Fq2, Fq2]; export type ProvingKey = Uint8Array; - export type ResolveCallback = (location: string, path: string) => ResolverResult; + export type ResolveCallback = ( + location: string, + path: string + ) => ResolverResult; export interface CompileConfig { - allow_unconstrained_variables?: boolean, - isolate_branches?: boolean + allow_unconstrained_variables?: boolean; + isolate_branches?: boolean; } export interface CompileOptions { - location?: string, - resolveCallback?: ResolveCallback, - config?: CompileConfig + location?: string; + resolveCallback?: ResolveCallback; + config?: CompileConfig; } export interface VerificationKey { - alpha: G1Affine, - beta: G2Affine, - gamma: G2Affine, - delta: G2Affine, - gamma_abc: G1Affine[] + alpha: G1Affine; + beta: G2Affine; + gamma: G2Affine; + delta: G2Affine; + gamma_abc: G1Affine[]; } export interface ProofPoints { - a: G1Affine, - b: G2Affine, - c: G1Affine + a: G1Affine; + b: G2Affine; + c: G1Affine; } export interface Proof { - proof: ProofPoints, - inputs: string[] + proof: ProofPoints; + inputs: string[]; } export interface ResolverResult { - source: string, - location: string + source: string; + location: string; } export interface ComputationResult { - witness: string, - output: string + witness: string; + output: string; } export interface CompilationArtifacts { - program: Uint8Array, - abi: string, - } - - export interface SetupKeypair { - vk: VerificationKey, - pk: ProvingKey, + program: Uint8Array; + abi: string; } - export type Options = { - curve: Scheme, - backend: Backend, - scheme: Curve, + export interface SetupKeypair { + vk: VerificationKey; + pk: ProvingKey; } + export type Options = { + curve: Scheme; + backend: Backend; + scheme: Curve; + }; + type AtLeast = Partial & Pick; + export type SpecializedZoKratesProvider = { + compile( + source: string, + compileOptions?: CompileOptions + ): CompilationArtifacts; + computeWitness( + artifacts: CompilationArtifacts, + args: any[] + ): ComputationResult; + setup(program: Uint8Array): SetupKeypair; + universalSetup(size: number): Uint8Array; + setupWithSrs(srs: Uint8Array, program: Uint8Array): SetupKeypair; + generateProof( + program: Uint8Array, + witness: string, + provingKey: Uint8Array + ): Proof; + verify(verificationKey: VerificationKey, proof: Proof): boolean; + exportSolidityVerifier(verificationKey: VerificationKey): string; + }; + export interface ZoKratesProvider { - compile(source: string, options?: CompileOptions): CompilationArtifacts; - computeWitness(artifacts: CompilationArtifacts, args: any[]): ComputationResult; - setup(program: Uint8Array, options: AtLeast): SetupKeypair; - setupWithSrs(srs: Uint8Array, program: Uint8Array, options: AtLeast): SetupKeypair; + withOptions(options: Options): SpecializedZoKratesProvider; + compile( + source: string, + compileOptions?: CompileOptions + ): CompilationArtifacts; + computeWitness( + artifacts: CompilationArtifacts, + args: any[] + ): ComputationResult; + setup( + program: Uint8Array, + options: AtLeast + ): SetupKeypair; universalSetup(curve: Curve, size: number): Uint8Array; - exportSolidityVerifier(verificationKey: VerificationKey, options: AtLeast): string; - generateProof(program: Uint8Array, witness: string, provingKey: Uint8Array, options: AtLeast): Proof; - verify(verificationKey: VerificationKey, proof: Proof, options: Options): boolean; + setupWithSrs( + srs: Uint8Array, + program: Uint8Array, + options: AtLeast + ): SetupKeypair; + generateProof( + program: Uint8Array, + witness: string, + provingKey: Uint8Array, + options: AtLeast + ): Proof; + verify( + verificationKey: VerificationKey, + proof: Proof, + options: Options + ): boolean; + exportSolidityVerifier( + verificationKey: VerificationKey, + options: AtLeast + ): string; } export interface Metadata { - version: string + version: string; } export function initialize(): Promise; diff --git a/zokrates_js/src/lib.rs b/zokrates_js/src/lib.rs index 447fa662..cb40045e 100644 --- a/zokrates_js/src/lib.rs +++ b/zokrates_js/src/lib.rs @@ -67,14 +67,14 @@ impl<'a> Resolver for JsResolver<'a> { ) .map_err(|_| { Error::new(format!( - "Error thrown in JS callback: could not resolve {}", + "Error thrown in JS callback: could not resolve `{}`", import_location.display() )) })?; if value.is_null() || value.is_undefined() { Err(Error::new(format!( - "Could not resolve {}", + "Could not resolve `{}`", import_location.display() ))) } else { @@ -210,15 +210,10 @@ pub fn compile( location: JsValue, resolve_callback: &js_sys::Function, config: JsValue, - options: JsValue, + curve: JsValue, ) -> Result { - let options: serde_json::Value = options.into_serde().unwrap(); - let curve = CurveParameter::try_from( - options["curve"] - .as_str() - .ok_or_else(|| JsValue::from_str("missing field `curve` in `options` object"))?, - ) - .map_err(|e| JsValue::from_str(&e))?; + let curve = CurveParameter::try_from(curve.as_string().unwrap().as_str()) + .map_err(|e| JsValue::from_str(&e))?; match curve { CurveParameter::Bn128 => { @@ -358,7 +353,7 @@ pub fn universal_setup(curve: JsValue, size: u32) -> Result, JsValue> { Ok(internal::universal_setup_of_size::(size)) } c => Err(JsValue::from_str(&format!( - "Unsupported curve `{:?}` provided in universal setup", + "Unsupported curve `{:?}` provided to universal setup", c ))), } diff --git a/zokrates_js/tests/tests.js b/zokrates_js/tests/tests.js index 7e8f8a4a..7afdfa5a 100644 --- a/zokrates_js/tests/tests.js +++ b/zokrates_js/tests/tests.js @@ -1,234 +1,166 @@ -const assert = require('assert'); -const { initialize } = require('../node/index.js'); +const assert = require("assert"); +const { initialize } = require("../node/index.js"); -describe('tests', function() { - let zokrates; +describe("tests", function () { + let zokrates; + + // initialize once before running tests + before((done) => { + initialize().then((provider) => { + zokrates = provider; + done(); + }); + }); + + describe("compilation", () => { + it("should compile", () => { + assert.doesNotThrow(() => { + const artifacts = zokrates.compile("def main() -> field: return 42"); + assert.ok(artifacts !== undefined); + }); + }); + + it("should throw on invalid code", () => { + assert.throws(() => zokrates.compile(":-)")); + }); + + it("should resolve stdlib module", () => { + const stdlib = require("../stdlib.json"); + assert.doesNotThrow(() => { + const code = `import "${ + Object.keys(stdlib)[0] + }" as func\ndef main(): return`; + zokrates.compile(code); + }); + }); + + it("should resolve user module", () => { + assert.doesNotThrow(() => { + const code = + 'import "test" as test\ndef main() -> field: return test()'; + const options = { + resolveCallback: (_, path) => { + return { + source: "def main() -> (field): return 1", + location: path, + }; + }, + }; + zokrates.compile(code, options); + }); + }); + + it("should throw on unresolved module", () => { + assert.throws(() => { + const code = + 'import "test" as test\ndef main() -> field: return test()'; + zokrates.compile(code); + }); + }); + }); + + describe("computation", () => { + it("should compute with valid inputs", () => { + assert.doesNotThrow(() => { + const code = "def main(private field a) -> field: return a * a"; + const artifacts = zokrates.compile(code); + + const result = zokrates.computeWitness(artifacts, ["2"]); + const output = JSON.parse(result.output); + assert.deepEqual(output, ["4"]); + }); + }); + + it("should throw on invalid input count", () => { + assert.throws(() => { + const code = "def main(private field a) -> field: return a * a"; + const artifacts = zokrates.compile(code); + zokrates.computeWitness(artifacts, ["1", "2"]); + }); + }); + + it("should throw on invalid input type", () => { + assert.throws(() => { + const code = "def main(private field a) -> field: return a * a"; + const artifacts = zokrates.compile(code); + zokrates.computeWitness(artifacts, [true]); + }); + }); + }); + + const runWithOptions = (options) => { + let artifacts; + let computationResult; + let keypair; + let proof; - // initialize once before running tests before((done) => { - initialize().then((provider) => { - zokrates = provider; - done(); - }); + const code = "def main(private field a) -> field: return a + a"; + artifacts = zokrates.compile(code, { curve: options.curve }); + computationResult = zokrates.computeWitness(artifacts, ["2"]); + done(); }); - describe("compilation", () => { - it('should compile', () => { - assert.doesNotThrow(() => { - const artifacts = zokrates.compile("def main() -> field: return 42"); - assert.ok(artifacts !== undefined); - }) - }); - - it('should throw on invalid code', () => { - assert.throws(() => zokrates.compile(":-)")); - }); - - it('should resolve stdlib module', () => { - const stdlib = require('../stdlib.json'); - assert.doesNotThrow(() => { - const code = `import "${Object.keys(stdlib)[0]}" as func\ndef main(): return`; - zokrates.compile(code); - }); - }); - - it('should resolve user module', () => { - assert.doesNotThrow(() => { - const code = 'import "test" as test\ndef main() -> field: return test()'; - const options = { - resolveCallback: (_, path) => { - return { - source: "def main() -> (field): return 1", - location: path - } - } - }; - zokrates.compile(code, options); - }); - }); - - it('should throw on unresolved module', () => { - assert.throws(() => { - const code = 'import "test" as test\ndef main() -> field: return test()'; - zokrates.compile(code); - }); - }); + it("setup", () => { + assert.doesNotThrow(() => { + if (options.scheme === "marlin") { + const srs = zokrates.universalSetup(options.curve, 2); + keypair = zokrates.setupWithSrs(srs, artifacts.program, options); + } else { + keypair = zokrates.setup(artifacts.program, options); + } + }); }); - describe("computation", () => { - it('should compute with valid inputs', () => { - assert.doesNotThrow(() => { - const code = 'def main(private field a) -> field: return a * a'; - const artifacts = zokrates.compile(code); - - const result = zokrates.computeWitness(artifacts, ["2"]); - const output = JSON.parse(result.output); - assert.deepEqual(output, ["4"]); - }); - }); - - it('should throw on invalid input count', () => { - assert.throws(() => { - const code = 'def main(private field a) -> field: return a * a'; - const artifacts = zokrates.compile(code); - zokrates.computeWitness(artifacts, ["1", "2"]); - }); - }); - - it('should throw on invalid input type', () => { - assert.throws(() => { - const code = 'def main(private field a) -> field: return a * a'; - const artifacts = zokrates.compile(code); - zokrates.computeWitness(artifacts, [true]); - }); - }); + it("generate proof", () => { + assert.doesNotThrow(() => { + proof = zokrates.generateProof( + artifacts.program, + computationResult.witness, + keypair.pk, + options + ); + assert.ok(proof !== undefined); + assert.ok(proof.proof.hasOwnProperty("a")); + assert.ok(proof.proof.hasOwnProperty("b")); + assert.ok(proof.proof.hasOwnProperty("c")); + assert.equal(proof.inputs.length, 1); + }); }); - describe("bellman", () => { - describe("groth16", () => { - const options = { - backend: "bellman", - curve: "bn128", - scheme: "g16" - }; - - let artifacts; - let computationResult; - let keypair; - let proof; - - before((done) => { - const code = 'def main(private field a) -> field: return a * a'; - artifacts = zokrates.compile(code); - computationResult = zokrates.computeWitness(artifacts, ["2"]); - done(); - }); - - it("setup", () => { - assert.doesNotThrow(() => { - keypair = zokrates.setup(artifacts.program, options); - }); - }); - - it("generate proof", () => { - assert.doesNotThrow(() => { - proof = zokrates.generateProof(artifacts.program, computationResult.witness, keypair.pk, options); - assert.ok(proof !== undefined); - assert.deepEqual(proof.inputs, ["0x0000000000000000000000000000000000000000000000000000000000000004"]); - }); - }); - - it("export solidity verifier", () => { - let verifier = zokrates.exportSolidityVerifier(keypair.vk, options); - assert(verifier.length > 0); - }); - - it("verify with valid proof", () => { - assert.doesNotThrow(() => { - assert(zokrates.verify(keypair.vk, proof, options) === true); - }); - }); - - it("verify with invalid proof", () => { - // falsify proof - proof["proof"]["a"][0] = "0x0000000000000000000000000000000000000000000000000000000000000000"; - assert(zokrates.verify(keypair.vk, proof, options) === false); - }); - }); + it("verify with valid proof", () => { + assert.doesNotThrow(() => { + assert(zokrates.verify(keypair.vk, proof, options) === true); + }); }); - describe("ark", () => { - describe("gm17", () => { - const options = { - backend: "ark", - curve: "bn128", - scheme: "gm17" - }; - - let artifacts; - let computationResult; - let keypair; - let proof; - - before((done) => { - const code = 'def main(private field a) -> field: return a * a'; - artifacts = zokrates.compile(code); - computationResult = zokrates.computeWitness(artifacts, ["2"]); - done(); - }); - - it("setup", () => { - assert.doesNotThrow(() => { - keypair = zokrates.setup(artifacts.program, options); - }); - }); - - it("generate proof", () => { - assert.doesNotThrow(() => { - proof = zokrates.generateProof(artifacts.program, computationResult.witness, keypair.pk, options); - assert.ok(proof !== undefined); - assert.deepEqual(proof.inputs, ["0x0000000000000000000000000000000000000000000000000000000000000004"]); - }); - }); - - it("verify with valid proof", () => { - assert.doesNotThrow(() => { - assert(zokrates.verify(keypair.vk, proof, options) === true); - }); - }); - - it("verify with invalid proof", () => { - // falsify proof - proof["proof"]["a"][0] = "0x0000000000000000000000000000000000000000000000000000000000000000"; - assert(zokrates.verify(keypair.vk, proof, options) === false); - }); - }); - - describe("marlin", () => { - const options = { - backend: "ark", - curve: "bn128", - scheme: "marlin" - }; - - let artifacts; - let computationResult; - let keypair; - let proof; - - before((done) => { - const code = 'def main(private field a, private field b) -> bool: return a * a == b'; - artifacts = zokrates.compile(code); - computationResult = zokrates.computeWitness(artifacts, ["2", "4"]); - done(); - }); - - it("setup", () => { - assert.doesNotThrow(() => { - const srs = zokrates.universalSetup("bn128", 4); - keypair = zokrates.setupWithSrs(srs, artifacts.program, options); - }); - }); - - it("generate proof", () => { - assert.doesNotThrow(() => { - proof = zokrates.generateProof(artifacts.program, computationResult.witness, keypair.pk, options); - assert.ok(proof !== undefined); - assert.deepEqual(proof.inputs, ["0x0000000000000000000000000000000000000000000000000000000000000001"]); - }); - }); - - it("verify with valid proof", () => { - assert.doesNotThrow(() => { - assert(zokrates.verify(keypair.vk, proof, options) === true); - }); - }); - - it("verify with invalid proof", () => { - // falsify proof - proof["inputs"][0] = "0x0000000000000000000000000000000000000000000000000000000000000000"; - assert(zokrates.verify(keypair.vk, proof, options) === false); - }); - }); + it("falsify proof", () => { + let tmp = proof["proof"]["a"][0]; + proof["proof"]["a"][0] = proof["proof"]["a"][1]; + proof["proof"]["a"][1] = tmp; + assert(zokrates.verify(keypair.vk, proof, options) === false); }); -}); \ No newline at end of file + }; + + describe("bellman", () => { + describe("groth16", () => { + for (const curve of ["bn128", "bls12_381"]) { + describe(curve, () => + runWithOptions({ backend: "bellman", scheme: "g16", curve }) + ); + } + }); + }); + + describe("ark", () => { + for (const scheme of ["gm17", "marlin"]) { + describe(scheme, () => { + for (const curve of ["bn128", "bls12_377", "bw6_761"]) { + describe(curve, () => + runWithOptions({ backend: "ark", scheme: "gm17", curve }) + ); + } + }); + } + }); +}); diff --git a/zokrates_js/wrapper.js b/zokrates_js/wrapper.js index b94cd7be..2665b39e 100644 --- a/zokrates_js/wrapper.js +++ b/zokrates_js/wrapper.js @@ -1,71 +1,114 @@ const getAbsolutePath = (basePath, relativePath) => { - if (relativePath[0] !== '.') { - return relativePath; - } - var stack = basePath.split('/'); - var chunks = relativePath.split('/'); - stack.pop(); + if (relativePath[0] !== ".") { + return relativePath; + } + var stack = basePath.split("/"); + var chunks = relativePath.split("/"); + stack.pop(); - for (var i = 0; i < chunks.length; i++) { - if (chunks[i] == '.') { - continue; - } else if (chunks[i] == '..') { - stack.pop(); - } else { - stack.push(chunks[i]); - } + for (var i = 0; i < chunks.length; i++) { + if (chunks[i] == ".") { + continue; + } else if (chunks[i] == "..") { + stack.pop(); + } else { + stack.push(chunks[i]); } - return stack.join('/'); -} + } + return stack.join("/"); +}; const getImportPath = (currentLocation, importLocation) => { - let path = getAbsolutePath(currentLocation, importLocation); - const extension = path.slice((path.lastIndexOf(".") - 1 >>> 0) + 2); - return extension ? path : path.concat('.zok'); -} + let path = getAbsolutePath(currentLocation, importLocation); + const extension = path.slice(((path.lastIndexOf(".") - 1) >>> 0) + 2); + return extension ? path : path.concat(".zok"); +}; module.exports = (dep) => { - const { zokrates, stdlib } = dep; + const { zokrates, stdlib } = dep; - const resolveFromStdlib = (currentLocation, importLocation) => { - let key = getImportPath(currentLocation, importLocation); - let source = stdlib[key]; - return source ? { source, location: key } : null; - } + const resolveFromStdlib = (currentLocation, importLocation) => { + let key = getImportPath(currentLocation, importLocation); + let source = stdlib[key]; + return source ? { source, location: key } : null; + }; - return { - compile: (source, options = {}) => { - const { curve = "bn128", location = "main.zok", resolveCallback = () => null, config = {} } = options; - const callback = (currentLocation, importLocation) => { - return resolveFromStdlib(currentLocation, importLocation) || resolveCallback(currentLocation, importLocation); - }; - const { program, abi } = zokrates.compile(source, location, callback, config, { curve }); - return { - program: new Uint8Array(program), - abi - } - }, - computeWitness: (artifacts, args) => { - const { program, abi } = artifacts; - return zokrates.compute_witness(program, abi, JSON.stringify(Array.from(args))); - }, - setup: (program, options) => { - return zokrates.setup(program, options); - }, - universalSetup: (curve, size) => { - return zokrates.universal_setup(curve, size); - }, - setupWithSrs: (srs, program, options) => { - return zokrates.setup_with_srs(srs, program, options); - }, - generateProof: (program, witness, provingKey, options) => { - return zokrates.generate_proof(program, witness, provingKey, options); - }, - verify: (vk, proof, options) => { - return zokrates.verify(vk, proof, options); - }, - exportSolidityVerifier: (vk, options) => { - return zokrates.export_solidity_verifier(vk, options); - } - } -}; \ No newline at end of file + const defaultProvider = { + compile: (source, compileOptions = {}) => { + const { + curve = "bn128", + location = "main.zok", + resolveCallback = () => null, + config = {}, + } = compileOptions; + const callback = (currentLocation, importLocation) => { + return ( + resolveFromStdlib(currentLocation, importLocation) || + resolveCallback(currentLocation, importLocation) + ); + }; + const { program, abi } = zokrates.compile( + source, + location, + callback, + config, + curve + ); + return { + program: new Uint8Array(program), + abi, + }; + }, + computeWitness: (artifacts, args) => { + const { program, abi } = artifacts; + return zokrates.compute_witness( + program, + abi, + JSON.stringify(Array.from(args)) + ); + }, + setup: (program, options) => { + return zokrates.setup(program, options); + }, + universalSetup: (curve, size) => { + return zokrates.universal_setup(curve, size); + }, + setupWithSrs: (srs, program, options) => { + return zokrates.setup_with_srs(srs, program, options); + }, + generateProof: (program, witness, provingKey, options) => { + return zokrates.generate_proof(program, witness, provingKey, options); + }, + verify: (vk, proof, options) => { + return zokrates.verify(vk, proof, options); + }, + exportSolidityVerifier: (vk, options) => { + return zokrates.export_solidity_verifier(vk, options); + }, + }; + + const withOptions = (options) => ({ + compile: (source, compileOptions = {}) => + defaultProvider.compile(source, { + ...compileOptions, + curve: options.curve, + }), + computeWitness: (artifacts, args) => + defaultProvider.computeWitness(artifacts, args), + setup: (program) => defaultProvider.setup(program, options), + universalSetup: (size) => + defaultProvider.universalSetup(options.curve, size), + setupWithSrs: (srs, program) => + defaultProvider.setupWithSrs(srs, program, options), + generateProof: (program, witness, provingKey) => + defaultProvider.generateProof(program, witness, provingKey, options), + verify: (vk, proof) => defaultProvider.verify(vk, proof, options), + exportSolidityVerifier: (vk) => + defaultProvider.exportSolidityVerifier(vk, options), + }); + + return { + withOptions, + ...defaultProvider, + }; +};