1
0
Fork 0
mirror of synced 2025-09-23 12:18:44 +00:00

fix conflicts

This commit is contained in:
schaeff 2020-08-04 17:34:23 +02:00
commit 2605559f4c
17 changed files with 258 additions and 188 deletions

View file

@ -233,12 +233,6 @@ err() {
exit 1
}
need() {
if ! command -v $1 > /dev/null 2>&1; then
err "need $1 (command not found)"
fi
}
main() {
need_cmd curl
@ -259,12 +253,11 @@ main() {
done
# Dependencies
need basename
need curl
need install
need mkdir
need mktemp
need tar
need_cmd curl
need_cmd install
need_cmd mkdir
need_cmd mktemp
need_cmd tar
git="ZoKrates/ZoKrates"

View file

@ -1,51 +1,33 @@
# zokrates.js
You can get JavaScript bindings for ZoKrates by running
JavaScript bindings for [ZoKrates](https://github.com/Zokrates/ZoKrates).
```bash
npm install zokrates-js
```
## API
## Importing
| Function | Description |
| ------ | ------ |
| initialize | Loads binding wasm module and returns a promise with ZoKrates provider |
| compile | Compiles source code into ZoKrates internal representation of arithmetic circuits |
| computeWitness | Computes a valid assignment of the variables, which include the results of the computation |
| setup | Generates a trusted setup for the compiled program |
| exportSolidityVerifier | Generates a Solidity contract which contains the generated verification key and a public function to verify a solution to the compiled program |
| generateProof | Generates a proof for a computation of the compiled program |
## Usage
### Importing
Bundlers
##### Bundlers
**Note:** As this library uses a model where the wasm module itself is natively an ES module, you will need a bundler of some form.
Currently the only known bundler known to be fully compatible with `zokrates-js` is [Webpack](https://webpack.js.org/).
The choice of this default was done to reflect the trends of the JS ecosystem.
```js
import { initialize } from 'zokrates-js';
```
Node
##### Node
```js
const { initialize } = require('zokrates-js/node');
```
### Example
## Example
```js
function importResolver(currentLocation, importLocation) {
// implement your resolving logic here
return {
source: "def main(): return",
location: importLocation
};
}
initialize().then((zokratesProvider) => {
const source = "def main(private field a) -> field: return a * a";
// compilation
const artifacts = zokratesProvider.compile(source, "main", importResolver);
const artifacts = zokratesProvider.compile(source);
// computation
const { witness, output } = zokratesProvider.computeWitness(artifacts, ["2"]);
@ -60,3 +42,103 @@ initialize().then((zokratesProvider) => {
const verifier = zokratesProvider.exportSolidityVerifier(keypair.vk, "v1");
});
```
## API
##### initialize()
Returns an initialized `ZoKratesProvider` as a promise.
```js
initialize().then(zokratesProvider => {
// call api functions here
});
```
Returns: `Promise<ZoKratesProvider>`
##### compile(source[, options])
Compiles source code into ZoKrates internal representation of arithmetic circuits.
Parameters:
* `source` - Source code to compile
* `options` - Compilation options
Returns: `CompilationArtifacts`
**Examples:**
Compilation:
```js
const artifacts = zokratesProvider.compile("def main() -> (): return");
```
Compilation with custom options:
```js
const source = "...";
const options = {
location: "main.zok", // location of the root module
resolveCallback: (currentLocation, importLocation) => {
console.log(currentLocation + ' is importing ' + importLocation);
return {
source: "def main() -> (): return",
location: importLocation
};
},
config: {
is_release: true
}
};
const artifacts = zokratesProvider.compile(source, options);
```
**Note:** The `resolveCallback` function is used to resolve dependencies.
This callback receives the current module location and the import location of the module which is being imported.
The callback must synchronously return either an error, `null` or a valid `ResolverResult` object like shown in the example above.
##### computeWitness(artifacts, args)
Computes a valid assignment of the variables, which include the results of the computation.
Parameters:
* `artifacts` - Compilation artifacts
* `args` - Array of arguments (eg. `["1", "2", true]`)
Returns: `ComputationResult`
**Example:**
```js
const code = 'def main(private field a) -> (field): return a * a';
const artifacts = zokratesProvider.compile(code);
const { witness, output } = zokratesProvider.computeWitness(artifacts, ["2"]);
console.log(witness); // Resulting witness which can be used to generate a proof
console.log(output); // Computation output: ["4"]
```
##### setup(program)
Generates a trusted setup for the compiled program.
Parameters:
* `program` - Compiled program
Returns: `SetupKeypair`
##### exportSolidityVerifier(verificationKey, abi)
Generates a Solidity contract which contains the generated verification key and a public function to verify proofs of computation of the compiled program.
Parameters:
* `verificationKey` - Verification key from the setup keypair
* `abi` - Abi version (`"v1"` | `"v2"`)
Returns: `string`
##### generateProof(program, witness, provingKey)
Generates a proof for a computation of the compiled program.
Parameters:
* `program` - Compiled program
* `witness` - Witness (valid assignment of the variables) from the computation result
* `provingKey` - Proving key from the setup keypair
Returns: `Proof`

View file

@ -409,23 +409,13 @@ impl<'ast, T: Field> From<pest::Spread<'ast>> for absy::SpreadNode<'ast, T> {
}
}
impl<'ast, T: Field> From<pest::Range<'ast>> for absy::RangeNode<T> {
fn from(range: pest::Range<'ast>) -> absy::RangeNode<T> {
impl<'ast, T: Field> From<pest::Range<'ast>> for absy::RangeNode<'ast, T> {
fn from(range: pest::Range<'ast>) -> absy::RangeNode<'ast, T> {
use absy::NodeValue;
let from = range
.from
.map(|e| match absy::ExpressionNode::from(e.0).value {
absy::Expression::FieldConstant(n) => n,
e => unimplemented!("Range bounds should be constants, found {}", e),
});
let from = range.from.map(|e| absy::ExpressionNode::from(e.0));
let to = range
.to
.map(|e| match absy::ExpressionNode::from(e.0).value {
absy::Expression::FieldConstant(n) => n,
e => unimplemented!("Range bounds should be constants, found {}", e),
});
let to = range.to.map(|e| absy::ExpressionNode::from(e.0));
absy::Range { from, to }.span(range.span)
}

View file

@ -395,7 +395,7 @@ impl<'ast, T: fmt::Debug> fmt::Debug for SpreadOrExpression<'ast, T> {
/// The index in an array selector. Can be a range or an expression.
#[derive(Clone, PartialEq)]
pub enum RangeOrExpression<'ast, T> {
Range(RangeNode<T>),
Range(RangeNode<'ast, T>),
Expression(ExpressionNode<'ast, T>),
}
@ -439,14 +439,14 @@ pub struct Spread<'ast, T> {
/// A range
#[derive(Clone, PartialEq)]
pub struct Range<T> {
pub from: Option<T>,
pub to: Option<T>,
pub struct Range<'ast, T> {
pub from: Option<ExpressionNode<'ast, T>>,
pub to: Option<ExpressionNode<'ast, T>>,
}
pub type RangeNode<T> = Node<Range<T>>;
pub type RangeNode<'ast, T> = Node<Range<'ast, T>>;
impl<'ast, T: fmt::Display> fmt::Display for Range<T> {
impl<'ast, T: fmt::Display> fmt::Display for Range<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
@ -463,7 +463,7 @@ impl<'ast, T: fmt::Display> fmt::Display for Range<T> {
}
}
impl<'ast, T: fmt::Debug> fmt::Debug for Range<T> {
impl<'ast, T: fmt::Debug> fmt::Debug for Range<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Range({:?}, {:?})", self.from, self.to)
}

View file

@ -91,7 +91,7 @@ impl<'ast> NodeValue for Variable<'ast> {}
impl<'ast> NodeValue for Parameter<'ast> {}
impl<'ast> NodeValue for Import<'ast> {}
impl<'ast, T: fmt::Display + fmt::Debug + PartialEq> NodeValue for Spread<'ast, T> {}
impl<T: fmt::Display + fmt::Debug + PartialEq> NodeValue for Range<T> {}
impl<'ast, T: fmt::Display + fmt::Debug + PartialEq> NodeValue for Range<'ast, T> {}
impl<T: PartialEq> PartialEq for Node<T> {
fn eq(&self, other: &Node<T>) -> bool {

View file

@ -1659,17 +1659,50 @@ impl<'ast> Checker<'ast> {
let array_size = array.size();
let inner_type = array.inner_type().clone();
// check that the bounds are valid expressions
let from = r
.value
.from
.map(|v| v.to_dec_string().parse::<usize>().unwrap())
.unwrap_or(0);
.map(|e| self.check_expression(e, module_id, &types))
.unwrap_or(Ok(FieldElementExpression::Number(T::from(0)).into()))?;
let to = r
.value
.to
.map(|v| v.to_dec_string().parse::<usize>().unwrap())
.unwrap_or(array_size);
.map(|e| self.check_expression(e, module_id, &types))
.unwrap_or(Ok(FieldElementExpression::Number(T::from(
array_size,
))
.into()))?;
// check the bounds are field constants
// Note: it would be nice to allow any field expression, and check it's a constant after constant propagation,
// but it's tricky from a type perspective: the size of the slice changes the type of the resulting array,
// which doesn't work well with our static array approach. Enabling arrays to have unknown size introduces a lot
// of complexity in the compiler, as function selection in inlining requires knowledge of the array size, but
// determining array size potentially requires inlining and propagating. This suggests we would need semantic checking
// to happen iteratively with inlining and propagation, which we can't do now as we go from absy to typed_absy
let from = match from {
TypedExpression::FieldElement(FieldElementExpression::Number(n)) => Ok(n.to_dec_string().parse::<usize>().unwrap()),
e => Err(ErrorInner {
pos: Some(pos),
message: format!(
"Expected the lower bound of the range to be a constant field, found {}",
e
),
})
}?;
let to = match to {
TypedExpression::FieldElement(FieldElementExpression::Number(n)) => Ok(n.to_dec_string().parse::<usize>().unwrap()),
e => Err(ErrorInner {
pos: Some(pos),
message: format!(
"Expected the higher bound of the range to be a constant field, found {}",
e
),
})
}?;
match (from, to, array_size) {
(f, _, s) if f > s => Err(ErrorInner {

View file

@ -5,3 +5,4 @@ pkg
wasm-pack.log
stdlib
stdlib.json
metadata.json

View file

@ -6,57 +6,4 @@ JavaScript bindings for [ZoKrates](https://github.com/Zokrates/ZoKrates) project
npm install zokrates-js
```
## API
| Function | Description |
| ------ | ------ |
| initialize | Loads binding wasm module and returns a promise with ZoKrates provider |
| compile | Compiles source code into ZoKrates internal representation of arithmetic circuits |
| computeWitness | Computes a valid assignment of the variables, which include the results of the computation |
| setup | Generates a trusted setup for the compiled program |
| exportSolidityVerifier | Generates a Solidity contract which contains the generated verification key and a public function to verify a solution to the compiled program |
| generateProof | Generates a proof for a computation of the compiled program |
## Usage
### Importing
Bundlers
```js
import { initialize } from 'zokrates-js';
```
Node
```js
const { initialize } = require('zokrates-js/node');
```
### Example
```js
function importResolver(currentLocation, importLocation) {
// implement your resolving logic here
return {
source: "def main(): return",
location: importLocation
};
}
initialize().then((zokratesProvider) => {
const source = "def main(private field a) -> field: return a * a";
// compilation
const artifacts = zokratesProvider.compile(source, "main", importResolver);
// computation
const { witness, output } = zokratesProvider.computeWitness(artifacts, ["2"]);
// run setup
const keypair = zokratesProvider.setup(artifacts.program);
// generate proof
const proof = zokratesProvider.generateProof(artifacts.program, witness, keypair.pk);
// export solidity verifier
const verifier = zokratesProvider.exportSolidityVerifier(keypair.vk, "v1");
});
```
Check the offical [ZoKrates documentation](https://zokrates.github.io/zokrates_js.html) for more details.

View file

@ -3,10 +3,11 @@ const gulp = require('gulp');
const dree = require('dree');
const fs = require('fs');
const path = require('path');
const toml = require('toml');
/** stdlib constants */
const stdlibRoot = '../zokrates_stdlib/stdlib';
const output = 'stdlib.json';
const stdlibOutput = 'stdlib.json';
const options = {
extensions: ['zok']
@ -22,6 +23,17 @@ gulp.task('stdlib', (done) => {
stdlib[file.relativePath] = content;
});
fs.writeFileSync(path.resolve(__dirname, output), JSON.stringify(stdlib));
fs.writeFileSync(path.resolve(__dirname, stdlibOutput), JSON.stringify(stdlib));
done();
});
gulp.task('metadata', (done) => {
const config = toml.parse(fs.readFileSync('../zokrates_cli/Cargo.toml').toString());
const metadata = JSON.stringify({
version: config.package.version
});
fs.writeFileSync(path.resolve(__dirname, 'metadata.json'), metadata);
done();
});
gulp.task('setup', gulp.parallel('stdlib', 'metadata'));

View file

@ -4,10 +4,19 @@ declare module 'zokrates-js' {
export type G2Affine = [G1Affine, G1Affine];
export type ProvingKey = Uint8Array;
export type SolidityAbi = "v1" | "v2";
export type ResolveCallback = (location: string, path: string) => ResolverResult;
export interface CompileConfig {
is_release: boolean
}
export interface CompileOptions {
location?: string,
resolveCallback?: ResolveCallback,
config?: CompileConfig
}
export interface VerificationKey {
alpha: G1Affine,
beta: G2Affine,
@ -49,16 +58,18 @@ declare module 'zokrates-js' {
pk: ProvingKey,
}
export type SolidityAbi = "v1" | "v2";
export type ResolveCallback = (location: string, path: string) => ResolverResult;
export interface ZoKratesProvider {
compile(source: string, location: string, callback: ResolveCallback, config?: CompileConfig): CompilationArtifacts;
compile(source: string, options?: CompileOptions): CompilationArtifacts;
setup(program: Uint8Array): SetupKeypair;
computeWitness(artifacts: CompilationArtifacts, args: any[]): ComputationResult;
exportSolidityVerifier(verifyingKey: VerificationKey, abi: SolidityAbi): string;
generateProof(program: Uint8Array, witness: string, provingKey: Uint8Array): Proof;
}
export interface Metadata {
version: string
}
export function initialize(): Promise<ZoKratesProvider>;
export var metadata: Metadata;
}

View file

@ -1,9 +1,10 @@
import wrapper from './wrapper';
import stdlib from './stdlib.json';
import metadata from './metadata.json';
const initialize = async () => {
const zokrates = await import('./pkg/index.js');
return wrapper({ zokrates, stdlib });
}
export { initialize };
export { initialize, metadata };

View file

@ -1,5 +1,6 @@
const wrapper = require('../wrapper.js');
const stdlib = require('../stdlib.json');
const metadata = require('../metadata.json');
const initialize = async () => {
return wrapper({
@ -8,4 +9,4 @@ const initialize = async () => {
});
}
module.exports = { initialize };
module.exports = { initialize, metadata };

View file

@ -4572,6 +4572,12 @@
"through2": "^2.0.3"
}
},
"toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"dev": true
},
"type": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",

View file

@ -16,12 +16,13 @@
"index.d.ts",
"wrapper.js",
"stdlib.json",
"metadata.json",
"README.md"
],
"types": "index.d.ts",
"scripts": {
"wasm-pack": "wasm-pack build --out-name index --release",
"setup": "npm install && gulp stdlib",
"setup": "npm install && gulp setup",
"prebuild": "npm run setup",
"build": "npm run build:bundler && npm run build:node",
"build:bundler": "rimraf pkg && npm run wasm-pack -- --target bundler && npm run clean-pkg",
@ -39,7 +40,8 @@
"mocha": "^7.1.1",
"rimraf": "^3.0.2",
"serve": "^11.3.2",
"text-encoding": "^0.7.0"
"text-encoding": "^0.7.0",
"toml": "^3.0.0"
},
"dependencies": {}
}

View file

@ -98,22 +98,13 @@ impl<'a> Resolver<Error> for JsResolver<'a> {
pub fn compile(
source: JsValue,
location: JsValue,
resolve: &js_sys::Function,
resolve_callback: &js_sys::Function,
config: JsValue,
) -> Result<JsValue, JsValue> {
let resolver = JsResolver::new(resolve_callback);
let config: CompileConfig = config.into_serde().unwrap_or(CompileConfig::default());
let fmt_error = |e: &CompileError| format!("{}:{}", e.file().display(), e.value());
let resolver = JsResolver::new(resolve);
let config: CompileConfig = {
if config.is_object() {
config
.into_serde()
.map_err(|e| JsValue::from_str(&format!("Invalid config format: {}", e)))?
} else {
CompileConfig::default()
}
};
let artifacts: CompilationArtifacts<Bn128Field> = core_compile(
source.as_string().unwrap(),
PathBuf::from(location.as_string().unwrap()),
@ -208,8 +199,6 @@ pub fn generate_proof(program: JsValue, witness: JsValue, pk: JsValue) -> Result
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();
Ok(())
}

View file

@ -14,112 +14,113 @@ describe('tests', function() {
describe("compilation", () => {
it('should compile', function() {
assert.doesNotThrow(() => {
const artifacts = this.zokrates.compile("def main() -> field: return 42", "main");
const artifacts = this.zokrates.compile("def main() -> field: return 42");
assert.ok(artifacts !== undefined);
})
});
it('should throw on invalid code', function() {
assert.throws(() => this.zokrates.compile(":-)", "main"));
assert.throws(() => this.zokrates.compile(":-)"));
});
it('should resolve stdlib module', function() {
const stdlib = require('../stdlib.json');
assert.doesNotThrow(() => {
const code = `
def main():
return
`;
this.zokrates.compile(code, "main");
})
const code = `import "${Object.keys(stdlib)[0]}" as func\ndef main(): return`;
this.zokrates.compile(code);
});
});
it('should resolve user module', function() {
assert.doesNotThrow(() => {
const code = 'import "test" as test\ndef main() -> field: return test()';
this.zokrates.compile(code, "main", (_, path) => {
const options = {
resolveCallback: (_, path) => {
return {
source: "def main() -> field: return 1",
source: "def main() -> (field): return 1",
location: path
}
}
};
this.zokrates.compile(code, options);
});
})
});
it('should throw on invalid module', function() {
it('should throw on unresolved module', function() {
assert.throws(() => {
const code = 'import "test" as test\ndef main() -> field: return test()';
this.zokrates.compile(code, "main", (_loc, _path) => null);
})
this.zokrates.compile(code);
});
});
});
})
describe("computation", () => {
it('should compute with valid inputs', function() {
assert.doesNotThrow(() => {
const code = 'def main(private field a) -> field: return a * a';
const artifacts = this.zokrates.compile(code, "main", null);
const artifacts = this.zokrates.compile(code);
const result = this.zokrates.computeWitness(artifacts, ["2"])
const result = this.zokrates.computeWitness(artifacts, ["2"]);
const output = JSON.parse(result.output);
assert.deepEqual(output, ["4"]);
})
});
});
it('should throw on invalid input count', function() {
assert.throws(() => {
const code = 'def main(private field a) -> field: return a * a';
const artifacts = this.zokrates.compile(code, "main", null);
const artifacts = this.zokrates.compile(code);
this.zokrates.computeWitness(artifacts, ["1", "2"])
})
this.zokrates.computeWitness(artifacts, ["1", "2"]);
});
});
it('should throw on invalid input type', function() {
assert.throws(() => {
const code = 'def main(private field a) -> field: return a * a';
const artifacts = this.zokrates.compile(code, "main", null);
const artifacts = this.zokrates.compile(code);
this.zokrates.computeWitness(artifacts, [true])
})
this.zokrates.computeWitness(artifacts, [true]);
});
});
});
})
describe("setup", () => {
it('should run setup', function() {
assert.doesNotThrow(() => {
const code = 'def main(private field a) -> field: return a * a';
const artifacts = this.zokrates.compile(code, "main", null);
const artifacts = this.zokrates.compile(code);
this.zokrates.setup(artifacts.program);
})
});
})
});
});
describe("export-verifier", () => {
it('should export solidity verifier', function() {
assert.doesNotThrow(() => {
const code = 'def main(private field a) -> field: return a * a';
const artifacts = this.zokrates.compile(code, "main", null);
const artifacts = this.zokrates.compile(code);
const keypair = this.zokrates.setup(artifacts.program);
const verifier = this.zokrates.exportSolidityVerifier(keypair.vk, "v1");
assert.ok(verifier.length > 0)
})
assert.ok(verifier.length > 0);
});
});
});
})
describe("generate-proof", () => {
it('should generate proof', function() {
assert.doesNotThrow(() => {
const code = 'def main(private field a) -> field: return a * a';
const artifacts = this.zokrates.compile(code, "main", null);
const artifacts = this.zokrates.compile(code);
const computationResult = this.zokrates.computeWitness(artifacts, ["2"])
const keypair = this.zokrates.setup(artifacts.program);
const proof = this.zokrates.generateProof(artifacts.program, computationResult.witness, keypair.pk);
assert.ok(proof !== undefined);
assert.deepEqual(proof.inputs, ["0x0000000000000000000000000000000000000000000000000000000000000004"])
assert.deepEqual(proof.inputs, ["0x0000000000000000000000000000000000000000000000000000000000000004"]);
})
});
});

View file

@ -35,11 +35,12 @@ module.exports = (dep) => {
}
return {
compile: (source, location, callback) => {
let importCallback = (currentLocation, importLocation) => {
return resolveFromStdlib(currentLocation, importLocation) || callback(currentLocation, importLocation);
compile: (source, options = {}) => {
const { 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, importCallback);
const { program, abi } = zokrates.compile(source, location, callback, config);
return {
program: Array.from(program),
abi