1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

Merge pull request #1183 from Zokrates/rc/0.8.0

Release 0.8.0
This commit is contained in:
Darko Macesic 2022-07-07 15:29:16 +02:00 committed by GitHub
commit d0015e253b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
953 changed files with 20000 additions and 17174 deletions

View file

@ -22,7 +22,7 @@ jobs:
- restore-sccache-cache
- run:
name: Build
command: WITH_LIBSNARK=1 RUSTFLAGS="-D warnings" ./build.sh
command: RUSTFLAGS="-D warnings" ./build.sh
- save-sccache-cache
test:
docker:
@ -45,19 +45,12 @@ jobs:
- run:
name: Run tests
no_output_timeout: 1h
command: WITH_LIBSNARK=1 RUSTFLAGS="-D warnings" ./test.sh
command: RUSTFLAGS="-D warnings" ./test.sh
- save-sccache-cache
cpp_format:
docker:
- image: zokrates/env:latest
steps:
- checkout
- run:
name: Check cpp format (clang-format)
command: run-clang-format.py -r $(pwd)/zokrates_core/lib
wasm_test:
docker:
- image: zokrates/env:latest
resource_class: large
steps:
- checkout
- run:
@ -68,8 +61,8 @@ jobs:
- run:
name: Test on firefox
command: |
cd zokrates_core
wasm-pack test --firefox --headless -- --no-default-features --features "wasm ark"
cd zokrates_test
wasm-pack test --firefox --headless
- save-sccache-cache
integration_test:
docker:
@ -86,7 +79,7 @@ jobs:
- run:
name: Run integration tests
no_output_timeout: "30m"
command: WITH_LIBSNARK=1 RUSTFLAGS="-D warnings" ./integration_test.sh
command: RUSTFLAGS="-D warnings" ./integration_test.sh
- save-sccache-cache
deploy:
docker:
@ -256,7 +249,6 @@ workflows:
jobs:
- build
- test
- cpp_format
- wasm_test
- integration_test
- zokrates_js_build
@ -315,7 +307,6 @@ workflows:
requires:
- build
- test
- cpp_format
- wasm_test
- integration_test
- zokrates_js_build

7
.gitignore vendored
View file

@ -14,6 +14,13 @@ proof.json
universal_setup.dat
witness
# ZoKrates source files at the root of the repository
/*.zok
# snarkjs artifacts
*.wtns
*.r1cs
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
# Cargo.lock

View file

@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
https://github.com/Zokrates/ZoKrates/compare/latest...develop
## [0.8.0] - 2022-07-07
### Release
- https://github.com/Zokrates/ZoKrates/releases/tag/0.8.0 <!-- markdown-link-check-disable-line -->
### Changes
- Drop support for PGHR13 proving scheme (#1181, @schaeff)
- Use signature output for constant parameter inference (#1172, @dark64)
- Add log statements to the language (#1171, @schaeff)
- Remove multiple returns (#1170, @dark64)
- Introduce the `mut` keyword and make variables immutable by default (#1168, @schaeff)
- Drop support for libsnark (#1153, @schaeff)
- Split codebase into smaller crates (#1151, @schaeff)
- Introduce curly bracket based syntax, use a semicolon to separate statements, change the syntax of `if-else` expression (#1121, @dark64)
- Optionally export snarkjs artifacts (#1143, @schaeff)
- Fix constant inlining for tuples (#1169, @dark64)
- Change the default backend to `ark` in the CLI (#1165, @dark64)
## [0.7.14] - 2022-05-31
### Release

841
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,14 @@ members = [
"zokrates_test",
"zokrates_core_test",
"zokrates_solidity_test",
"zokrates_ark",
"zokrates_ast",
"zokrates_interpreter",
"zokrates_embed",
"zokrates_bellman",
"zokrates_proof_systems",
"zokrates_js",
"zokrates_circom"
]
exclude = ["zokrates_js"]
exclude = []

View file

@ -1,6 +1,5 @@
FROM zokrates/env:20.04 as build
ENV WITH_LIBSNARK=1
WORKDIR /build
COPY . src

View file

@ -15,14 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
build-essential \
software-properties-common \
cmake \
gnupg \
libboost-all-dev \
libgmp3-dev \
libprocps-dev \
libssl-dev \
pkg-config \
clang-format \
python-is-python3 \
python-markdown \
&& add-apt-repository ppa:mozillateam/ppa \
@ -33,7 +26,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y \
&& rustup toolchain install $RUST_VERSION --allow-downgrade --profile minimal --component rustfmt clippy \
&& curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs && npm i -g solc \
&& curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs && npm i -g solc \
&& curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh \
&& curl -sL https://raw.githubusercontent.com/Sarcasm/run-clang-format/master/run-clang-format.py > /opt/run-clang-format.py \
&& chmod +x /opt/run-clang-format.py \

View file

@ -3,8 +3,4 @@
# Exit if any subcommand fails
set -e
if [ -n "$WITH_LIBSNARK" ]; then
cargo build --package zokrates_cli --features="libsnark"
else
cargo build --package zokrates_cli
fi
cargo build --package zokrates_cli

View file

@ -4,8 +4,4 @@
set -e
export RUSTFLAGS="--remap-path-prefix=$PWD="
if [ -n "$WITH_LIBSNARK" ]; then
cargo build --release --package zokrates_cli --features="libsnark"
else
cargo build --release --package zokrates_cli
fi
cargo build --release --package zokrates_cli

View file

@ -4,11 +4,6 @@ MAINTAINER JacobEberhardt <jacob.eberhardt@tu-berlin.de>, Thibaut Schaeffer <thi
RUN useradd -u 1000 -m zokrates
ENV WITH_LIBSNARK=1
COPY ./scripts/install_libsnark_prerequisites.sh /tmp/
RUN /tmp/install_libsnark_prerequisites.sh
COPY ./scripts/install_solcjs_deb.sh /tmp/
RUN /tmp/install_solcjs_deb.sh

View file

@ -3,8 +3,4 @@
# Exit if any subcommand fails
set -e
if [ -n "$WITH_LIBSNARK" ]; then
cargo test -j 4 --release --package zokrates_cli --features="libsnark" -- --ignored
else
cargo test -j 4 --release --package zokrates_cli -- --ignored
fi
cargo test -j 4 --release --package zokrates_cli -- --ignored

View file

@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2022-03-10"
channel = "nightly-2022-07-01"

View file

@ -1,8 +0,0 @@
#!/bin/bash
# Usage: ./clang-format.sh zokrates_core/lib
dir=$1
for file in $dir/*.cpp $dir/*.hpp $dir/*.tcc; do
clang-format -i -style=WebKit -verbose $file
done

View file

@ -1,17 +0,0 @@
#!/bin/bash
# Exit if any subcommand fails
set -e
apt-get update
apt-get install -qq curl zlib1g-dev build-essential python
apt-get install -qq cmake g++ pkg-config jq
apt-get install -qq libcurl4-openssl-dev libelf-dev libdw-dev binutils-dev libiberty-dev
cargo install cargo-kcov
cargo kcov --print-install-kcov-sh | sh
cd zokrates_fs_resolver && WITH_LIBSNARK=1 LIBSNARK_SOURCE_PATH=$HOME/libsnark cargo kcov && cd ..
cd zokrates_core && WITH_LIBSNARK=1 LIBSNARK_SOURCE_PATH=$HOME/libsnark cargo kcov && cd ..
cd zokrates_cli && WITH_LIBSNARK=1 LIBSNARK_SOURCE_PATH=$HOME/libsnark cargo kcov && cd ..
cd zokrates_field && WITH_LIBSNARK=1 LIBSNARK_SOURCE_PATH=$HOME/libsnark cargo kcov && cd ..
bash <(curl -s https://codecov.io/bash)
echo "Uploaded code coverage"

View file

@ -2,6 +2,6 @@
apt-get update -y
apt-get install -y curl gnupg sudo build-essential git
curl -sL https://deb.nodesource.com/setup_12.x | bash -
curl -sL https://deb.nodesource.com/setup_16.x | bash -
apt-get install -y nodejs
npm i -g solc

View file

@ -3,10 +3,4 @@
# Exit if any subcommand fails
set -e
if [ -n "$WITH_LIBSNARK" ]; then
# run specifically the libsnark tests inside zokrates_core
cargo test -j 4 --release --package zokrates_core --features="libsnark" libsnark -- --test-threads=1
fi
# run all tests without libsnark on
cargo test -j 4 --release

View file

@ -4,9 +4,14 @@ version = "0.1.7"
authors = ["Thibaut Schaeffer <thibaut@schaeff.fr>"]
edition = "2018"
[features]
default = ["ark", "bellman"]
ark = ["zokrates_ast/ark"]
bellman = ["zokrates_ast/bellman"]
[dependencies]
zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false }
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
serde = "1.0"
serde_derive = "1.0"
serde_json = { version = "1.0", features = ["preserve_order"] }

View file

@ -15,11 +15,11 @@ impl<T: Field> Encode<T> for Inputs<T> {
}
use std::fmt;
use zokrates_core::typed_absy::types::{ConcreteType, UBitwidth};
use zokrates_ast::typed::types::{ConcreteType, UBitwidth};
use zokrates_field::Field;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Json(String),
Conversion(String),
@ -277,7 +277,7 @@ fn parse_value<T: Field>(
.map_err(|_| Error::Type(format!("Could not parse `{}` to u64 type", s))),
(ConcreteType::Boolean, serde_json::Value::Bool(b)) => Ok(Value::Boolean(b)),
(ConcreteType::Array(array_type), serde_json::Value::Array(a)) => {
let size = array_type.size;
let size = *array_type.size;
if a.len() != size as usize {
Err(Error::Type(format!(
"Expected array of size {}, found array of size {}",
@ -380,9 +380,7 @@ pub fn parse_strict_json<T: Field>(
#[cfg(test)]
mod tests {
use super::*;
use zokrates_core::typed_absy::types::{
ConcreteStructMember, ConcreteStructType, ConcreteType,
};
use zokrates_ast::typed::types::{ConcreteStructMember, ConcreteStructType, ConcreteType};
use zokrates_field::Bn128Field;
#[test]

34
zokrates_ark/Cargo.toml Normal file
View file

@ -0,0 +1,34 @@
[package]
name = "zokrates_ark"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
zokrates_proof_systems = { version = "0.1", path = "../zokrates_proof_systems", default-features = false }
ark-ff = { version = "^0.3.0", default-features = false }
ark-ec = { version = "^0.3.0", default-features = false }
ark-bn254 = { version = "^0.3.0", features = ["curve"], default-features = false }
ark-bls12-377 = { version = "^0.3.0", features = ["curve"], default-features = false }
ark-bw6-761 = { version = "^0.3.0", default-features = false }
ark-gm17 = { version = "^0.3.0", default-features = false }
ark-groth16 = { version = "^0.3.0", default-features = false }
ark-serialize = { version = "^0.3.0", default-features = false }
ark-relations = { version = "^0.3.0", default-features = false }
ark-marlin = { git = "https://github.com/arkworks-rs/marlin", rev = "63cfd82", default-features = false }
ark-poly = { version = "^0.3.0", default-features = false }
ark-poly-commit = { version = "^0.3.0", default-features = false }
ark-crypto-primitives = { version = "^0.3.0", default-features = false }
sha3 = { version = "0.9" }
digest = { version = "0.9" }
rand_0_8 = { version = "0.8", package = "rand" }
hex = "0.4.2"
[dev-dependencies]
zokrates_interpreter = { version = "0.1", path = "../zokrates_interpreter", features = ["ark"] }

163
zokrates_ark/src/gm17.rs Normal file
View file

@ -0,0 +1,163 @@
use ark_crypto_primitives::SNARK;
use ark_gm17::{
prepare_verifying_key, verify_proof, PreparedVerifyingKey, Proof as ArkProof, ProvingKey,
VerifyingKey, GM17 as ArkGM17,
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use zokrates_field::{ArkFieldExtensions, Field};
use crate::Computation;
use crate::{parse_fr, parse_g1, parse_g2};
use crate::{serialization, Ark};
use rand_0_8::{rngs::StdRng, SeedableRng};
use zokrates_ast::ir::{ProgIterator, Statement, Witness};
use zokrates_proof_systems::gm17::{ProofPoints, VerificationKey, GM17};
use zokrates_proof_systems::Scheme;
use zokrates_proof_systems::{Backend, NonUniversalBackend, Proof, SetupKeypair};
impl<T: Field + ArkFieldExtensions> NonUniversalBackend<T, GM17> for Ark {
fn setup<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
) -> SetupKeypair<T, GM17> {
let computation = Computation::without_witness(program);
let rng = &mut StdRng::from_entropy();
let (pk, vk) = ArkGM17::<T::ArkEngine>::circuit_specific_setup(computation, rng).unwrap();
let mut pk_vec: Vec<u8> = Vec::new();
pk.serialize_uncompressed(&mut pk_vec).unwrap();
let vk = VerificationKey {
h: parse_g2::<T>(&vk.h_g2),
g_alpha: parse_g1::<T>(&vk.g_alpha_g1),
h_beta: parse_g2::<T>(&vk.h_beta_g2),
g_gamma: parse_g1::<T>(&vk.g_gamma_g1),
h_gamma: parse_g2::<T>(&vk.h_gamma_g2),
query: vk.query.iter().map(|g1| parse_g1::<T>(g1)).collect(),
};
SetupKeypair::new(vk, pk_vec)
}
}
impl<T: Field + ArkFieldExtensions> Backend<T, GM17> for Ark {
fn generate_proof<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
) -> Proof<T, GM17> {
let computation = Computation::with_witness(program, witness);
let inputs = computation
.public_inputs_values()
.iter()
.map(parse_fr::<T>)
.collect::<Vec<_>>();
let pk = ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_uncompressed(
&mut proving_key.as_slice(),
)
.unwrap();
let rng = &mut StdRng::from_entropy();
let proof = ArkGM17::<T::ArkEngine>::prove(&pk, computation, rng).unwrap();
let proof_points = ProofPoints {
a: parse_g1::<T>(&proof.a),
b: parse_g2::<T>(&proof.b),
c: parse_g1::<T>(&proof.c),
};
Proof::new(proof_points, inputs)
}
fn verify(vk: <GM17 as Scheme<T>>::VerificationKey, proof: Proof<T, GM17>) -> bool {
let vk = VerifyingKey {
h_g2: serialization::to_g2::<T>(vk.h),
g_alpha_g1: serialization::to_g1::<T>(vk.g_alpha),
h_beta_g2: serialization::to_g2::<T>(vk.h_beta),
g_gamma_g1: serialization::to_g1::<T>(vk.g_gamma),
h_gamma_g2: serialization::to_g2::<T>(vk.h_gamma),
query: vk
.query
.into_iter()
.map(serialization::to_g1::<T>)
.collect(),
};
let ark_proof = ArkProof {
a: serialization::to_g1::<T>(proof.proof.a),
b: serialization::to_g2::<T>(proof.proof.b),
c: serialization::to_g1::<T>(proof.proof.c),
};
let pvk: PreparedVerifyingKey<<T as ArkFieldExtensions>::ArkEngine> =
prepare_verifying_key(&vk);
let public_inputs: Vec<_> = proof
.inputs
.iter()
.map(|s| {
T::try_from_str(s.trim_start_matches("0x"), 16)
.unwrap()
.into_ark()
})
.collect::<Vec<_>>();
verify_proof(&pvk, &ark_proof, &public_inputs).unwrap()
}
}
#[cfg(test)]
mod tests {
use zokrates_ast::flat::{Parameter, Variable};
use zokrates_ast::ir::{Prog, Statement};
use zokrates_interpreter::Interpreter;
use super::*;
use zokrates_field::{Bls12_377Field, Bw6_761Field};
#[test]
fn verify_bls12_377_field() {
let program: Prog<Bls12_377Field> = Prog {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let keypair = <Ark as NonUniversalBackend<Bls12_377Field, GM17>>::setup(program.clone());
let interpreter = Interpreter::default();
let witness = interpreter
.execute(program.clone(), &[Bls12_377Field::from(42)])
.unwrap();
let proof =
<Ark as Backend<Bls12_377Field, GM17>>::generate_proof(program, witness, keypair.pk);
let ans = <Ark as Backend<Bls12_377Field, GM17>>::verify(keypair.vk, proof);
assert!(ans);
}
#[test]
fn verify_bw6_761_field() {
let program: Prog<Bw6_761Field> = Prog {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let keypair = <Ark as NonUniversalBackend<Bw6_761Field, GM17>>::setup(program.clone());
let interpreter = Interpreter::default();
let witness = interpreter
.execute(program.clone(), &[Bw6_761Field::from(42)])
.unwrap();
let proof =
<Ark as Backend<Bw6_761Field, GM17>>::generate_proof(program, witness, keypair.pk);
let ans = <Ark as Backend<Bw6_761Field, GM17>>::verify(keypair.vk, proof);
assert!(ans);
}
}

View file

@ -1,25 +1,24 @@
use crate::proof_system::{Backend, NonUniversalBackend, NotBw6_761Field, Proof, SetupKeypair};
use ark_crypto_primitives::SNARK;
use ark_groth16::{
prepare_verifying_key, verify_proof, Groth16, PreparedVerifyingKey, Proof as ArkProof,
ProvingKey, VerifyingKey,
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use zokrates_field::ArkFieldExtensions;
use zokrates_field::Field;
use zokrates_field::{ArkFieldExtensions, Bw6_761Field};
use zokrates_proof_systems::{Backend, NonUniversalBackend, Proof, SetupKeypair};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::ark::Computation;
use crate::proof_system::ark::{parse_fr, serialization, Ark};
use crate::proof_system::ark::{parse_g1, parse_g2};
use crate::proof_system::groth16::{ProofPoints, VerificationKey, G16};
use crate::proof_system::Scheme;
use ark_bw6_761::BW6_761;
use crate::Computation;
use crate::{parse_fr, serialization, Ark};
use crate::{parse_g1, parse_g2};
use rand_0_8::{rngs::StdRng, SeedableRng};
use zokrates_ast::ir::{ProgIterator, Statement, Witness};
use zokrates_proof_systems::groth16::{ProofPoints, VerificationKey, G16};
use zokrates_proof_systems::Scheme;
const G16_WARNING: &str = "WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/toolbox/proving_schemes.html#g16-malleability for implications.";
impl<T: Field + ArkFieldExtensions + NotBw6_761Field> Backend<T, G16> for Ark {
impl<T: Field + ArkFieldExtensions> Backend<T, G16> for Ark {
fn generate_proof<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
witness: Witness<T>,
@ -86,7 +85,7 @@ impl<T: Field + ArkFieldExtensions + NotBw6_761Field> Backend<T, G16> for Ark {
}
}
impl<T: Field + ArkFieldExtensions + NotBw6_761Field> NonUniversalBackend<T, G16> for Ark {
impl<T: Field + ArkFieldExtensions> NonUniversalBackend<T, G16> for Ark {
fn setup<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
) -> SetupKeypair<T, G16> {
@ -112,108 +111,11 @@ impl<T: Field + ArkFieldExtensions + NotBw6_761Field> NonUniversalBackend<T, G16
}
}
impl Backend<Bw6_761Field, G16> for Ark {
fn generate_proof<I: IntoIterator<Item = Statement<Bw6_761Field>>>(
program: ProgIterator<Bw6_761Field, I>,
witness: Witness<Bw6_761Field>,
proving_key: Vec<u8>,
) -> Proof<Bw6_761Field, G16> {
println!("{}", G16_WARNING);
let computation = Computation::with_witness(program, witness);
let inputs = computation
.public_inputs_values()
.iter()
.map(parse_fr::<Bw6_761Field>)
.collect::<Vec<_>>();
let pk =
ProvingKey::<BW6_761>::deserialize_uncompressed(&mut proving_key.as_slice()).unwrap();
let rng = &mut StdRng::from_entropy();
let proof = Groth16::<BW6_761>::prove(&pk, computation, rng).unwrap();
let proof_points = ProofPoints {
a: parse_g1::<Bw6_761Field>(&proof.a),
b: parse_g2::<Bw6_761Field>(&proof.b),
c: parse_g1::<Bw6_761Field>(&proof.c),
};
Proof::new(proof_points, inputs)
}
fn verify(
vk: <G16 as Scheme<Bw6_761Field>>::VerificationKey,
proof: Proof<Bw6_761Field, G16>,
) -> bool {
let vk = VerifyingKey {
alpha_g1: serialization::to_g1::<Bw6_761Field>(vk.alpha),
beta_g2: serialization::to_g2::<Bw6_761Field>(vk.beta),
gamma_g2: serialization::to_g2::<Bw6_761Field>(vk.gamma),
delta_g2: serialization::to_g2::<Bw6_761Field>(vk.delta),
gamma_abc_g1: vk
.gamma_abc
.into_iter()
.map(serialization::to_g1::<Bw6_761Field>)
.collect(),
};
let pvk: PreparedVerifyingKey<BW6_761> = prepare_verifying_key(&vk);
let ark_proof = ArkProof {
a: serialization::to_g1::<Bw6_761Field>(proof.proof.a),
b: serialization::to_g2::<Bw6_761Field>(proof.proof.b),
c: serialization::to_g1::<Bw6_761Field>(proof.proof.c),
};
let public_inputs: Vec<_> = proof
.inputs
.iter()
.map(|s| {
Bw6_761Field::try_from_str(s.trim_start_matches("0x"), 16)
.unwrap()
.into_ark()
})
.collect::<Vec<_>>();
verify_proof(&pvk, &ark_proof, &public_inputs).unwrap()
}
}
impl NonUniversalBackend<Bw6_761Field, G16> for Ark {
fn setup<I: IntoIterator<Item = Statement<Bw6_761Field>>>(
program: ProgIterator<Bw6_761Field, I>,
) -> SetupKeypair<Bw6_761Field, G16> {
println!("{}", G16_WARNING);
let computation = Computation::without_witness(program);
let rng = &mut StdRng::from_entropy();
let (pk, vk) = Groth16::<BW6_761>::circuit_specific_setup(computation, rng).unwrap();
let mut pk_vec: Vec<u8> = Vec::new();
pk.serialize_uncompressed(&mut pk_vec).unwrap();
let vk = VerificationKey {
alpha: parse_g1::<Bw6_761Field>(&vk.alpha_g1),
beta: parse_g2::<Bw6_761Field>(&vk.beta_g2),
gamma: parse_g2::<Bw6_761Field>(&vk.gamma_g2),
delta: parse_g2::<Bw6_761Field>(&vk.delta_g2),
gamma_abc: vk
.gamma_abc_g1
.iter()
.map(parse_g1::<Bw6_761Field>)
.collect(),
};
SetupKeypair::new(vk, pk_vec)
}
}
#[cfg(test)]
mod tests {
use crate::flat_absy::{FlatParameter, FlatVariable};
use crate::ir::{Interpreter, Prog, Statement};
use zokrates_ast::flat::{Parameter, Variable};
use zokrates_ast::ir::{Prog, Statement};
use zokrates_interpreter::Interpreter;
use super::*;
use zokrates_field::{Bls12_377Field, Bw6_761Field};
@ -221,12 +123,9 @@ mod tests {
#[test]
fn verify_bls12_377_field() {
let program: Prog<Bls12_377Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
)],
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let keypair = <Ark as NonUniversalBackend<Bls12_377Field, G16>>::setup(program.clone());
@ -246,12 +145,9 @@ mod tests {
#[test]
fn verify_bw6_761_field() {
let program: Prog<Bw6_761Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
)],
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let keypair = <Ark as NonUniversalBackend<Bw6_761Field, G16>>::setup(program.clone());

View file

@ -2,14 +2,14 @@ pub mod gm17;
pub mod groth16;
pub mod marlin;
use crate::flat_absy::FlatVariable;
use crate::ir::{CanonicalLinComb, ProgIterator, Statement, Witness};
use ark_ec::PairingEngine;
use ark_relations::r1cs::{
ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, LinearCombination,
SynthesisError, Variable,
SynthesisError, Variable as ArkVariable,
};
use std::collections::BTreeMap;
use zokrates_ast::common::Variable;
use zokrates_ast::ir::{CanonicalLinComb, ProgIterator, Statement, Witness};
use zokrates_field::{ArkFieldExtensions, Field};
pub use self::parse::*;
@ -41,7 +41,7 @@ impl<T, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
fn ark_combination<T: Field + ArkFieldExtensions>(
l: CanonicalLinComb<T>,
cs: &mut ConstraintSystem<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
symbols: &mut BTreeMap<FlatVariable, Variable>,
symbols: &mut BTreeMap<Variable, ArkVariable>,
witness: &mut Witness<T>,
) -> LinearCombination<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr> {
l.0.into_iter()
@ -72,23 +72,25 @@ fn ark_combination<T: Field + ArkFieldExtensions>(
.fold(LinearCombination::zero(), |acc, e| acc + e)
}
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn generate_constraints(
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>>
ConstraintSynthesizer<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>
for Computation<T, I>
{
fn generate_constraints(
self,
cs: ConstraintSystemRef<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
witness: Option<Witness<T>>,
) -> Result<(), SynthesisError> {
// mapping from IR variables
let mut symbols = BTreeMap::new();
let mut witness = witness.unwrap_or_else(Witness::empty);
let mut witness = self.witness.unwrap_or_else(Witness::empty);
assert!(symbols.insert(FlatVariable::one(), ConstraintSystem::<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>::one()).is_none());
assert!(symbols.insert(Variable::one(), ConstraintSystem::<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>::one()).is_none());
match cs {
ConstraintSystemRef::CS(rc) => {
let mut cs = rc.borrow_mut();
symbols.extend(self.arguments.iter().enumerate().map(|(_, p)| {
symbols.extend(self.program.arguments.iter().enumerate().map(|(_, p)| {
let wire = match p.private {
true => cs.new_witness_variable(|| {
Ok(witness
@ -109,7 +111,7 @@ impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> ProgIt
(p.id, wire)
}));
for statement in self.statements {
for statement in self.program.statements {
if let Statement::Constraint(quad, lin, _) = statement {
let a = ark_combination(
quad.left.clone().into_canonical(),
@ -144,29 +146,18 @@ impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> ProgIt
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
pub fn public_inputs_values(&self) -> Vec<<T::ArkEngine as PairingEngine>::Fr> {
self.program
.public_inputs(self.witness.as_ref().unwrap())
.public_inputs_values(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_ark())
.collect()
}
}
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>>
ConstraintSynthesizer<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>
for Computation<T, I>
{
fn generate_constraints(
self,
cs: ConstraintSystemRef<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
) -> Result<(), SynthesisError> {
self.program.generate_constraints(cs, self.witness)
}
}
mod parse {
use super::*;
use crate::proof_system::{Fr, G1Affine, G2Affine, G2AffineFq};
use ark_ff::ToBytes;
use zokrates_field::G2Type;
use zokrates_proof_systems::{Fr, G1Affine, G2Affine, G2AffineFq, G2AffineFq2};
pub fn parse_g1<T: Field + ArkFieldExtensions>(
e: &<T::ArkEngine as PairingEngine>::G1Affine,
@ -196,48 +187,46 @@ mod parse {
e.write(&mut bytes).unwrap();
let length = bytes.len() - 1; // [x, y, infinity] - infinity
let element_length = length / 4;
let mut elements = vec![];
for i in 0..4 {
let start = i * element_length;
let end = start + element_length;
let mut e = bytes[start..end].to_vec();
e.reverse();
elements.push(e);
match T::G2_TYPE {
G2Type::Fq2 => {
let element_length = length / 4;
let mut elements = vec![];
for i in 0..4 {
let start = i * element_length;
let end = start + element_length;
let mut e = bytes[start..end].to_vec();
e.reverse();
elements.push(e);
}
G2Affine::Fq2(G2AffineFq2(
(
format!("0x{}", hex::encode(&elements[0])),
format!("0x{}", hex::encode(&elements[1])),
),
(
format!("0x{}", hex::encode(&elements[2])),
format!("0x{}", hex::encode(&elements[3])),
),
))
}
G2Type::Fq => {
let element_length = length / 2;
let mut x = bytes[0..element_length].to_vec();
let mut y = bytes[element_length..length].to_vec();
x.reverse();
y.reverse();
G2Affine::Fq(G2AffineFq(
format!("0x{}", hex::encode(&x)),
format!("0x{}", hex::encode(&y)),
))
}
}
G2Affine(
(
format!("0x{}", hex::encode(&elements[0])),
format!("0x{}", hex::encode(&elements[1])),
),
(
format!("0x{}", hex::encode(&elements[2])),
format!("0x{}", hex::encode(&elements[3])),
),
)
}
pub fn parse_g2_fq<T: ArkFieldExtensions>(
e: &<T::ArkEngine as PairingEngine>::G2Affine,
) -> G2AffineFq {
let mut bytes: Vec<u8> = Vec::new();
e.write(&mut bytes).unwrap();
let length = bytes.len() - 1; // [x, y, infinity] - infinity
let element_length = length / 2;
let mut x = bytes[0..element_length].to_vec();
let mut y = bytes[element_length..length].to_vec();
x.reverse();
y.reverse();
G2AffineFq(
format!("0x{}", hex::encode(&x)),
format!("0x{}", hex::encode(&y)),
)
}
pub fn parse_fr<T: ArkFieldExtensions>(e: &<T::ArkEngine as PairingEngine>::Fr) -> Fr {
@ -250,10 +239,10 @@ mod parse {
}
pub mod serialization {
use crate::proof_system::{G1Affine, G2Affine, G2AffineFq};
use ark_ec::PairingEngine;
use ark_ff::FromBytes;
use zokrates_field::ArkFieldExtensions;
use zokrates_proof_systems::{G1Affine, G2Affine};
#[inline]
fn decode_hex(value: String) -> Vec<u8> {
@ -273,22 +262,21 @@ pub mod serialization {
pub fn to_g2<T: ArkFieldExtensions>(g2: G2Affine) -> <T::ArkEngine as PairingEngine>::G2Affine {
let mut bytes = vec![];
bytes.append(&mut decode_hex((g2.0).0));
bytes.append(&mut decode_hex((g2.0).1));
bytes.append(&mut decode_hex((g2.1).0));
bytes.append(&mut decode_hex((g2.1).1));
bytes.push(0u8); // infinity flag
<T::ArkEngine as PairingEngine>::G2Affine::read(&*bytes).unwrap()
}
pub fn to_g2_fq<T: ArkFieldExtensions>(
g2: G2AffineFq,
) -> <T::ArkEngine as PairingEngine>::G2Affine {
let mut bytes = vec![];
bytes.append(&mut decode_hex(g2.0));
bytes.append(&mut decode_hex(g2.1));
bytes.push(0u8); // infinity flag
match g2 {
G2Affine::Fq(g2) => {
bytes.append(&mut decode_hex(g2.0));
bytes.append(&mut decode_hex(g2.1));
bytes.push(0u8); // infinity flag
}
G2Affine::Fq2(g2) => {
bytes.append(&mut decode_hex((g2.0).0));
bytes.append(&mut decode_hex((g2.0).1));
bytes.append(&mut decode_hex((g2.1).0));
bytes.append(&mut decode_hex((g2.1).1));
bytes.push(0u8); // infinity flag
}
};
<T::ArkEngine as PairingEngine>::G2Affine::read(&*bytes).unwrap()
}

View file

@ -23,13 +23,13 @@ use std::marker::PhantomData;
use zokrates_field::{ArkFieldExtensions, Field};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::ark::Ark;
use crate::proof_system::ark::Computation;
use crate::proof_system::ark::{parse_fr, parse_g1, parse_g2, serialization};
use crate::proof_system::marlin::{self, KZGVerifierKey, ProofPoints, VerificationKey};
use crate::proof_system::Scheme;
use crate::proof_system::{Backend, Proof, SetupKeypair, UniversalBackend};
use crate::Ark;
use crate::Computation;
use crate::{parse_fr, parse_g1, parse_g2, serialization};
use zokrates_ast::ir::{ProgIterator, Statement, Witness};
use zokrates_proof_systems::marlin::{self, KZGVerifierKey, ProofPoints, VerificationKey};
use zokrates_proof_systems::Scheme;
use zokrates_proof_systems::{Backend, Proof, SetupKeypair, UniversalBackend};
const MINIMUM_CONSTRAINT_COUNT: usize = 2;
@ -386,27 +386,28 @@ impl<T: Field + ArkFieldExtensions> Backend<T, marlin::Marlin> for Ark {
#[cfg(test)]
mod tests {
use crate::flat_absy::{FlatParameter, FlatVariable};
use crate::ir::{Interpreter, Prog, QuadComb, Statement};
use zokrates_ast::flat::{Parameter, Variable};
use zokrates_ast::ir::{Prog, QuadComb, Statement};
use zokrates_interpreter::Interpreter;
use super::*;
use crate::proof_system::scheme::Marlin;
use zokrates_field::{Bls12_377Field, Bw6_761Field};
use zokrates_proof_systems::Marlin;
#[test]
fn verify_bls12_377_field() {
let program: Prog<Bls12_377Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
arguments: vec![Parameter::private(Variable::new(0))],
return_count: 1,
statements: vec![
Statement::constraint(
QuadComb::from_linear_combinations(
FlatVariable::new(0).into(),
FlatVariable::new(0).into(),
Variable::new(0).into(),
Variable::new(0).into(),
),
FlatVariable::new(1),
Variable::new(1),
),
Statement::constraint(FlatVariable::new(1), FlatVariable::public(0)),
Statement::constraint(Variable::new(1), Variable::public(0)),
],
};
@ -429,17 +430,17 @@ mod tests {
#[test]
fn verify_bw6_761_field() {
let program: Prog<Bw6_761Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
arguments: vec![Parameter::private(Variable::new(0))],
return_count: 1,
statements: vec![
Statement::constraint(
QuadComb::from_linear_combinations(
FlatVariable::new(0).into(),
FlatVariable::new(0).into(),
Variable::new(0).into(),
Variable::new(0).into(),
),
FlatVariable::new(1),
Variable::new(1),
),
Statement::constraint(FlatVariable::new(1), FlatVariable::public(0)),
Statement::constraint(Variable::new(1), Variable::public(0)),
],
};

26
zokrates_ast/Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "zokrates_ast"
version = "0.1.0"
edition = "2021"
[features]
default = ["bellman", "ark"]
bellman = ["zokrates_field/bellman", "pairing_ce", "zokrates_embed/bellman"]
ark = ["ark-bls12-377", "zokrates_embed/ark"]
[dependencies]
zokrates_pest_ast = { version = "0.3.0", path = "../zokrates_pest_ast" }
cfg-if = "0.1"
zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false }
serde = { version = "1.0", features = ["derive"] }
csv = "1"
serde_cbor = "0.11.2"
num-bigint = { version = "0.2", default-features = false }
serde_json = { version = "1.0", features = ["preserve_order"] }
zokrates_embed = { version = "0.1.0", path = "../zokrates_embed", default-features = false }
pairing_ce = { version = "^0.21", optional = true }
ark-bls12-377 = { version = "^0.3.0", features = ["curve"], default-features = false, optional = true }

View file

@ -1,16 +1,14 @@
use crate::absy::{
types::{UnresolvedSignature, UnresolvedType},
ConstantGenericNode, Expression,
};
use crate::flat_absy::{
FlatDirective, FlatExpression, FlatFunctionIterator, FlatParameter, FlatStatement,
FlatVariable, RuntimeError,
};
use crate::solvers::Solver;
use crate::typed_absy::types::{
use crate::common::{Parameter, RuntimeError, Solver, Variable};
use crate::flat::{flat_expression_from_bits, flat_expression_from_variable_summands};
use crate::flat::{FlatDirective, FlatExpression, FlatFunctionIterator, FlatStatement};
use crate::typed::types::{
ConcreteGenericsAssignment, DeclarationConstant, DeclarationSignature, DeclarationType,
GenericIdentifier,
};
use crate::untyped::{
types::{UnresolvedSignature, UnresolvedType},
ConstantGenericNode, Expression,
};
use std::collections::HashMap;
use zokrates_field::Field;
@ -65,66 +63,76 @@ impl FlatEmbed {
)
.into(),
])
.outputs(vec![UnresolvedType::Boolean.into()]),
.output(UnresolvedType::Boolean.into()),
FlatEmbed::Unpack => UnresolvedSignature::new()
.generics(vec!["N".into()])
.inputs(vec![UnresolvedType::FieldElement.into()])
.outputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::Identifier("N").into(),
)
.into()]),
.output(
UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::Identifier("N").into(),
)
.into(),
),
FlatEmbed::U8ToBits => UnresolvedSignature::new()
.inputs(vec![UnresolvedType::Uint(8).into()])
.outputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(8).into(),
)
.into()]),
.output(
UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(8).into(),
)
.into(),
),
FlatEmbed::U16ToBits => UnresolvedSignature::new()
.inputs(vec![UnresolvedType::Uint(16).into()])
.outputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(16).into(),
)
.into()]),
.output(
UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(16).into(),
)
.into(),
),
FlatEmbed::U32ToBits => UnresolvedSignature::new()
.inputs(vec![UnresolvedType::Uint(32).into()])
.outputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(32).into(),
)
.into()]),
.output(
UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(32).into(),
)
.into(),
),
FlatEmbed::U64ToBits => UnresolvedSignature::new()
.inputs(vec![UnresolvedType::Uint(64).into()])
.outputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(64).into(),
)
.into()]),
.output(
UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(64).into(),
)
.into(),
),
FlatEmbed::U8FromBits => UnresolvedSignature::new()
.outputs(vec![UnresolvedType::Uint(8).into()])
.output(UnresolvedType::Uint(8).into())
.inputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(8).into(),
)
.into()]),
FlatEmbed::U16FromBits => UnresolvedSignature::new()
.outputs(vec![UnresolvedType::Uint(16).into()])
.output(UnresolvedType::Uint(16).into())
.inputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(16).into(),
)
.into()]),
FlatEmbed::U32FromBits => UnresolvedSignature::new()
.outputs(vec![UnresolvedType::Uint(32).into()])
.output(UnresolvedType::Uint(32).into())
.inputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(32).into(),
)
.into()]),
FlatEmbed::U64FromBits => UnresolvedSignature::new()
.outputs(vec![UnresolvedType::Uint(64).into()])
.output(UnresolvedType::Uint(64).into())
.inputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(64).into(),
@ -144,11 +152,13 @@ impl FlatEmbed {
)
.into(),
])
.outputs(vec![UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(256).into(),
)
.into()]),
.output(
UnresolvedType::array(
UnresolvedType::Boolean.into(),
Expression::U32Constant(256).into(),
)
.into(),
),
#[cfg(feature = "ark")]
FlatEmbed::SnarkVerifyBls12377 => UnresolvedSignature::new()
.generics(vec!["N".into(), "V".into()])
@ -169,7 +179,7 @@ impl FlatEmbed {
)
.into(), // 18 + (2 * n) // vk
])
.outputs(vec![UnresolvedType::Boolean.into()]),
.output(UnresolvedType::Boolean.into()),
}
}
@ -189,60 +199,48 @@ impl FlatEmbed {
GenericIdentifier::with_name("N").with_index(0),
)),
])
.outputs(vec![DeclarationType::Boolean]),
.output(DeclarationType::Boolean),
FlatEmbed::Unpack => DeclarationSignature::new()
.generics(vec![Some(DeclarationConstant::Generic(
GenericIdentifier::with_name("N").with_index(0),
))])
.inputs(vec![DeclarationType::FieldElement])
.outputs(vec![DeclarationType::array((
.output(DeclarationType::array((
DeclarationType::Boolean,
GenericIdentifier::with_name("N").with_index(0),
))]),
))),
FlatEmbed::U8ToBits => DeclarationSignature::new()
.inputs(vec![DeclarationType::uint(8)])
.outputs(vec![DeclarationType::array((
DeclarationType::Boolean,
8u32,
))]),
.output(DeclarationType::array((DeclarationType::Boolean, 8u32))),
FlatEmbed::U16ToBits => DeclarationSignature::new()
.inputs(vec![DeclarationType::uint(16)])
.outputs(vec![DeclarationType::array((
DeclarationType::Boolean,
16u32,
))]),
.output(DeclarationType::array((DeclarationType::Boolean, 16u32))),
FlatEmbed::U32ToBits => DeclarationSignature::new()
.inputs(vec![DeclarationType::uint(32)])
.outputs(vec![DeclarationType::array((
DeclarationType::Boolean,
32u32,
))]),
.output(DeclarationType::array((DeclarationType::Boolean, 32u32))),
FlatEmbed::U64ToBits => DeclarationSignature::new()
.inputs(vec![DeclarationType::uint(64)])
.outputs(vec![DeclarationType::array((
DeclarationType::Boolean,
64u32,
))]),
.output(DeclarationType::array((DeclarationType::Boolean, 64u32))),
FlatEmbed::U8FromBits => DeclarationSignature::new()
.outputs(vec![DeclarationType::uint(8)])
.output(DeclarationType::uint(8))
.inputs(vec![DeclarationType::array((
DeclarationType::Boolean,
8u32,
))]),
FlatEmbed::U16FromBits => DeclarationSignature::new()
.outputs(vec![DeclarationType::uint(16)])
.output(DeclarationType::uint(16))
.inputs(vec![DeclarationType::array((
DeclarationType::Boolean,
16u32,
))]),
FlatEmbed::U32FromBits => DeclarationSignature::new()
.outputs(vec![DeclarationType::uint(32)])
.output(DeclarationType::uint(32))
.inputs(vec![DeclarationType::array((
DeclarationType::Boolean,
32u32,
))]),
FlatEmbed::U64FromBits => DeclarationSignature::new()
.outputs(vec![DeclarationType::uint(64)])
.output(DeclarationType::uint(64))
.inputs(vec![DeclarationType::array((
DeclarationType::Boolean,
64u32,
@ -253,10 +251,7 @@ impl FlatEmbed {
DeclarationType::array((DeclarationType::Boolean, 512u32)),
DeclarationType::array((DeclarationType::Boolean, 256u32)),
])
.outputs(vec![DeclarationType::array((
DeclarationType::Boolean,
256u32,
))]),
.output(DeclarationType::array((DeclarationType::Boolean, 256u32))),
#[cfg(feature = "ark")]
FlatEmbed::SnarkVerifyBls12377 => DeclarationSignature::new()
.generics(vec![
@ -278,7 +273,7 @@ impl FlatEmbed {
GenericIdentifier::with_name("V").with_index(1),
)), // 18 + (2 * n) // vk
])
.outputs(vec![DeclarationType::Boolean]),
.output(DeclarationType::Boolean),
}
}
@ -314,29 +309,6 @@ impl FlatEmbed {
}
}
// util to convert a vector of `(variable_id, coefficient)` to a flat_expression
// we build a binary tree of additions by splitting the vector recursively
#[cfg(any(feature = "ark", feature = "bellman"))]
fn flat_expression_from_vec<T: Field>(v: &[(usize, T)]) -> FlatExpression<T> {
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (key, val) = v[0].clone();
FlatExpression::Mult(
box FlatExpression::Number(val),
box FlatExpression::Identifier(FlatVariable::new(key)),
)
}
n => {
let (u, v) = v.split_at(n / 2);
FlatExpression::Add(
box flat_expression_from_vec::<T>(u),
box flat_expression_from_vec::<T>(v),
)
}
}
}
/// Returns a flat function which computes a sha256 round
///
/// # Remarks
@ -383,14 +355,14 @@ pub fn sha256_round<T: Field>(
.clone()
.into_iter()
.chain(current_hash_argument_indices.clone())
.map(|i| FlatParameter {
id: FlatVariable::new(i),
.map(|i| Parameter {
id: Variable::new(i),
private: true,
})
.collect();
// define a binding of the first variable in the constraint system to one
let one_binding_statement = FlatStatement::Condition(
FlatVariable::new(0).into(),
Variable::new(0).into(),
FlatExpression::Number(T::from(1)),
RuntimeError::BellmanOneBinding,
);
@ -398,17 +370,17 @@ pub fn sha256_round<T: Field>(
// bind input and current_hash to inputs
input_indices.chain(current_hash_indices).zip(input_argument_indices.clone().into_iter().chain(current_hash_argument_indices.clone())).map(|(cs_index, argument_index)| {
FlatStatement::Condition(
FlatVariable::new(cs_index).into(),
FlatVariable::new(argument_index).into(),
Variable::new(cs_index).into(),
Variable::new(argument_index).into(),
RuntimeError::BellmanInputBinding
)
});
// insert flattened statements to represent constraints
let constraint_statements = r1cs.constraints.into_iter().map(|c| {
let c = from_bellman::<T, Bn256>(c);
let rhs_a = flat_expression_from_vec::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_vec::<T>(c.b.as_slice());
let lhs = flat_expression_from_vec::<T>(c.c.as_slice());
let rhs_a = flat_expression_from_variable_summands::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_variable_summands::<T>(c.b.as_slice());
let lhs = flat_expression_from_variable_summands::<T>(c.c.as_slice());
FlatStatement::Condition(
lhs,
@ -418,14 +390,14 @@ pub fn sha256_round<T: Field>(
});
// define which subset of the witness is returned
let outputs = output_indices.map(|o| FlatExpression::Identifier(FlatVariable::new(o)));
let outputs = output_indices.map(|o| FlatExpression::Identifier(Variable::new(o)));
// insert a directive to set the witness based on the bellman gadget and inputs
let directive_statement = FlatStatement::Directive(FlatDirective {
outputs: cs_indices.map(FlatVariable::new).collect(),
outputs: cs_indices.map(Variable::new).collect(),
inputs: input_argument_indices
.into_iter()
.chain(current_hash_argument_indices)
.map(|i| FlatVariable::new(i).into())
.map(|i| Variable::new(i).into())
.collect(),
solver: Solver::Sha256Round,
});
@ -433,7 +405,7 @@ pub fn sha256_round<T: Field>(
let return_statements = outputs
.into_iter()
.enumerate()
.map(|(index, e)| FlatStatement::Definition(FlatVariable::public(index), e));
.map(|(index, e)| FlatStatement::Definition(Variable::public(index), e));
let statements = std::iter::once(directive_statement)
.chain(std::iter::once(one_binding_statement))
.chain(input_binding_statements)
@ -471,15 +443,15 @@ pub fn snark_verify_bls12_377<T: Field>(
let input_arguments = input_argument_indices
.clone()
.map(|i| FlatParameter::private(FlatVariable::new(i)));
.map(|i| Parameter::private(Variable::new(i)));
let proof_arguments = proof_argument_indices
.clone()
.map(|i| FlatParameter::private(FlatVariable::new(i)));
.map(|i| Parameter::private(Variable::new(i)));
let vk_arguments = vk_argument_indices
.clone()
.map(|i| FlatParameter::private(FlatVariable::new(i)));
.map(|i| Parameter::private(Variable::new(i)));
let arguments = input_arguments
.chain(proof_arguments)
@ -487,7 +459,7 @@ pub fn snark_verify_bls12_377<T: Field>(
.collect();
let one_binding_statement = FlatStatement::Condition(
FlatExpression::Identifier(FlatVariable::new(0)),
FlatExpression::Identifier(Variable::new(0)),
FlatExpression::Number(T::from(1)),
RuntimeError::ArkOneBinding,
);
@ -503,8 +475,8 @@ pub fn snark_verify_bls12_377<T: Field>(
)
.map(|(cs_index, argument_index)| {
FlatStatement::Condition(
FlatVariable::new(cs_index).into(),
FlatVariable::new(argument_index).into(),
Variable::new(cs_index).into(),
Variable::new(argument_index).into(),
RuntimeError::ArkInputBinding,
)
})
@ -514,9 +486,9 @@ pub fn snark_verify_bls12_377<T: Field>(
.into_iter()
.map(|c| {
let c = from_ark::<T, Bls12_377>(c);
let rhs_a = flat_expression_from_vec::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_vec::<T>(c.b.as_slice());
let lhs = flat_expression_from_vec::<T>(c.c.as_slice());
let rhs_a = flat_expression_from_variable_summands::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_variable_summands::<T>(c.b.as_slice());
let lhs = flat_expression_from_variable_summands::<T>(c.c.as_slice());
FlatStatement::Condition(
lhs,
@ -527,17 +499,17 @@ pub fn snark_verify_bls12_377<T: Field>(
.collect();
let return_statement = FlatStatement::Definition(
FlatVariable::public(0),
FlatExpression::Identifier(FlatVariable::new(out_index)),
Variable::public(0),
FlatExpression::Identifier(Variable::new(out_index)),
);
// insert a directive to set the witness
let directive_statement = FlatStatement::Directive(FlatDirective {
outputs: cs_indices.map(FlatVariable::new).collect(),
outputs: cs_indices.map(Variable::new).collect(),
inputs: input_argument_indices
.chain(proof_argument_indices)
.chain(vk_argument_indices)
.map(|i| FlatVariable::new(i).into())
.map(|i| Variable::new(i).into())
.collect(),
solver: Solver::SnarkVerifyBls12377(n),
});
@ -556,11 +528,11 @@ pub fn snark_verify_bls12_377<T: Field>(
}
fn use_variable(
layout: &mut HashMap<String, FlatVariable>,
layout: &mut HashMap<String, Variable>,
name: String,
index: &mut usize,
) -> FlatVariable {
let var = FlatVariable::new(*index);
) -> Variable {
let var = Variable::new(*index);
layout.insert(name, var);
*index += 1;
var
@ -581,8 +553,8 @@ pub fn unpack_to_bitwidth<T: Field>(
let mut layout = HashMap::new();
let arguments = vec![FlatParameter {
id: FlatVariable::new(0),
let arguments = vec![Parameter {
id: Variable::new(0),
private: true,
}];
@ -594,7 +566,7 @@ pub fn unpack_to_bitwidth<T: Field>(
&mut counter,
))];
let directive_outputs: Vec<FlatVariable> = (0..bit_width)
let directive_outputs: Vec<Variable> = (0..bit_width)
.map(|index| use_variable(&mut layout, format!("o{}", index), &mut counter))
.collect();
@ -610,7 +582,7 @@ pub fn unpack_to_bitwidth<T: Field>(
// o253, o252, ... o{253 - (bit_width - 1)} are bits
let mut statements: Vec<FlatStatement<T>> = (0..bit_width)
.map(|index| {
let bit = FlatExpression::Identifier(FlatVariable::new(bit_width - index));
let bit = FlatExpression::Identifier(Variable::new(bit_width - index));
FlatStatement::Condition(
bit.clone(),
FlatExpression::Mult(box bit.clone(), box bit.clone()),
@ -620,22 +592,16 @@ pub fn unpack_to_bitwidth<T: Field>(
.collect();
// sum check: o253 + o252 * 2 + ... + o{253 - (bit_width - 1)} * 2**(bit_width - 1)
let mut lhs_sum = FlatExpression::Number(T::from(0));
for i in 0..bit_width {
lhs_sum = FlatExpression::Add(
box lhs_sum,
box FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(bit_width - i)),
box FlatExpression::Number(T::from(2).pow(i)),
),
);
}
let lhs_sum = flat_expression_from_bits(
(0..bit_width)
.map(|i| FlatExpression::Identifier(Variable::new(i + 1)))
.collect(),
);
statements.push(FlatStatement::Condition(
lhs_sum,
FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(0)),
box FlatExpression::Identifier(Variable::new(0)),
box FlatExpression::Number(T::from(1)),
),
RuntimeError::Sum,
@ -654,7 +620,7 @@ pub fn unpack_to_bitwidth<T: Field>(
outputs
.into_iter()
.enumerate()
.map(|(index, e)| FlatStatement::Definition(FlatVariable::public(index), e)),
.map(|(index, e)| FlatStatement::Definition(Variable::public(index), e)),
);
FlatFunctionIterator {
@ -678,18 +644,15 @@ mod tests {
let unpack =
unpack_to_bitwidth::<Bn128Field>(Bn128Field::get_required_bits()).collect();
assert_eq!(
unpack.arguments,
vec![FlatParameter::private(FlatVariable::new(0))]
);
assert_eq!(unpack.arguments, vec![Parameter::private(Variable::new(0))]);
assert_eq!(
unpack.statements[0],
FlatStatement::Directive(FlatDirective::new(
(0..Bn128Field::get_required_bits())
.map(|i| FlatVariable::new(i + 1))
.map(|i| Variable::new(i + 1))
.collect(),
Solver::bits(Bn128Field::get_required_bits()),
vec![FlatVariable::new(0)]
vec![Variable::new(0)]
))
);
assert_eq!(
@ -703,7 +666,6 @@ mod tests {
#[cfg(test)]
mod sha256 {
use super::*;
use crate::ir::Interpreter;
#[test]
fn generate_sha256_constraints() {
@ -732,14 +694,14 @@ mod tests {
// function input should be offset by variable_count
assert_eq!(
compiled.arguments[0].id,
FlatVariable::new(directive.outputs.len() + 1)
Variable::new(directive.outputs.len() + 1)
);
// bellman variable #0: index 0 should equal 1
assert_eq!(
compiled.statements[1],
FlatStatement::Condition(
FlatVariable::new(0).into(),
Variable::new(0).into(),
FlatExpression::Number(Bn128Field::from(1)),
RuntimeError::BellmanOneBinding
)
@ -749,22 +711,11 @@ mod tests {
assert_eq!(
compiled.statements[2],
FlatStatement::Condition(
FlatVariable::new(1).into(),
FlatVariable::new(26936).into(),
Variable::new(1).into(),
Variable::new(26936).into(),
RuntimeError::BellmanInputBinding
)
);
let input: Vec<_> = (0..512)
.map(|_| 0)
.chain((0..256).map(|_| 1))
.map(Bn128Field::from)
.collect();
let ir = crate::ir::from_flat::from_flat(compiled);
let interpreter = Interpreter::default();
interpreter.execute(ir, &input).unwrap();
}
}
}

View file

@ -0,0 +1,91 @@
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub enum RuntimeError {
BellmanConstraint,
BellmanOneBinding,
BellmanInputBinding,
ArkConstraint,
ArkOneBinding,
ArkInputBinding,
Bitness,
Sum,
Equal,
Le,
BranchIsolation,
ConstantLtBitness,
ConstantLtSum,
LtBitness,
LtSum,
LtFinalBitness,
LtFinalSum,
LtSymetric,
Or,
Xor,
Inverse,
Euclidean,
ShaXor,
Division,
SourceAssertion(String),
ArgumentBitness,
SelectRangeCheck,
}
impl From<crate::zir::RuntimeError> for RuntimeError {
fn from(error: crate::zir::RuntimeError) -> Self {
match error {
crate::zir::RuntimeError::SourceAssertion(s) => RuntimeError::SourceAssertion(s),
crate::zir::RuntimeError::SelectRangeCheck => RuntimeError::SelectRangeCheck,
}
}
}
impl RuntimeError {
pub fn is_malicious(&self) -> bool {
use RuntimeError::*;
!matches!(
self,
SourceAssertion(_) | Inverse | LtSum | SelectRangeCheck | ArgumentBitness
)
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use RuntimeError::*;
let msg = match self {
BellmanConstraint => "Bellman constraint is unsatisfied",
BellmanOneBinding => "Bellman ~one binding is unsatisfied",
BellmanInputBinding => "Bellman input binding is unsatisfied",
ArkConstraint => "Ark constraint is unsatisfied",
ArkOneBinding => "Ark ~one binding is unsatisfied",
ArkInputBinding => "Ark input binding is unsatisfied",
Bitness => "Bitness check failed",
Sum => "Sum check failed",
Equal => "Equal check failed",
Le => "Constant Le check failed",
BranchIsolation => "Branch isolation failed",
ConstantLtBitness => "Bitness check failed in constant Lt check",
ConstantLtSum => "Sum check failed in constant Lt check",
LtBitness => "Bitness check failed in Lt check",
LtSum => "Sum check failed in Lt check",
LtFinalBitness => "Bitness check failed in final Lt check",
LtFinalSum => "Sum check failed in final Lt check",
LtSymetric => "Symetrical check failed in Lt check",
Or => "Or check failed",
Xor => "Xor check failed",
Inverse => "Division by zero",
Euclidean => "Euclidean check failed",
ShaXor => "Internal Sha check failed",
Division => "Division check failed",
SourceAssertion(m) => m.as_str(),
ArgumentBitness => "Argument bitness check failed",
SelectRangeCheck => "Out of bounds array access",
};
write!(f, "{}", msg)
}
}

View file

@ -0,0 +1,33 @@
use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct FormatString {
pub parts: Vec<String>,
}
impl fmt::Display for FormatString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.parts.join("{}"))
}
}
impl FormatString {
pub fn len(&self) -> usize {
self.parts.len() - 1
}
pub fn is_empty(&self) -> bool {
self.parts.len() == 1
}
}
impl From<&str> for FormatString {
fn from(s: &str) -> Self {
let parts = s.split("{}").map(|p| p.to_string());
FormatString {
parts: parts.collect(),
}
}
}

View file

@ -0,0 +1,13 @@
pub mod embed;
mod error;
mod format_string;
mod parameter;
mod solvers;
mod variable;
pub use self::embed::FlatEmbed;
pub use self::error::RuntimeError;
pub use self::parameter::Parameter;
pub use self::solvers::Solver;
pub use self::variable::Variable;
pub use format_string::FormatString;

View file

@ -1,47 +1,44 @@
use crate::flat_absy::flat_variable::FlatVariable;
use super::variable::Variable;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Clone, Copy)]
pub struct FlatParameter {
pub id: FlatVariable,
pub struct Parameter {
pub id: Variable,
pub private: bool,
}
impl FlatParameter {
fn new(id: FlatVariable, private: bool) -> Self {
FlatParameter { id, private }
impl Parameter {
fn new(id: Variable, private: bool) -> Self {
Parameter { id, private }
}
pub fn public(v: FlatVariable) -> Self {
pub fn public(v: Variable) -> Self {
Self::new(v, false)
}
pub fn private(v: FlatVariable) -> Self {
pub fn private(v: Variable) -> Self {
Self::new(v, true)
}
}
impl fmt::Display for FlatParameter {
impl fmt::Display for Parameter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let visibility = if self.private { "private " } else { "" };
write!(f, "{}{}", visibility, self.id)
}
}
impl fmt::Debug for FlatParameter {
impl fmt::Debug for Parameter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FlatParameter(id: {:?})", self.id)
write!(f, "Parameter(id: {:?})", self.id)
}
}
impl FlatParameter {
pub fn apply_substitution(
self,
substitution: &HashMap<FlatVariable, FlatVariable>,
) -> FlatParameter {
FlatParameter {
impl Parameter {
pub fn apply_substitution(self, substitution: &HashMap<Variable, Variable>) -> Parameter {
Parameter {
id: *substitution.get(&self.id).unwrap(),
private: self.private,
}

View file

@ -7,23 +7,23 @@ use std::fmt;
// id == 0 for ~one
// id < 0 for public outputs
#[derive(Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Ord, PartialOrd, Copy)]
pub struct FlatVariable {
id: isize,
pub struct Variable {
pub id: isize,
}
impl FlatVariable {
impl Variable {
pub fn new(id: usize) -> Self {
FlatVariable {
Variable {
id: 1 + id as isize,
}
}
pub fn one() -> Self {
FlatVariable { id: 0 }
Variable { id: 0 }
}
pub fn public(id: usize) -> Self {
FlatVariable {
Variable {
id: -(id as isize) - 1,
}
}
@ -35,21 +35,21 @@ impl FlatVariable {
pub fn try_from_human_readable(s: &str) -> Result<Self, &str> {
if s == "~one" {
return Ok(FlatVariable::one());
return Ok(Variable::one());
}
let mut public = s.split("~out_");
match public.nth(1) {
Some(v) => {
let v = v.parse().map_err(|_| s)?;
Ok(FlatVariable::public(v))
Ok(Variable::public(v))
}
None => {
let mut private = s.split('_');
match private.nth(1) {
Some(v) => {
let v = v.parse().map_err(|_| s)?;
Ok(FlatVariable::new(v))
Ok(Variable::new(v))
}
None => Err(s),
}
@ -58,7 +58,7 @@ impl FlatVariable {
}
}
impl fmt::Display for FlatVariable {
impl fmt::Display for Variable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.id {
0 => write!(f, "~one"),
@ -68,7 +68,7 @@ impl fmt::Display for FlatVariable {
}
}
impl fmt::Debug for FlatVariable {
impl fmt::Debug for Variable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.id {
0 => write!(f, "~one"),
@ -78,8 +78,8 @@ impl fmt::Debug for FlatVariable {
}
}
impl FlatVariable {
pub fn apply_substitution(self, substitution: &HashMap<FlatVariable, FlatVariable>) -> &Self {
impl Variable {
pub fn apply_substitution(self, substitution: &HashMap<Variable, Variable>) -> &Self {
substitution.get(&self).unwrap()
}
@ -94,18 +94,18 @@ mod tests {
#[test]
fn one() {
assert_eq!(format!("{}", FlatVariable::one()), "~one");
assert_eq!(format!("{}", Variable::one()), "~one");
}
#[test]
fn public() {
assert_eq!(format!("{}", FlatVariable::public(0)), "~out_0");
assert_eq!(format!("{}", FlatVariable::public(42)), "~out_42");
assert_eq!(format!("{}", Variable::public(0)), "~out_0");
assert_eq!(format!("{}", Variable::public(42)), "~out_42");
}
#[test]
fn private() {
assert_eq!(format!("{}", FlatVariable::new(0)), "_0");
assert_eq!(format!("{}", FlatVariable::new(42)), "_42");
assert_eq!(format!("{}", Variable::new(0)), "_0");
assert_eq!(format!("{}", Variable::new(42)), "_42");
}
}

View file

@ -0,0 +1,113 @@
// Generic walk through an IR AST. Not mutating in place
use super::*;
use crate::common::Variable;
use zokrates_field::Field;
pub trait Folder<T: Field>: Sized {
fn fold_program(&mut self, p: FlatProg<T>) -> FlatProg<T> {
fold_program(self, p)
}
fn fold_argument(&mut self, p: Parameter) -> Parameter {
fold_argument(self, p)
}
fn fold_variable(&mut self, v: Variable) -> Variable {
fold_variable(self, v)
}
fn fold_statement(&mut self, s: FlatStatement<T>) -> Vec<FlatStatement<T>> {
fold_statement(self, s)
}
fn fold_expression(&mut self, e: FlatExpression<T>) -> FlatExpression<T> {
fold_expression(self, e)
}
fn fold_directive(&mut self, d: FlatDirective<T>) -> FlatDirective<T> {
fold_directive(self, d)
}
}
pub fn fold_program<T: Field, F: Folder<T>>(f: &mut F, p: FlatProg<T>) -> FlatProg<T> {
FlatProg {
arguments: p
.arguments
.into_iter()
.map(|a| f.fold_argument(a))
.collect(),
statements: p
.statements
.into_iter()
.flat_map(|s| f.fold_statement(s))
.collect(),
return_count: p.return_count,
}
}
pub fn fold_statement<T: Field, F: Folder<T>>(
f: &mut F,
s: FlatStatement<T>,
) -> Vec<FlatStatement<T>> {
match s {
FlatStatement::Condition(left, right, error) => vec![FlatStatement::Condition(
f.fold_expression(left),
f.fold_expression(right),
error,
)],
FlatStatement::Definition(v, e) => vec![FlatStatement::Definition(
f.fold_variable(v),
f.fold_expression(e),
)],
FlatStatement::Directive(d) => vec![FlatStatement::Directive(f.fold_directive(d))],
FlatStatement::Log(s, e) => vec![FlatStatement::Log(
s,
e.into_iter()
.map(|(t, e)| (t, e.into_iter().map(|e| f.fold_expression(e)).collect()))
.collect(),
)],
}
}
pub fn fold_expression<T: Field, F: Folder<T>>(
f: &mut F,
e: FlatExpression<T>,
) -> FlatExpression<T> {
match e {
FlatExpression::Number(n) => FlatExpression::Number(n),
FlatExpression::Identifier(id) => FlatExpression::Identifier(f.fold_variable(id)),
FlatExpression::Add(box left, box right) => {
FlatExpression::Add(box f.fold_expression(left), box f.fold_expression(right))
}
FlatExpression::Sub(box left, box right) => {
FlatExpression::Sub(box f.fold_expression(left), box f.fold_expression(right))
}
FlatExpression::Mult(box left, box right) => {
FlatExpression::Mult(box f.fold_expression(left), box f.fold_expression(right))
}
}
}
pub fn fold_directive<T: Field, F: Folder<T>>(f: &mut F, ds: FlatDirective<T>) -> FlatDirective<T> {
FlatDirective {
inputs: ds
.inputs
.into_iter()
.map(|e| f.fold_expression(e))
.collect(),
outputs: ds.outputs.into_iter().map(|o| f.fold_variable(o)).collect(),
..ds
}
}
pub fn fold_argument<T: Field, F: Folder<T>>(f: &mut F, a: Parameter) -> Parameter {
Parameter {
id: f.fold_variable(a.id),
private: a.private,
}
}
pub fn fold_variable<T: Field, F: Folder<T>>(_f: &mut F, v: Variable) -> Variable {
v
}

View file

@ -5,109 +5,35 @@
//! @author Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>
//! @date 2017
pub mod flat_parameter;
pub mod flat_variable;
pub mod folder;
pub mod utils;
pub use self::flat_parameter::FlatParameter;
pub use self::flat_variable::FlatVariable;
use crate::common::FormatString;
pub use crate::common::Parameter;
pub use crate::common::RuntimeError;
pub use crate::common::Variable;
use serde::{Deserialize, Serialize};
pub use utils::{
flat_expression_from_bits, flat_expression_from_expression_summands,
flat_expression_from_variable_summands,
};
use crate::solvers::Solver;
use crate::common::Solver;
use crate::typed::ConcreteType;
use std::collections::HashMap;
use std::fmt;
use zokrates_field::Field;
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub enum RuntimeError {
BellmanConstraint,
BellmanOneBinding,
BellmanInputBinding,
ArkConstraint,
ArkOneBinding,
ArkInputBinding,
Bitness,
Sum,
Equal,
Le,
BranchIsolation,
ConstantLtBitness,
ConstantLtSum,
LtBitness,
LtSum,
LtFinalBitness,
LtFinalSum,
LtSymetric,
Or,
Xor,
Inverse,
Euclidean,
ShaXor,
Division,
SourceAssertion(String),
ArgumentBitness,
SelectRangeCheck,
}
impl RuntimeError {
pub(crate) fn is_malicious(&self) -> bool {
use RuntimeError::*;
!matches!(
self,
SourceAssertion(_) | Inverse | LtSum | SelectRangeCheck | ArgumentBitness
)
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use RuntimeError::*;
let msg = match self {
BellmanConstraint => "Bellman constraint is unsatisfied",
BellmanOneBinding => "Bellman ~one binding is unsatisfied",
BellmanInputBinding => "Bellman input binding is unsatisfied",
ArkConstraint => "Ark constraint is unsatisfied",
ArkOneBinding => "Ark ~one binding is unsatisfied",
ArkInputBinding => "Ark input binding is unsatisfied",
Bitness => "Bitness check failed",
Sum => "Sum check failed",
Equal => "Equal check failed",
Le => "Constant Le check failed",
BranchIsolation => "Branch isolation failed",
ConstantLtBitness => "Bitness check failed in constant Lt check",
ConstantLtSum => "Sum check failed in constant Lt check",
LtBitness => "Bitness check failed in Lt check",
LtSum => "Sum check failed in Lt check",
LtFinalBitness => "Bitness check failed in final Lt check",
LtFinalSum => "Sum check failed in final Lt check",
LtSymetric => "Symetrical check failed in Lt check",
Or => "Or check failed",
Xor => "Xor check failed",
Inverse => "Division by zero",
Euclidean => "Euclidean check failed",
ShaXor => "Internal Sha check failed",
Division => "Division check failed",
SourceAssertion(m) => m.as_str(),
ArgumentBitness => "Argument bitness check failed",
SelectRangeCheck => "Out of bounds array access",
};
write!(f, "{}", msg)
}
}
pub type FlatProg<T> = FlatFunction<T>;
pub type FlatFunction<T> = FlatFunctionIterator<T, Vec<FlatStatement<T>>>;
pub type FlatProgIterator<T, I> = FlatFunctionIterator<T, I>;
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FlatFunctionIterator<T, I: IntoIterator<Item = FlatStatement<T>>> {
/// Arguments of the function
pub arguments: Vec<FlatParameter>,
pub arguments: Vec<Parameter>,
/// Vector of statements that are executed when running the function
pub statements: I,
/// Number of outputs
@ -154,11 +80,12 @@ impl<T: Field> fmt::Display for FlatFunction<T> {
///
/// * r1cs - R1CS in standard JSON data format
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum FlatStatement<T> {
Condition(FlatExpression<T>, FlatExpression<T>, RuntimeError),
Definition(FlatVariable, FlatExpression<T>),
Definition(Variable, FlatExpression<T>),
Directive(FlatDirective<T>),
Log(FormatString, Vec<(ConcreteType, Vec<FlatExpression<T>>)>),
}
impl<T: Field> fmt::Display for FlatStatement<T> {
@ -169,6 +96,22 @@ impl<T: Field> fmt::Display for FlatStatement<T> {
write!(f, "{} == {} // {}", lhs, rhs, message)
}
FlatStatement::Directive(ref d) => write!(f, "{}", d),
FlatStatement::Log(ref l, ref expressions) => write!(
f,
"log(\"{}\"), {})",
l,
expressions
.iter()
.map(|(_, e)| format!(
"[{}]",
e.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
}
}
}
@ -176,7 +119,7 @@ impl<T: Field> fmt::Display for FlatStatement<T> {
impl<T: Field> FlatStatement<T> {
pub fn apply_substitution(
self,
substitution: &HashMap<FlatVariable, FlatVariable>,
substitution: &HashMap<Variable, Variable>,
) -> FlatStatement<T> {
match self {
FlatStatement::Definition(id, x) => FlatStatement::Definition(
@ -206,6 +149,19 @@ impl<T: Field> FlatStatement<T> {
..d
})
}
FlatStatement::Log(l, e) => FlatStatement::Log(
l,
e.into_iter()
.map(|(t, e)| {
(
t,
e.into_iter()
.map(|e| e.apply_substitution(substitution))
.collect(),
)
})
.collect(),
),
}
}
}
@ -213,13 +169,13 @@ impl<T: Field> FlatStatement<T> {
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub struct FlatDirective<T> {
pub inputs: Vec<FlatExpression<T>>,
pub outputs: Vec<FlatVariable>,
pub outputs: Vec<Variable>,
pub solver: Solver,
}
impl<T> FlatDirective<T> {
pub fn new<E: Into<FlatExpression<T>>>(
outputs: Vec<FlatVariable>,
outputs: Vec<Variable>,
solver: Solver,
inputs: Vec<E>,
) -> Self {
@ -257,7 +213,7 @@ impl<T: Field> fmt::Display for FlatDirective<T> {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum FlatExpression<T> {
Number(T),
Identifier(FlatVariable),
Identifier(Variable),
Add(Box<FlatExpression<T>>, Box<FlatExpression<T>>),
Sub(Box<FlatExpression<T>>, Box<FlatExpression<T>>),
Mult(Box<FlatExpression<T>>, Box<FlatExpression<T>>),
@ -272,7 +228,7 @@ impl<T> From<T> for FlatExpression<T> {
impl<T: Field> FlatExpression<T> {
pub fn apply_substitution(
self,
substitution: &HashMap<FlatVariable, FlatVariable>,
substitution: &HashMap<Variable, Variable>,
) -> FlatExpression<T> {
match self {
e @ FlatExpression::Number(_) => e,
@ -328,13 +284,13 @@ impl<T: Field> fmt::Display for FlatExpression<T> {
}
}
impl<T: Field> From<FlatVariable> for FlatExpression<T> {
fn from(v: FlatVariable) -> FlatExpression<T> {
impl<T: Field> From<Variable> for FlatExpression<T> {
fn from(v: Variable) -> FlatExpression<T> {
FlatExpression::Identifier(v)
}
}
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Error {
message: String,
}

View file

@ -0,0 +1,53 @@
use crate::flat::{FlatExpression, Variable};
use zokrates_field::Field;
// util to convert a vector of `(coefficient, expression)` to a flat_expression
// we build a binary tree of additions by splitting the vector recursively
pub fn flat_expression_from_expression_summands<T: Field, U: Clone + Into<FlatExpression<T>>>(
v: &[(T, U)],
) -> FlatExpression<T> {
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (val, var) = v[0].clone();
FlatExpression::Mult(box FlatExpression::Number(val), box var.into())
}
n => {
let (u, v) = v.split_at(n / 2);
FlatExpression::Add(
box flat_expression_from_expression_summands(u),
box flat_expression_from_expression_summands(v),
)
}
}
}
pub fn flat_expression_from_bits<T: Field>(v: Vec<FlatExpression<T>>) -> FlatExpression<T> {
flat_expression_from_expression_summands(
&v.into_iter()
.rev()
.enumerate()
.map(|(index, var)| (T::from(2).pow(index), var))
.collect::<Vec<_>>(),
)
}
pub fn flat_expression_from_variable_summands<T: Field>(v: &[(T, usize)]) -> FlatExpression<T> {
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (val, var) = v[0].clone();
FlatExpression::Mult(
box FlatExpression::Number(val),
box FlatExpression::Identifier(Variable::new(var)),
)
}
n => {
let (u, v) = v.split_at(n / 2);
FlatExpression::Add(
box flat_expression_from_variable_summands(u),
box flat_expression_from_variable_summands(v),
)
}
}
}

View file

@ -1,15 +1,15 @@
use crate::flat_absy::FlatParameter;
use crate::flat_absy::FlatVariable;
use crate::ir::folder::Folder;
use crate::ir::Directive;
use crate::ir::Parameter;
use crate::ir::ProgIterator;
use crate::ir::Statement;
use crate::ir::Variable;
use std::collections::HashSet;
use zokrates_field::Field;
#[derive(Debug)]
pub struct UnconstrainedVariableDetector {
pub(self) variables: HashSet<FlatVariable>,
pub(self) variables: HashSet<Variable>,
}
impl UnconstrainedVariableDetector {
@ -33,10 +33,10 @@ impl UnconstrainedVariableDetector {
}
impl<T: Field> Folder<T> for UnconstrainedVariableDetector {
fn fold_argument(&mut self, p: FlatParameter) -> FlatParameter {
fn fold_argument(&mut self, p: Parameter) -> Parameter {
p
}
fn fold_variable(&mut self, v: FlatVariable) -> FlatVariable {
fn fold_variable(&mut self, v: Variable) -> Variable {
self.variables.remove(&v);
v
}

View file

@ -1,4 +1,5 @@
use crate::flat_absy::FlatVariable;
use super::Witness;
use crate::common::Variable;
use serde::{Deserialize, Serialize};
use std::collections::btree_map::{BTreeMap, Entry};
use std::fmt;
@ -54,10 +55,10 @@ impl<T: Field> fmt::Display for QuadComb<T> {
}
#[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub struct LinComb<T>(pub Vec<(FlatVariable, T)>);
pub struct LinComb<T>(pub Vec<(Variable, T)>);
#[derive(PartialEq, PartialOrd, Clone, Eq, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct CanonicalLinComb<T>(pub BTreeMap<FlatVariable, T>);
pub struct CanonicalLinComb<T>(pub BTreeMap<Variable, T>);
#[derive(PartialEq, PartialOrd, Clone, Eq, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct CanonicalQuadComb<T> {
@ -81,7 +82,7 @@ impl<T> From<CanonicalLinComb<T>> for LinComb<T> {
}
impl<T> LinComb<T> {
pub fn summand<U: Into<T>>(mult: U, var: FlatVariable) -> LinComb<T> {
pub fn summand<U: Into<T>>(mult: U, var: Variable) -> LinComb<T> {
let res = vec![(var, mult.into())];
LinComb(res)
@ -105,7 +106,7 @@ impl<T: Field> LinComb<T> {
// take the first variable in the lincomb
let first = &self.0[0].0;
if first != &FlatVariable::one() {
if first != &Variable::one() {
return Err(self);
}
@ -119,7 +120,13 @@ impl<T: Field> LinComb<T> {
}
}
pub fn try_summand(self) -> Result<(FlatVariable, T), Self> {
pub fn is_assignee(&self, witness: &Witness<T>) -> bool {
self.0.len() == 1
&& self.0.get(0).unwrap().1 == T::from(1)
&& !witness.0.contains_key(&self.0.get(0).unwrap().0)
}
pub fn try_summand(self) -> Result<(Variable, T), Self> {
match self.0.len() {
// if the lincomb is empty, it is not reduceable to a summand
0 => Err(self),
@ -143,7 +150,7 @@ impl<T: Field> LinComb<T> {
}
pub fn one() -> LinComb<T> {
Self::summand(1, FlatVariable::one())
Self::summand(1, Variable::one())
}
}
@ -212,8 +219,8 @@ impl<T: Field> fmt::Display for LinComb<T> {
}
}
impl<T: Field> From<FlatVariable> for LinComb<T> {
fn from(v: FlatVariable) -> LinComb<T> {
impl<T: Field> From<Variable> for LinComb<T> {
fn from(v: Variable) -> LinComb<T> {
let r = vec![(v, T::one())];
LinComb(r)
}
@ -277,32 +284,32 @@ mod tests {
#[test]
fn add_zero() {
let a: LinComb<Bn128Field> = LinComb::zero();
let b: LinComb<Bn128Field> = FlatVariable::new(42).into();
let b: LinComb<Bn128Field> = Variable::new(42).into();
let c = a + b.clone();
assert_eq!(c, b);
}
#[test]
fn add() {
let a: LinComb<Bn128Field> = FlatVariable::new(42).into();
let b: LinComb<Bn128Field> = FlatVariable::new(42).into();
let a: LinComb<Bn128Field> = Variable::new(42).into();
let b: LinComb<Bn128Field> = Variable::new(42).into();
let c = a + b;
let expected_vec = vec![
(FlatVariable::new(42), Bn128Field::from(1)),
(FlatVariable::new(42), Bn128Field::from(1)),
(Variable::new(42), Bn128Field::from(1)),
(Variable::new(42), Bn128Field::from(1)),
];
assert_eq!(c, LinComb(expected_vec));
}
#[test]
fn sub() {
let a: LinComb<Bn128Field> = FlatVariable::new(42).into();
let b: LinComb<Bn128Field> = FlatVariable::new(42).into();
let a: LinComb<Bn128Field> = Variable::new(42).into();
let b: LinComb<Bn128Field> = Variable::new(42).into();
let c = a - b;
let expected_vec = vec![
(FlatVariable::new(42), Bn128Field::from(1)),
(FlatVariable::new(42), Bn128Field::from(-1)),
(Variable::new(42), Bn128Field::from(1)),
(Variable::new(42), Bn128Field::from(-1)),
];
assert_eq!(c, LinComb(expected_vec));
@ -311,7 +318,7 @@ mod tests {
#[test]
fn display() {
let a: LinComb<Bn128Field> =
LinComb::from(FlatVariable::new(42)) + LinComb::summand(3, FlatVariable::new(21));
LinComb::from(Variable::new(42)) + LinComb::summand(3, Variable::new(21));
assert_eq!(&a.to_string(), "1 * _42 + 3 * _21");
let zero: LinComb<Bn128Field> = LinComb::zero();
assert_eq!(&zero.to_string(), "0");
@ -322,8 +329,8 @@ mod tests {
use super::*;
#[test]
fn from_linear() {
let a: LinComb<Bn128Field> = LinComb::summand(3, FlatVariable::new(42))
+ LinComb::summand(4, FlatVariable::new(33));
let a: LinComb<Bn128Field> =
LinComb::summand(3, Variable::new(42)) + LinComb::summand(4, Variable::new(33));
let expected = QuadComb {
left: LinComb::one(),
right: a.clone(),
@ -344,14 +351,14 @@ mod tests {
#[test]
fn display() {
let a: QuadComb<Bn128Field> = QuadComb {
left: LinComb::summand(3, FlatVariable::new(42))
+ LinComb::summand(4, FlatVariable::new(33)),
right: LinComb::summand(1, FlatVariable::new(21)),
left: LinComb::summand(3, Variable::new(42))
+ LinComb::summand(4, Variable::new(33)),
right: LinComb::summand(1, Variable::new(21)),
};
assert_eq!(&a.to_string(), "(3 * _42 + 4 * _33) * (1 * _21)");
let a: QuadComb<Bn128Field> = QuadComb {
left: LinComb::zero(),
right: LinComb::summand(1, FlatVariable::new(21)),
right: LinComb::summand(1, Variable::new(21)),
};
assert_eq!(&a.to_string(), "(0) * (1 * _21)");
}
@ -363,19 +370,19 @@ mod tests {
#[test]
fn try_summand() {
let summand = LinComb(vec![
(FlatVariable::new(42), Bn128Field::from(1)),
(FlatVariable::new(42), Bn128Field::from(2)),
(FlatVariable::new(42), Bn128Field::from(3)),
(Variable::new(42), Bn128Field::from(1)),
(Variable::new(42), Bn128Field::from(2)),
(Variable::new(42), Bn128Field::from(3)),
]);
assert_eq!(
summand.try_summand(),
Ok((FlatVariable::new(42), Bn128Field::from(6)))
Ok((Variable::new(42), Bn128Field::from(6)))
);
let not_summand = LinComb(vec![
(FlatVariable::new(41), Bn128Field::from(1)),
(FlatVariable::new(42), Bn128Field::from(2)),
(FlatVariable::new(42), Bn128Field::from(3)),
(Variable::new(41), Bn128Field::from(1)),
(Variable::new(42), Bn128Field::from(2)),
(Variable::new(42), Bn128Field::from(3)),
]);
assert!(not_summand.try_summand().is_err());

View file

@ -1,7 +1,7 @@
// Generic walk through an IR AST. Not mutating in place
use crate::flat_absy::flat_variable::FlatVariable;
use crate::ir::*;
use super::*;
use crate::common::Variable;
use zokrates_field::Field;
pub trait Folder<T: Field>: Sized {
@ -9,11 +9,11 @@ pub trait Folder<T: Field>: Sized {
fold_program(self, p)
}
fn fold_argument(&mut self, p: FlatParameter) -> FlatParameter {
fn fold_argument(&mut self, p: Parameter) -> Parameter {
fold_argument(self, p)
}
fn fold_variable(&mut self, v: FlatVariable) -> FlatVariable {
fn fold_variable(&mut self, v: Variable) -> Variable {
fold_variable(self, v)
}
@ -58,6 +58,19 @@ pub fn fold_statement<T: Field, F: Folder<T>>(f: &mut F, s: Statement<T>) -> Vec
message,
)],
Statement::Directive(dir) => vec![Statement::Directive(f.fold_directive(dir))],
Statement::Log(l, e) => vec![Statement::Log(
l,
e.into_iter()
.map(|(t, e)| {
(
t,
e.into_iter()
.map(|e| f.fold_linear_combination(e))
.collect(),
)
})
.collect(),
)],
}
}
@ -91,13 +104,13 @@ pub fn fold_directive<T: Field, F: Folder<T>>(f: &mut F, ds: Directive<T>) -> Di
}
}
pub fn fold_argument<T: Field, F: Folder<T>>(f: &mut F, a: FlatParameter) -> FlatParameter {
FlatParameter {
pub fn fold_argument<T: Field, F: Folder<T>>(f: &mut F, a: Parameter) -> Parameter {
Parameter {
id: f.fold_variable(a.id),
private: a.private,
}
}
pub fn fold_variable<T: Field, F: Folder<T>>(_f: &mut F, v: FlatVariable) -> FlatVariable {
pub fn fold_variable<T: Field, F: Folder<T>>(_f: &mut F, v: Variable) -> Variable {
v
}

View file

@ -1,6 +1,4 @@
use crate::flat_absy::{
FlatDirective, FlatExpression, FlatProgIterator, FlatStatement, FlatVariable,
};
use crate::flat::{FlatDirective, FlatExpression, FlatProgIterator, FlatStatement, Variable};
use crate::ir::{Directive, LinComb, ProgIterator, QuadComb, Statement};
use zokrates_field::Field;
@ -33,7 +31,7 @@ impl<T: Field> From<FlatExpression<T>> for LinComb<T> {
fn from(flat_expression: FlatExpression<T>) -> LinComb<T> {
match flat_expression {
FlatExpression::Number(ref n) if *n == T::from(0) => LinComb::zero(),
FlatExpression::Number(n) => LinComb::summand(n, FlatVariable::one()),
FlatExpression::Number(n) => LinComb::summand(n, Variable::one()),
FlatExpression::Identifier(id) => LinComb::from(id),
FlatExpression::Add(box e1, box e2) => LinComb::from(e1) + LinComb::from(e2),
FlatExpression::Sub(box e1, box e2) => LinComb::from(e1) - LinComb::from(e2),
@ -48,7 +46,7 @@ impl<T: Field> From<FlatExpression<T>> for LinComb<T> {
FlatExpression::Mult(
box FlatExpression::Number(n1),
box FlatExpression::Number(n2),
) => LinComb::summand(n1 * n2, FlatVariable::one()),
) => LinComb::summand(n1 * n2, Variable::one()),
e => unreachable!("{}", e),
}
}
@ -74,6 +72,13 @@ impl<T: Field> From<FlatStatement<T>> for Statement<T> {
e => Statement::Constraint(LinComb::from(e).into(), var.into(), None),
},
FlatStatement::Directive(ds) => Statement::Directive(ds.into()),
FlatStatement::Log(l, expressions) => Statement::Log(
l,
expressions
.into_iter()
.map(|(t, e)| (t, e.into_iter().map(LinComb::from).collect()))
.collect(),
),
}
}
}
@ -109,7 +114,7 @@ mod tests {
fn one() {
// 1
let one = FlatExpression::Number(Bn128Field::from(1));
let expected: LinComb<Bn128Field> = FlatVariable::one().into();
let expected: LinComb<Bn128Field> = Variable::one().into();
assert_eq!(LinComb::from(one), expected);
}
@ -117,7 +122,7 @@ mod tests {
fn forty_two() {
// 42
let one = FlatExpression::Number(Bn128Field::from(42));
let expected: LinComb<Bn128Field> = LinComb::summand(42, FlatVariable::one());
let expected: LinComb<Bn128Field> = LinComb::summand(42, Variable::one());
assert_eq!(LinComb::from(one), expected);
}
@ -125,11 +130,11 @@ mod tests {
fn add() {
// x + y
let add = FlatExpression::Add(
box FlatExpression::Identifier(FlatVariable::new(42)),
box FlatExpression::Identifier(FlatVariable::new(21)),
box FlatExpression::Identifier(Variable::new(42)),
box FlatExpression::Identifier(Variable::new(21)),
);
let expected: LinComb<Bn128Field> =
LinComb::summand(1, FlatVariable::new(42)) + LinComb::summand(1, FlatVariable::new(21));
LinComb::summand(1, Variable::new(42)) + LinComb::summand(1, Variable::new(21));
assert_eq!(LinComb::from(add), expected);
}
@ -139,15 +144,15 @@ mod tests {
let add = FlatExpression::Add(
box FlatExpression::Mult(
box FlatExpression::Number(Bn128Field::from(42)),
box FlatExpression::Identifier(FlatVariable::new(42)),
box FlatExpression::Identifier(Variable::new(42)),
),
box FlatExpression::Mult(
box FlatExpression::Number(Bn128Field::from(21)),
box FlatExpression::Identifier(FlatVariable::new(21)),
box FlatExpression::Identifier(Variable::new(21)),
),
);
let expected: LinComb<Bn128Field> = LinComb::summand(42, FlatVariable::new(42))
+ LinComb::summand(21, FlatVariable::new(21));
let expected: LinComb<Bn128Field> =
LinComb::summand(42, Variable::new(42)) + LinComb::summand(21, Variable::new(21));
assert_eq!(LinComb::from(add), expected);
}
@ -156,16 +161,16 @@ mod tests {
// x*42 + y*21
let add = FlatExpression::Add(
box FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(42)),
box FlatExpression::Identifier(Variable::new(42)),
box FlatExpression::Number(Bn128Field::from(42)),
),
box FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(21)),
box FlatExpression::Identifier(Variable::new(21)),
box FlatExpression::Number(Bn128Field::from(21)),
),
);
let expected: LinComb<Bn128Field> = LinComb::summand(42, FlatVariable::new(42))
+ LinComb::summand(21, FlatVariable::new(21));
let expected: LinComb<Bn128Field> =
LinComb::summand(42, Variable::new(42)) + LinComb::summand(21, Variable::new(21));
assert_eq!(LinComb::from(add), expected);
}
}

View file

@ -1,15 +1,15 @@
use crate::flat_absy::flat_parameter::FlatParameter;
use crate::flat_absy::{FlatVariable, RuntimeError};
use crate::solvers::Solver;
use crate::common::FormatString;
use crate::typed::ConcreteType;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::fmt;
use std::hash::Hash;
use zokrates_field::Field;
mod check;
mod expression;
pub mod folder;
pub mod from_flat;
mod interpreter;
mod serialize;
pub mod smtlib2;
pub mod visitor;
@ -18,18 +18,24 @@ mod witness;
pub use self::expression::QuadComb;
pub use self::expression::{CanonicalLinComb, LinComb};
pub use self::serialize::ProgEnum;
pub use crate::common::Parameter;
pub use crate::common::RuntimeError;
pub use crate::common::Solver;
pub use crate::common::Variable;
pub use self::interpreter::{Error, ExecutionResult, Interpreter};
pub use self::witness::Witness;
#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
pub enum Statement<T> {
Constraint(QuadComb<T>, LinComb<T>, Option<RuntimeError>),
Directive(Directive<T>),
Log(FormatString, Vec<(ConcreteType, Vec<LinComb<T>>)>),
}
pub type PublicInputs = BTreeSet<Variable>;
impl<T: Field> Statement<T> {
pub fn definition<U: Into<QuadComb<T>>>(v: FlatVariable, e: U) -> Self {
pub fn definition<U: Into<QuadComb<T>>>(v: Variable, e: U) -> Self {
Statement::Constraint(e.into(), v.into(), None)
}
@ -41,7 +47,7 @@ impl<T: Field> Statement<T> {
#[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub struct Directive<T> {
pub inputs: Vec<QuadComb<T>>,
pub outputs: Vec<FlatVariable>,
pub outputs: Vec<Variable>,
pub solver: Solver,
}
@ -70,21 +76,37 @@ impl<T: Field> fmt::Display for Statement<T> {
match *self {
Statement::Constraint(ref quad, ref lin, _) => write!(f, "{} == {}", quad, lin),
Statement::Directive(ref s) => write!(f, "{}", s),
Statement::Log(ref s, ref expressions) => write!(
f,
"log(\"{}\", {})",
s,
expressions
.iter()
.map(|(_, l)| format!(
"[{}]",
l.iter()
.map(|l| l.to_string())
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
}
}
}
pub type Prog<T> = ProgIterator<T, Vec<Statement<T>>>;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct ProgIterator<T, I: IntoIterator<Item = Statement<T>>> {
pub arguments: Vec<FlatParameter>,
pub arguments: Vec<Parameter>,
pub return_count: usize,
pub statements: I,
}
impl<T, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn new(arguments: Vec<FlatParameter>, statements: I, return_count: usize) -> Self {
pub fn new(arguments: Vec<Parameter>, statements: I, return_count: usize) -> Self {
Self {
arguments,
return_count,
@ -100,17 +122,25 @@ impl<T, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
}
}
pub fn returns(&self) -> Vec<FlatVariable> {
(0..self.return_count).map(FlatVariable::public).collect()
pub fn returns(&self) -> Vec<Variable> {
(0..self.return_count).map(Variable::public).collect()
}
pub fn public_count(&self) -> usize {
self.arguments.iter().filter(|a| !a.private).count() + self.return_count
}
pub fn public_inputs(&self) -> PublicInputs {
self.arguments
.iter()
.filter(|a| !a.private)
.map(|a| a.id)
.collect()
}
}
impl<T: Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn public_inputs(&self, witness: &Witness<T>) -> Vec<T> {
pub fn public_inputs_values(&self, witness: &Witness<T>) -> Vec<T> {
self.arguments
.iter()
.filter(|p| !p.private)
@ -139,15 +169,21 @@ impl<T> Prog<T> {
impl<T: Field> fmt::Display for Prog<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let returns = (0..self.return_count)
.map(Variable::public)
.map(|e| format!("{}", e))
.collect::<Vec<_>>()
.join(", ");
writeln!(
f,
"def main({}) -> ({}):",
"def main({}) -> ({}) {{",
self.arguments
.iter()
.map(|v| format!("{}", v))
.collect::<Vec<_>>()
.join(", "),
self.return_count,
returns,
)?;
for s in &self.statements {
writeln!(f, "\t{}", s)?;
@ -156,11 +192,14 @@ impl<T: Field> fmt::Display for Prog<T> {
f,
"\treturn {}",
(0..self.return_count)
.map(FlatVariable::public)
.map(Variable::public)
.map(|e| format!("{}", e))
.collect::<Vec<_>>()
.join(", ")
)
)?;
writeln!(f, "\treturn {}", returns)?;
writeln!(f, "}}")
}
}
@ -176,10 +215,10 @@ mod tests {
fn print_constraint() {
let c: Statement<Bn128Field> = Statement::Constraint(
QuadComb::from_linear_combinations(
FlatVariable::new(42).into(),
FlatVariable::new(42).into(),
Variable::new(42).into(),
Variable::new(42).into(),
),
FlatVariable::new(42).into(),
Variable::new(42).into(),
None,
);
assert_eq!(format!("{}", c), "(1 * _42) * (1 * _42) == 1 * _42")

View file

@ -1,7 +1,6 @@
use crate::{
ir::{ProgIterator, Statement},
static_analysis::UnconstrainedVariableDetector,
};
use crate::ir::check::UnconstrainedVariableDetector;
use super::{ProgIterator, Statement};
use serde_cbor::{self, StreamDeserializer};
use std::io::{Read, Write};
use zokrates_field::*;
@ -11,7 +10,7 @@ type DynamicError = Box<dyn std::error::Error>;
const ZOKRATES_MAGIC: &[u8; 4] = &[0x5a, 0x4f, 0x4b, 0];
const ZOKRATES_VERSION_2: &[u8; 4] = &[0, 0, 0, 2];
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum ProgEnum<
Bls12_381I: IntoIterator<Item = Statement<Bls12_381Field>>,
Bn128I: IntoIterator<Item = Statement<Bn128Field>>,
@ -60,7 +59,7 @@ impl<T: Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
/// serialize a program iterator, returning the number of constraints serialized
/// Note that we only return constraints, not other statements such as directives
pub fn serialize<W: Write>(self, mut w: W) -> Result<usize, DynamicError> {
use crate::ir::folder::Folder;
use super::folder::Folder;
w.write_all(ZOKRATES_MAGIC)?;
w.write_all(ZOKRATES_VERSION_2)?;
@ -137,7 +136,7 @@ impl<'de, R: Read>
struct ArgumentsVisitor;
impl<'de> serde::de::Visitor<'de> for ArgumentsVisitor {
type Value = Vec<crate::ir::FlatParameter>;
type Value = Vec<super::Parameter>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("seq of flat param")
}
@ -239,13 +238,13 @@ impl<'de, R: Read>
#[cfg(test)]
mod tests {
use super::*;
use crate::ir;
use crate::ir::Prog;
use std::io::{Cursor, Seek, SeekFrom};
use zokrates_field::{Bls12_381Field, Bn128Field};
#[test]
fn ser_deser_v2() {
let p: ir::Prog<Bn128Field> = ir::Prog::default();
let p: Prog<Bn128Field> = Prog::default();
let mut buffer = Cursor::new(vec![]);
p.clone().serialize(&mut buffer).unwrap();
@ -258,7 +257,7 @@ mod tests {
assert_eq!(ProgEnum::Bn128Program(p), deserialized_p.collect());
let p: ir::Prog<Bls12_381Field> = ir::Prog::default();
let p: Prog<Bls12_381Field> = Prog::default();
let mut buffer = Cursor::new(vec![]);
p.clone().serialize(&mut buffer).unwrap();

View file

@ -20,23 +20,23 @@ impl<T: Field> fmt::Display for SMTLib2Display<'_, T> {
}
}
struct FlatVariableCollector {
variables: BTreeSet<FlatVariable>,
struct VariableCollector {
variables: BTreeSet<Variable>,
}
impl<T: Field> Visitor<T> for FlatVariableCollector {
fn visit_variable(&mut self, v: &FlatVariable) {
impl<T: Field> Visitor<T> for VariableCollector {
fn visit_variable(&mut self, v: &Variable) {
self.variables.insert(*v);
}
}
impl<T: Field> SMTLib2 for Prog<T> {
fn to_smtlib2(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut collector = FlatVariableCollector {
variables: BTreeSet::<FlatVariable>::new(),
let mut collector = VariableCollector {
variables: BTreeSet::<Variable>::new(),
};
collector.visit_module(self);
collector.variables.insert(FlatVariable::one());
collector.variables.insert(Variable::one());
writeln!(f, "; Auto generated by ZoKrates")?;
writeln!(
@ -86,6 +86,7 @@ impl<T: Field> SMTLib2 for Statement<T> {
write!(f, " |~prime|))")
}
Statement::Directive(ref s) => s.to_smtlib2(f),
Statement::Log(..) => write!(f, ""),
}
}
}
@ -122,7 +123,7 @@ impl<T: Field> SMTLib2 for LinComb<T> {
}
}
impl SMTLib2 for FlatVariable {
impl SMTLib2 for Variable {
fn to_smtlib2(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "|{}|", self)
}

View file

@ -1,7 +1,7 @@
// Generic walk through an IR AST. Not mutating in place
use crate::flat_absy::flat_variable::FlatVariable;
use crate::ir::*;
use super::*;
use crate::common::Variable;
use zokrates_field::Field;
pub trait Visitor<T: Field>: Sized {
@ -9,11 +9,11 @@ pub trait Visitor<T: Field>: Sized {
visit_module(self, p)
}
fn visit_argument(&mut self, p: &FlatParameter) {
fn visit_argument(&mut self, p: &Parameter) {
visit_argument(self, p)
}
fn visit_variable(&mut self, v: &FlatVariable) {
fn visit_variable(&mut self, v: &Variable) {
visit_variable(self, v)
}
@ -61,6 +61,13 @@ pub fn visit_statement<T: Field, F: Visitor<T>>(f: &mut F, s: &Statement<T>) {
}
}
Statement::Directive(dir) => f.visit_directive(dir),
Statement::Log(_, expressions) => {
for (_, e) in expressions {
for e in e {
f.visit_linear_combination(e);
}
}
}
}
}
@ -85,11 +92,11 @@ pub fn visit_directive<T: Field, F: Visitor<T>>(f: &mut F, ds: &Directive<T>) {
}
}
pub fn visit_argument<T: Field, F: Visitor<T>>(f: &mut F, a: &FlatParameter) {
pub fn visit_argument<T: Field, F: Visitor<T>>(f: &mut F, a: &Parameter) {
f.visit_variable(&a.id)
}
pub fn visit_variable<T: Field, F: Visitor<T>>(_f: &mut F, _v: &FlatVariable) {}
pub fn visit_variable<T: Field, F: Visitor<T>>(_f: &mut F, _v: &Variable) {}
pub fn visit_value<T: Field, F: Visitor<T>>(_f: &mut F, _v: &T) {}

View file

@ -1,12 +1,12 @@
use crate::flat_absy::FlatVariable;
use crate::common::Variable;
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::io;
use std::io::{Read, Write};
use zokrates_field::Field;
#[derive(Clone, Debug, PartialEq)]
pub struct Witness<T>(pub BTreeMap<FlatVariable, T>);
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct Witness<T>(pub BTreeMap<Variable, T>);
impl<T: Field> Witness<T> {
pub fn return_values(&self) -> Vec<T> {
@ -17,11 +17,15 @@ impl<T: Field> Witness<T> {
.collect::<HashMap<_, _>>();
(0..out.len())
.map(|i| *out.get(&FlatVariable::public(i)).unwrap())
.map(|i| *out.get(&Variable::public(i)).unwrap())
.cloned()
.collect()
}
pub fn insert(&mut self, var: Variable, val: T) -> Option<T> {
self.0.insert(var, val)
}
pub fn format_outputs(&self) -> String {
self.0
.iter()
@ -63,13 +67,12 @@ impl<T: Field> Witness<T> {
.deserialize::<(String, String)>()
.map(|r| {
r.map(|(variable, value)| {
let variable =
FlatVariable::try_from_human_readable(&variable).map_err(|why| {
io::Error::new(
io::ErrorKind::Other,
format!("Invalid variable in witness: {}", why),
)
})?;
let variable = Variable::try_from_human_readable(&variable).map_err(|why| {
io::Error::new(
io::ErrorKind::Other,
format!("Invalid variable in witness: {}", why),
)
})?;
let value = T::try_from_dec_str(&value).map_err(|_| {
io::Error::new(
io::ErrorKind::Other,
@ -83,7 +86,7 @@ impl<T: Field> Witness<T> {
e => io::Error::new(io::ErrorKind::Other, format!("{:?}", e)),
})?
})
.collect::<io::Result<BTreeMap<FlatVariable, T>>>()?;
.collect::<io::Result<BTreeMap<Variable, T>>>()?;
Ok(Witness(map))
}
@ -116,9 +119,9 @@ mod tests {
fn serialize_deserialize() {
let w = Witness(
vec![
(FlatVariable::new(42), Bn128Field::from(42)),
(FlatVariable::public(8), Bn128Field::from(8)),
(FlatVariable::one(), Bn128Field::from(1)),
(Variable::new(42), Bn128Field::from(42)),
(Variable::public(8), Bn128Field::from(8)),
(Variable::one(), Bn128Field::from(1)),
]
.into_iter()
.collect(),

10
zokrates_ast/src/lib.rs Normal file
View file

@ -0,0 +1,10 @@
#![feature(box_patterns, box_syntax)]
pub mod common;
pub mod flat;
pub mod ir;
pub mod typed;
pub mod untyped;
pub mod zir;
pub use common::Solver;

View file

@ -1,4 +1,4 @@
use crate::typed_absy::types::{ConcreteSignature, ConcreteType};
use crate::typed::types::{ConcreteSignature, ConcreteType};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
@ -14,7 +14,7 @@ pub type AbiOutput = ConcreteType;
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct Abi {
pub inputs: Vec<AbiInput>,
pub outputs: Vec<AbiOutput>,
pub output: AbiOutput,
}
impl Abi {
@ -22,7 +22,7 @@ impl Abi {
ConcreteSignature {
generics: vec![],
inputs: self.inputs.iter().map(|i| i.ty.clone()).collect(),
outputs: self.outputs.clone(),
output: box self.output.clone(),
}
}
}
@ -30,10 +30,12 @@ impl Abi {
#[cfg(test)]
mod tests {
use super::*;
use crate::typed_absy::types::{
ConcreteArrayType, ConcreteFunctionKey, ConcreteStructMember, ConcreteStructType, UBitwidth,
use crate::typed::types::{
ConcreteArrayType, ConcreteFunctionKey, ConcreteStructMember, ConcreteStructType,
GTupleType, UBitwidth,
};
use crate::typed_absy::{
use crate::typed::DeclarationType;
use crate::typed::{
parameter::DeclarationParameter, variable::DeclarationVariable, ConcreteTupleType,
ConcreteType, TypedFunction, TypedFunctionSymbol, TypedFunctionSymbolDeclaration,
TypedModule, TypedProgram,
@ -48,18 +50,18 @@ mod tests {
TypedFunctionSymbol::Here(TypedFunction {
arguments: vec![
DeclarationParameter {
id: DeclarationVariable::field_element("a"),
id: DeclarationVariable::new("a", DeclarationType::FieldElement, true),
private: true,
},
DeclarationParameter {
id: DeclarationVariable::boolean("b"),
id: DeclarationVariable::new("b", DeclarationType::Boolean, false),
private: false,
},
],
statements: vec![],
signature: ConcreteSignature::new()
.inputs(vec![ConcreteType::FieldElement, ConcreteType::Boolean])
.outputs(vec![ConcreteType::FieldElement])
.output(ConcreteType::FieldElement)
.into(),
}),
)
@ -87,7 +89,7 @@ mod tests {
ty: ConcreteType::Boolean,
},
],
outputs: vec![ConcreteType::FieldElement],
output: ConcreteType::FieldElement,
};
assert_eq!(expected_abi, abi);
@ -97,11 +99,14 @@ mod tests {
fn serialize_empty() {
let abi: Abi = Abi {
inputs: vec![],
outputs: vec![],
output: ConcreteType::Tuple(GTupleType::new(vec![])),
};
let json = serde_json::to_string(&abi).unwrap();
assert_eq!(&json, r#"{"inputs":[],"outputs":[]}"#);
assert_eq!(
&json,
r#"{"inputs":[],"output":{"type":"tuple","components":{"elements":[]}}}"#
);
let de_abi: Abi = serde_json::from_str(json.as_ref()).unwrap();
assert_eq!(de_abi, abi);
}
@ -113,7 +118,7 @@ mod tests {
let abi: Abi = Abi {
inputs: vec![],
outputs: vec![ConcreteType::Int],
output: ConcreteType::Int,
};
let _ = serde_json::to_string_pretty(&abi).unwrap();
@ -134,7 +139,7 @@ mod tests {
ty: ConcreteType::FieldElement,
},
],
outputs: vec![ConcreteType::FieldElement],
output: ConcreteType::FieldElement,
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -153,11 +158,9 @@ mod tests {
"type": "field"
}
],
"outputs": [
{
"type": "field"
}
]
"output": {
"type": "field"
}
}"#
);
@ -185,7 +188,7 @@ mod tests {
ty: ConcreteType::Uint(UBitwidth::B32),
},
],
outputs: vec![],
output: ConcreteType::Tuple(GTupleType::new(vec![])),
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -209,7 +212,12 @@ mod tests {
"type": "u32"
}
],
"outputs": []
"output": {
"type": "tuple",
"components": {
"elements": []
}
}
}"#
);
@ -236,7 +244,7 @@ mod tests {
)],
)),
}],
outputs: vec![ConcreteType::Struct(ConcreteStructType::new(
output: ConcreteType::Struct(ConcreteStructType::new(
"".into(),
"Foo".into(),
vec![],
@ -244,7 +252,7 @@ mod tests {
ConcreteStructMember::new(String::from("a"), ConcreteType::FieldElement),
ConcreteStructMember::new(String::from("b"), ConcreteType::Boolean),
],
))],
)),
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -274,25 +282,23 @@ mod tests {
}
}
],
"outputs": [
{
"type": "struct",
"components": {
"name": "Foo",
"generics": [],
"members": [
{
"name": "a",
"type": "field"
},
{
"name": "b",
"type": "bool"
}
]
}
"output": {
"type": "struct",
"components": {
"name": "Foo",
"generics": [],
"members": [
{
"name": "a",
"type": "field"
},
{
"name": "b",
"type": "bool"
}
]
}
]
}
}"#
);
@ -330,7 +336,7 @@ mod tests {
)],
)),
}],
outputs: vec![],
output: ConcreteType::Tuple(GTupleType::new(vec![])),
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -368,7 +374,12 @@ mod tests {
}
}
],
"outputs": []
"output": {
"type": "tuple",
"components": {
"elements": []
}
}
}"#
);
@ -398,7 +409,7 @@ mod tests {
2u32,
)),
}],
outputs: vec![ConcreteType::Boolean],
output: ConcreteType::Boolean,
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -430,11 +441,9 @@ mod tests {
}
}
],
"outputs": [
{
"type": "bool"
}
]
"output": {
"type": "bool"
}
}"#
);
@ -453,7 +462,7 @@ mod tests {
2u32,
)),
}],
outputs: vec![ConcreteType::FieldElement],
output: ConcreteType::FieldElement,
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -475,11 +484,9 @@ mod tests {
}
}
],
"outputs": [
{
"type": "field"
}
]
"output": {
"type": "field"
}
}"#
);
@ -498,9 +505,7 @@ mod tests {
ConcreteType::Boolean,
])),
}],
outputs: vec![ConcreteType::Tuple(ConcreteTupleType::new(vec![
ConcreteType::FieldElement,
]))],
output: ConcreteType::Tuple(ConcreteTupleType::new(vec![ConcreteType::FieldElement])),
};
let json = serde_json::to_string_pretty(&abi).unwrap();
@ -524,18 +529,16 @@ mod tests {
}
}
],
"outputs": [
{
"type": "tuple",
"components": {
"elements": [
{
"type": "field"
}
]
}
"output": {
"type": "tuple",
"components": {
"elements": [
{
"type": "field"
}
]
}
]
}
}"#
);

View file

@ -1,7 +1,7 @@
// Generic walk through a typed AST. Not mutating in place
use crate::typed_absy::types::*;
use crate::typed_absy::*;
use crate::typed::types::*;
use crate::typed::*;
use zokrates_field::Field;
pub trait Fold<'ast, T: Field>: Sized {
@ -142,6 +142,7 @@ pub trait Folder<'ast, T: Field>: Sized {
Variable {
id: self.fold_name(v.id),
_type: self.fold_type(v._type),
is_mutable: v.is_mutable,
}
}
@ -152,6 +153,7 @@ pub trait Folder<'ast, T: Field>: Sized {
DeclarationVariable {
id: self.fold_name(v.id),
_type: self.fold_declaration_type(v._type),
is_mutable: v.is_mutable,
}
}
@ -166,14 +168,10 @@ pub trait Folder<'ast, T: Field>: Sized {
}
}
fn fold_types(&mut self, tys: Types<'ast, T>) -> Types<'ast, T> {
fold_types(self, tys)
}
fn fold_array_type(&mut self, t: ArrayType<'ast, T>) -> ArrayType<'ast, T> {
ArrayType {
ty: box self.fold_type(*t.ty),
size: self.fold_uint_expression(t.size),
size: box self.fold_uint_expression(*t.size),
}
}
@ -208,6 +206,7 @@ pub trait Folder<'ast, T: Field>: Sized {
match t {
Array(array_type) => Array(self.fold_declaration_array_type(array_type)),
Struct(struct_type) => Struct(self.fold_declaration_struct_type(struct_type)),
Tuple(tuple_type) => Tuple(self.fold_declaration_tuple_type(tuple_type)),
t => t,
}
}
@ -218,7 +217,7 @@ pub trait Folder<'ast, T: Field>: Sized {
) -> DeclarationArrayType<'ast, T> {
DeclarationArrayType {
ty: box self.fold_declaration_type(*t.ty),
size: self.fold_declaration_constant(t.size),
size: box self.fold_declaration_constant(*t.size),
}
}
@ -265,6 +264,10 @@ pub trait Folder<'ast, T: Field>: Sized {
fold_statement(self, s)
}
fn fold_embed_call(&mut self, e: EmbedCall<'ast, T>) -> EmbedCall<'ast, T> {
fold_embed_call(self, e)
}
fn fold_expression_or_spread(
&mut self,
e: TypedExpressionOrSpread<'ast, T>,
@ -300,15 +303,7 @@ pub trait Folder<'ast, T: Field>: Sized {
}
fn fold_expression(&mut self, e: TypedExpression<'ast, T>) -> TypedExpression<'ast, T> {
match e {
TypedExpression::FieldElement(e) => self.fold_field_expression(e).into(),
TypedExpression::Boolean(e) => self.fold_boolean_expression(e).into(),
TypedExpression::Uint(e) => self.fold_uint_expression(e).into(),
TypedExpression::Array(e) => self.fold_array_expression(e).into(),
TypedExpression::Tuple(e) => self.fold_tuple_expression(e).into(),
TypedExpression::Struct(e) => self.fold_struct_expression(e).into(),
TypedExpression::Int(e) => self.fold_int_expression(e).into(),
}
fold_expression(self, e)
}
fn fold_block_expression<E: Fold<'ast, T>>(
@ -394,21 +389,6 @@ pub trait Folder<'ast, T: Field>: Sized {
fold_tuple_expression(self, e)
}
fn fold_expression_list(
&mut self,
es: TypedExpressionList<'ast, T>,
) -> TypedExpressionList<'ast, T> {
fold_expression_list(self, es)
}
fn fold_expression_list_inner(
&mut self,
tys: Types<'ast, T>,
es: TypedExpressionListInner<'ast, T>,
) -> TypedExpressionListInner<'ast, T> {
fold_expression_list_inner(self, tys, es)
}
fn fold_int_expression(&mut self, e: IntExpression<'ast, T>) -> IntExpression<'ast, T> {
fold_int_expression(self, e)
}
@ -419,12 +399,14 @@ pub trait Folder<'ast, T: Field>: Sized {
) -> FieldElementExpression<'ast, T> {
fold_field_expression(self, e)
}
fn fold_boolean_expression(
&mut self,
e: BooleanExpression<'ast, T>,
) -> BooleanExpression<'ast, T> {
fold_boolean_expression(self, e)
}
fn fold_uint_expression(&mut self, e: UExpression<'ast, T>) -> UExpression<'ast, T> {
fold_uint_expression(self, e)
}
@ -514,16 +496,10 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
s: TypedStatement<'ast, T>,
) -> Vec<TypedStatement<'ast, T>> {
let res = match s {
TypedStatement::Return(expressions) => TypedStatement::Return(
expressions
.into_iter()
.map(|e| f.fold_expression(e))
.collect(),
),
TypedStatement::Return(e) => TypedStatement::Return(f.fold_expression(e)),
TypedStatement::Definition(a, e) => {
TypedStatement::Definition(f.fold_assignee(a), f.fold_expression(e))
}
TypedStatement::Declaration(v) => TypedStatement::Declaration(f.fold_variable(v)),
TypedStatement::Assertion(e, error) => {
TypedStatement::Assertion(f.fold_boolean_expression(e), error)
}
@ -536,15 +512,49 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
.flat_map(|s| f.fold_statement(s))
.collect(),
),
TypedStatement::MultipleDefinition(assignees, elist) => TypedStatement::MultipleDefinition(
assignees.into_iter().map(|a| f.fold_assignee(a)).collect(),
f.fold_expression_list(elist),
),
TypedStatement::Log(s, e) => {
TypedStatement::Log(s, e.into_iter().map(|e| f.fold_expression(e)).collect())
}
TypedStatement::EmbedCallDefinition(assignee, embed_call) => {
TypedStatement::EmbedCallDefinition(
f.fold_assignee(assignee),
f.fold_embed_call(embed_call),
)
}
s => s,
};
vec![res]
}
pub fn fold_embed_call<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
e: EmbedCall<'ast, T>,
) -> EmbedCall<'ast, T> {
EmbedCall {
arguments: e
.arguments
.into_iter()
.map(|s| f.fold_expression(s))
.collect(),
..e
}
}
pub fn fold_expression<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
e: TypedExpression<'ast, T>,
) -> TypedExpression<'ast, T> {
match e {
TypedExpression::FieldElement(e) => f.fold_field_expression(e).into(),
TypedExpression::Boolean(e) => f.fold_boolean_expression(e).into(),
TypedExpression::Uint(e) => f.fold_uint_expression(e).into(),
TypedExpression::Array(e) => f.fold_array_expression(e).into(),
TypedExpression::Tuple(e) => f.fold_tuple_expression(e).into(),
TypedExpression::Struct(e) => f.fold_struct_expression(e).into(),
TypedExpression::Int(e) => f.fold_int_expression(e).into(),
}
}
pub fn fold_array_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
ty: &ArrayType<'ast, T>,
@ -1133,11 +1143,7 @@ fn fold_signature<'ast, T: Field, F: Folder<'ast, T>>(
.into_iter()
.map(|o| f.fold_declaration_type(o))
.collect(),
outputs: s
.outputs
.into_iter()
.map(|o| f.fold_declaration_type(o))
.collect(),
output: box f.fold_declaration_type(*s.output),
}
}
@ -1166,54 +1172,6 @@ pub fn fold_array_expression<'ast, T: Field, F: Folder<'ast, T>>(
}
}
pub fn fold_expression_list<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
es: TypedExpressionList<'ast, T>,
) -> TypedExpressionList<'ast, T> {
let types = f.fold_types(es.types);
TypedExpressionList {
inner: f.fold_expression_list_inner(types.clone(), es.inner),
types,
}
}
pub fn fold_types<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
tys: Types<'ast, T>,
) -> Types<'ast, T> {
Types {
inner: tys.inner.into_iter().map(|t| f.fold_type(t)).collect(),
}
}
pub fn fold_expression_list_inner<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
tys: Types<'ast, T>,
es: TypedExpressionListInner<'ast, T>,
) -> TypedExpressionListInner<'ast, T> {
match es {
TypedExpressionListInner::FunctionCall(function_call) => {
match f.fold_function_call_expression(&tys, function_call) {
FunctionCallOrExpression::FunctionCall(function_call) => {
TypedExpressionListInner::FunctionCall(function_call)
}
FunctionCallOrExpression::Expression(u) => u,
}
}
TypedExpressionListInner::EmbedCall(embed, generics, arguments) => {
TypedExpressionListInner::EmbedCall(
embed,
generics,
arguments
.into_iter()
.map(|a| f.fold_expression(a))
.collect(),
)
}
}
}
pub fn fold_struct_expression<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
e: StructExpression<'ast, T>,

View file

@ -1,4 +1,4 @@
use crate::typed_absy::CanonicalConstantIdentifier;
use crate::typed::CanonicalConstantIdentifier;
use std::convert::TryInto;
use std::fmt;

View file

@ -1,10 +1,10 @@
use crate::typed_absy::types::{
use crate::typed::types::{
ArrayType, DeclarationArrayType, DeclarationConstant, DeclarationStructMember,
DeclarationStructType, DeclarationTupleType, DeclarationType, GArrayType, GStructType,
GTupleType, GType, GenericIdentifier, StructType, TupleType, Type,
};
use crate::typed_absy::UBitwidth;
use crate::typed_absy::{
use crate::typed::UBitwidth;
use crate::typed::{
ArrayExpression, ArrayExpressionInner, BooleanExpression, Conditional, ConditionalExpression,
Expr, FieldElementExpression, Select, SelectExpression, StructExpression,
StructExpressionInner, TupleExpression, TupleExpressionInner, Typed, TypedExpression,
@ -86,7 +86,7 @@ impl<'ast, T: Clone> IntegerInference for ArrayType<'ast, T> {
Ok(DeclarationArrayType::new(
self.ty
.get_common_pattern(*other.ty)
.map_err(|(t, u)| (ArrayType::new(t, s0), ArrayType::new(u, s1)))?,
.map_err(|(t, u)| (ArrayType::new(t, *s0), ArrayType::new(u, *s1)))?,
DeclarationConstant::Generic(GenericIdentifier::with_name("DUMMY")), // sizes are not checked at this stage, therefore we insert a dummy generic variable which will be equal to all possible sizes
))
}
@ -659,28 +659,28 @@ impl<'ast, T: Field> ArrayExpression<'ast, T> {
let inner_ty = res.0[0].get_type().0;
Ok(ArrayExpressionInner::Value(res).annotate(inner_ty, array_ty.size))
Ok(ArrayExpressionInner::Value(res).annotate(inner_ty, *array_ty.size))
}
ArrayExpressionInner::Repeat(box e, box count) => {
match &*target_array_ty.ty {
GType::Int => Ok(ArrayExpressionInner::Repeat(box e, box count)
.annotate(Type::Int, array_ty.size)),
.annotate(Type::Int, *array_ty.size)),
// try to align the repeated element to the target type
t => TypedExpression::align_to_type(e, t)
.map(|e| {
let ty = e.get_type().clone();
ArrayExpressionInner::Repeat(box e, box count)
.annotate(ty, array_ty.size)
.annotate(ty, *array_ty.size)
})
.map_err(|(e, _)| e),
}
}
a => {
if *target_array_ty.ty == *array_ty.ty {
Ok(a.annotate(*array_ty.ty, array_ty.size))
Ok(a.annotate(*array_ty.ty, *array_ty.size))
} else {
Err(a.annotate(*array_ty.ty, array_ty.size).into())
Err(a.annotate(*array_ty.ty, *array_ty.size).into())
}
}
}
@ -749,7 +749,7 @@ impl<'ast, T: Field> TupleExpression<'ast, T> {
TupleExpressionInner::Value(inline_tuple) => inline_tuple
.into_iter()
.zip(target_tuple_ty.elements.iter())
.map(|(value, target_ty)| TypedExpression::align_to_type(value, &*target_ty))
.map(|(value, target_ty)| TypedExpression::align_to_type(value, target_ty))
.collect::<Result<Vec<_>, _>>()
.map(|v| {
let ty = TupleType::new(v.iter().map(|e| e.get_type()).collect());
@ -791,7 +791,7 @@ impl<'ast, T> From<BigUint> for IntExpression<'ast, T> {
#[cfg(test)]
mod tests {
use super::*;
use crate::typed_absy::ConditionalKind;
use crate::typed::ConditionalKind;
use zokrates_field::Bn128Field;
#[test]

View file

@ -22,31 +22,32 @@ pub use self::types::{
CanonicalConstantIdentifier, ConcreteFunctionKey, ConcreteSignature, ConcreteTupleType,
ConcreteType, ConstantIdentifier, DeclarationArrayType, DeclarationConstant,
DeclarationFunctionKey, DeclarationSignature, DeclarationStructType, DeclarationType,
GArrayType, GStructType, GType, GenericIdentifier, IntoTypes, Signature, StructType, TupleType,
Type, Types, UBitwidth,
GArrayType, GStructType, GType, GenericIdentifier, Signature, StructType, TupleType, Type,
UBitwidth,
};
use crate::parser::Position;
use crate::typed_absy::types::ConcreteGenericsAssignment;
use crate::typed::types::{ConcreteGenericsAssignment, IntoType};
use crate::untyped::Position;
pub use self::variable::{ConcreteVariable, DeclarationVariable, GVariable, Variable};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
pub use crate::typed_absy::integer::IntExpression;
pub use crate::typed_absy::uint::{bitwidth, UExpression, UExpressionInner, UMetadata};
pub use crate::typed::integer::IntExpression;
pub use crate::typed::uint::{bitwidth, UExpression, UExpressionInner, UMetadata};
use crate::embed::FlatEmbed;
use crate::common::{FlatEmbed, FormatString};
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::fmt;
pub use crate::typed_absy::types::{ArrayType, FunctionKey, MemberId};
pub use crate::typed::types::{ArrayType, FunctionKey, MemberId};
use zokrates_field::Field;
pub use self::folder::Folder;
use crate::typed_absy::abi::{Abi, AbiInput};
use crate::typed::abi::{Abi, AbiInput};
use std::ops::{Add, Div, Mul, Sub};
pub use self::identifier::Identifier;
@ -65,7 +66,7 @@ pub type TypedModules<'ast, T> = BTreeMap<OwnedTypedModuleId, TypedModule<'ast,
pub type TypedFunctionSymbols<'ast, T> =
BTreeMap<DeclarationFunctionKey<'ast, T>, TypedFunctionSymbol<'ast, T>>;
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypedConstantSymbol<'ast, T> {
Here(TypedConstant<'ast, T>),
There(CanonicalConstantIdentifier<'ast>),
@ -79,18 +80,12 @@ pub type TypedConstantSymbols<'ast, T> = Vec<(
)>;
/// A typed program as a collection of modules, one of them being the main
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct TypedProgram<'ast, T> {
pub modules: TypedModules<'ast, T>,
pub main: OwnedTypedModuleId,
}
impl<'ast, T> TypedProgram<'ast, T> {
pub fn main_function(&self) -> TypedFunction<'ast, T> {
unimplemented!()
}
}
impl<'ast, T: Field> TypedProgram<'ast, T> {
pub fn abi(&self) -> Abi {
let main = &self.modules[&self.main]
@ -109,7 +104,7 @@ impl<'ast, T: Field> TypedProgram<'ast, T> {
.iter()
.map(|p| {
types::ConcreteType::try_from(
crate::typed_absy::types::try_from_g_type::<
crate::typed::types::try_from_g_type::<
DeclarationConstant<'ast, T>,
UExpression<'ast, T>,
>(p.id._type.clone())
@ -123,21 +118,13 @@ impl<'ast, T: Field> TypedProgram<'ast, T> {
.unwrap()
})
.collect(),
outputs: main
.signature
.outputs
.iter()
.map(|ty| {
types::ConcreteType::try_from(
crate::typed_absy::types::try_from_g_type::<
DeclarationConstant<'ast, T>,
UExpression<'ast, T>,
>(ty.clone())
.unwrap(),
)
.unwrap()
})
.collect(),
output: types::ConcreteType::try_from(
types::try_from_g_type::<DeclarationConstant<'ast, T>, UExpression<'ast, T>>(
*main.signature.output.clone(),
)
.unwrap(),
)
.unwrap(),
}
}
}
@ -164,7 +151,7 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedProgram<'ast, T> {
}
}
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct TypedFunctionSymbolDeclaration<'ast, T> {
pub key: DeclarationFunctionKey<'ast, T>,
pub symbol: TypedFunctionSymbol<'ast, T>,
@ -176,7 +163,7 @@ impl<'ast, T> TypedFunctionSymbolDeclaration<'ast, T> {
}
}
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct TypedConstantSymbolDeclaration<'ast, T> {
pub id: CanonicalConstantIdentifier<'ast>,
pub symbol: TypedConstantSymbol<'ast, T>,
@ -192,7 +179,7 @@ impl<'ast, T> TypedConstantSymbolDeclaration<'ast, T> {
}
#[allow(clippy::large_enum_variant)]
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum TypedSymbolDeclaration<'ast, T> {
Function(TypedFunctionSymbolDeclaration<'ast, T>),
Constant(TypedConstantSymbolDeclaration<'ast, T>),
@ -222,7 +209,7 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedSymbolDeclaration<'ast, T> {
pub type TypedSymbolDeclarations<'ast, T> = Vec<TypedSymbolDeclaration<'ast, T>>;
/// A typed module as a collection of functions. Types have been resolved during semantic checking.
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct TypedModule<'ast, T> {
pub symbols: TypedSymbolDeclarations<'ast, T>,
}
@ -245,7 +232,7 @@ impl<'ast, T> TypedModule<'ast, T> {
}
}
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TypedFunctionSymbol<'ast, T> {
Here(TypedFunction<'ast, T>),
There(DeclarationFunctionKey<'ast, T>),
@ -276,12 +263,12 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedConstantSymbolDeclaration<'ast
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.symbol {
TypedConstantSymbol::Here(ref tc) => {
write!(f, "const {} {} = {}", tc.ty, self.id, tc.expression)
write!(f, "const {} {} = {};", tc.ty, self.id, tc.expression)
}
TypedConstantSymbol::There(ref imported_id) => {
write!(
f,
"from \"{}\" import {} as {}",
"from \"{}\" import {} as {};",
imported_id.module.display(),
imported_id.id,
self.id
@ -297,7 +284,7 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedFunctionSymbolDeclaration<'ast
TypedFunctionSymbol::Here(ref function) => write!(f, "def {}{}", self.key.id, function),
TypedFunctionSymbol::There(ref fun_key) => write!(
f,
"from \"{}\" import {} as {} // with signature {}",
"from \"{}\" import {} as {}; // with signature {}",
fun_key.module.display(),
fun_key.id,
self.key.id,
@ -306,7 +293,7 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedFunctionSymbolDeclaration<'ast
TypedFunctionSymbol::Flat(ref flat_fun) => {
write!(
f,
"def {}{}:\n\t// hidden",
"def {}{} {{\n\t// hidden\n}}",
self.key.id,
flat_fun.typed_signature::<T>()
)
@ -328,7 +315,7 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedModule<'ast, T> {
}
/// A typed function
#[derive(Clone, PartialEq, Debug, Hash)]
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypedFunction<'ast, T> {
/// Arguments of the function
pub arguments: Vec<DeclarationParameter<'ast, T>>,
@ -362,23 +349,7 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedFunction<'ast, T> {
.join(", "),
)?;
write!(
f,
"{}:",
match self.signature.outputs.len() {
0 => "".into(),
1 => format!(" -> {}", self.signature.outputs[0]),
_ => format!(
" -> ({})",
self.signature
.outputs
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(", ")
),
}
)?;
write!(f, " -> {} {{", self.signature.output)?;
writeln!(f)?;
@ -397,11 +368,13 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedFunction<'ast, T> {
};
}
writeln!(f, "}}")?;
Ok(())
}
}
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TypedConstant<'ast, T> {
pub expression: TypedExpression<'ast, T>,
pub ty: DeclarationType<'ast, T>,
@ -626,13 +599,59 @@ impl fmt::Display for RuntimeError {
}
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct EmbedCall<'ast, T> {
pub embed: FlatEmbed,
pub generics: Vec<u32>,
pub arguments: Vec<TypedExpression<'ast, T>>,
}
impl<'ast, T> EmbedCall<'ast, T> {
pub fn new(
embed: FlatEmbed,
generics: Vec<u32>,
arguments: Vec<TypedExpression<'ast, T>>,
) -> Self {
Self {
embed,
generics,
arguments,
}
}
}
impl<'ast, T: fmt::Display> fmt::Display for EmbedCall<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.embed.id())?;
if !self.generics.is_empty() {
write!(
f,
"::<{}>",
self.generics
.iter()
.map(|g| g.to_string())
.collect::<Vec<_>>()
.join(", ")
)?;
}
write!(f, "(")?;
let len = self.arguments.len();
for (i, arg) in self.arguments.iter().enumerate() {
write!(f, "{}", arg)?;
if i < len - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
}
}
/// A statement in a `TypedFunction`
#[allow(clippy::large_enum_variant)]
#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)]
pub enum TypedStatement<'ast, T> {
Return(Vec<TypedExpression<'ast, T>>),
Return(TypedExpression<'ast, T>),
Definition(TypedAssignee<'ast, T>, TypedExpression<'ast, T>),
Declaration(Variable<'ast, T>),
Assertion(BooleanExpression<'ast, T>, RuntimeError),
For(
Variable<'ast, T>,
@ -640,7 +659,8 @@ pub enum TypedStatement<'ast, T> {
UExpression<'ast, T>,
Vec<TypedStatement<'ast, T>>,
),
MultipleDefinition(Vec<TypedAssignee<'ast, T>>, TypedExpressionList<'ast, T>),
Log(FormatString, Vec<TypedExpression<'ast, T>>),
EmbedCallDefinition(TypedAssignee<'ast, T>, EmbedCall<'ast, T>),
// Aux
PushCallLog(
DeclarationFunctionKey<'ast, T>,
@ -654,12 +674,12 @@ impl<'ast, T: fmt::Display> TypedStatement<'ast, T> {
match self {
TypedStatement::For(variable, from, to, statements) => {
write!(f, "{}", "\t".repeat(depth))?;
writeln!(f, "for {} in {}..{} do", variable, from, to)?;
writeln!(f, "for {} in {}..{} {{", variable, from, to)?;
for s in statements {
s.fmt_indented(f, depth + 1)?;
writeln!(f)?;
}
write!(f, "{}endfor", "\t".repeat(depth))
write!(f, "{}}}", "\t".repeat(depth))
}
s => write!(f, "{}{}", "\t".repeat(depth), s),
}
@ -669,44 +689,40 @@ impl<'ast, T: fmt::Display> TypedStatement<'ast, T> {
impl<'ast, T: fmt::Display> fmt::Display for TypedStatement<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
TypedStatement::Return(ref exprs) => {
write!(f, "return ")?;
for (i, expr) in exprs.iter().enumerate() {
write!(f, "{}", expr)?;
if i < exprs.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "")
TypedStatement::Return(ref e) => {
write!(f, "return {};", e)
}
TypedStatement::Declaration(ref var) => write!(f, "{}", var),
TypedStatement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
TypedStatement::Definition(ref lhs, ref rhs) => write!(f, "{} = {};", lhs, rhs),
TypedStatement::Assertion(ref e, ref error) => {
write!(f, "assert({}", e)?;
match error {
RuntimeError::SourceAssertion(metadata) => match &metadata.message {
Some(m) => write!(f, ", \"{}\")", m),
None => write!(f, ")"),
Some(m) => write!(f, ", \"{}\");", m),
None => write!(f, ");"),
},
error => write!(f, ") // {}", error),
error => write!(f, "); // {}", error),
}
}
TypedStatement::For(ref var, ref start, ref stop, ref list) => {
writeln!(f, "for {} in {}..{} do", var, start, stop)?;
writeln!(f, "for {} in {}..{} {{", var, start, stop)?;
for l in list {
writeln!(f, "\t\t{}", l)?;
}
write!(f, "\tendfor")
write!(f, "\t}}")
}
TypedStatement::MultipleDefinition(ref ids, ref rhs) => {
for (i, id) in ids.iter().enumerate() {
write!(f, "{}", id)?;
if i < ids.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, " = {}", rhs)
TypedStatement::EmbedCallDefinition(ref lhs, ref rhs) => {
write!(f, "{} = {};", lhs, rhs)
}
TypedStatement::Log(ref l, ref expressions) => write!(
f,
"log({}, {})",
l,
expressions
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
),
TypedStatement::PushCallLog(ref key, ref generics) => write!(
f,
"// PUSH CALL TO {}/{}::<{}>",
@ -876,41 +892,6 @@ impl<'ast, T: Clone> Typed<'ast, T> for BooleanExpression<'ast, T> {
}
}
pub trait MultiTyped<'ast, T> {
fn get_types(&self) -> &Vec<Type<'ast, T>>;
}
#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)]
pub struct TypedExpressionList<'ast, T> {
pub inner: TypedExpressionListInner<'ast, T>,
pub types: Types<'ast, T>,
}
impl<'ast, T: fmt::Display> fmt::Display for TypedExpressionList<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)]
pub enum TypedExpressionListInner<'ast, T> {
FunctionCall(FunctionCallExpression<'ast, T, TypedExpressionList<'ast, T>>),
EmbedCall(FlatEmbed, Vec<u32>, Vec<TypedExpression<'ast, T>>),
}
impl<'ast, T> MultiTyped<'ast, T> for TypedExpressionList<'ast, T> {
fn get_types(&self) -> &Vec<Type<'ast, T>> {
&self.types.inner
}
}
impl<'ast, T> TypedExpressionListInner<'ast, T> {
pub fn annotate(self, types: Types<'ast, T>) -> TypedExpressionList<'ast, T> {
TypedExpressionList { inner: self, types }
}
}
#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)]
pub struct EqExpression<E> {
pub left: Box<E>,
@ -1049,11 +1030,13 @@ impl<'ast, T, E> ConditionalExpression<'ast, T, E> {
impl<'ast, T: fmt::Display, E: fmt::Display> fmt::Display for ConditionalExpression<'ast, T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ConditionalKind::IfElse => write!(
f,
"if {} then {} else {} fi",
self.condition, self.consequence, self.alternative
),
ConditionalKind::IfElse => {
write!(
f,
"if {} {} else {}",
self.condition, self.consequence, self.alternative
)
}
ConditionalKind::Ternary => write!(
f,
"{} ? {} : {}",
@ -1296,7 +1279,7 @@ impl<'ast, T: Field> ArrayValue<'ast, T> {
.map(|i| {
Some(U::select(
a.clone()
.annotate(*array_ty.ty.clone(), array_ty.size.clone()),
.annotate(*array_ty.ty.clone(), *array_ty.size.clone()),
i as u32,
))
})
@ -1372,7 +1355,7 @@ impl<'ast, T: Clone> ArrayExpression<'ast, T> {
}
pub fn size(&self) -> UExpression<'ast, T> {
self.ty.size.clone()
*self.ty.size.clone()
}
}
@ -1559,15 +1542,6 @@ impl<'ast, T> From<TypedExpression<'ast, T>> for TupleExpression<'ast, T> {
}
}
// `TypedExpressionList` can technically not be constructed from `TypedExpression`
// However implementing `From<TypedExpression>` is required for `TypedExpressionList` to be `Expr`, which makes generic treatment of function calls possible
// This could maybe be avoided by splitting the `Expr` trait into many, but I did not find a way
impl<'ast, T> From<TypedExpression<'ast, T>> for TypedExpressionList<'ast, T> {
fn from(_: TypedExpression<'ast, T>) -> TypedExpressionList<'ast, T> {
unreachable!()
}
}
impl<'ast, T> From<TypedConstant<'ast, T>> for FieldElementExpression<'ast, T> {
fn from(tc: TypedConstant<'ast, T>) -> FieldElementExpression<'ast, T> {
tc.expression.into()
@ -1741,38 +1715,6 @@ impl<'ast, T: fmt::Display> fmt::Display for ArrayExpressionInner<'ast, T> {
}
}
impl<'ast, T: fmt::Display> fmt::Display for TypedExpressionListInner<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
TypedExpressionListInner::FunctionCall(ref function_call) => {
write!(f, "{}", function_call)
}
TypedExpressionListInner::EmbedCall(ref embed, ref generics, ref p) => {
write!(f, "{}", embed.id())?;
if !generics.is_empty() {
write!(
f,
"::<{}>",
generics
.iter()
.map(|g| g.to_string())
.collect::<Vec<_>>()
.join(", ")
)?;
}
write!(f, "(")?;
for (i, param) in p.iter().enumerate() {
write!(f, "{}", param)?;
if i < p.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
}
}
}
}
// Variable to TypedExpression conversion
impl<'ast, T: Field> From<Variable<'ast, T>> for TypedExpression<'ast, T> {
@ -1781,7 +1723,7 @@ impl<'ast, T: Field> From<Variable<'ast, T>> for TypedExpression<'ast, T> {
Type::FieldElement => FieldElementExpression::Identifier(v.id).into(),
Type::Boolean => BooleanExpression::Identifier(v.id).into(),
Type::Array(ty) => ArrayExpressionInner::Identifier(v.id)
.annotate(*ty.ty, ty.size)
.annotate(*ty.ty, *ty.size)
.into(),
Type::Struct(ty) => StructExpressionInner::Identifier(v.id).annotate(ty).into(),
Type::Tuple(ty) => TupleExpressionInner::Identifier(v.id).annotate(ty).into(),
@ -1795,7 +1737,7 @@ impl<'ast, T: Field> From<Variable<'ast, T>> for TypedExpression<'ast, T> {
pub trait Expr<'ast, T>: fmt::Display + From<TypedExpression<'ast, T>> {
type Inner;
type Ty: Clone + IntoTypes<'ast, T>;
type Ty: Clone + IntoType<'ast, T>;
fn ty(&self) -> &Self::Ty;
@ -1953,27 +1895,6 @@ impl<'ast, T: Field> Expr<'ast, T> for IntExpression<'ast, T> {
}
}
impl<'ast, T: Field> Expr<'ast, T> for TypedExpressionList<'ast, T> {
type Inner = TypedExpressionListInner<'ast, T>;
type Ty = Types<'ast, T>;
fn ty(&self) -> &Self::Ty {
&self.types
}
fn into_inner(self) -> Self::Inner {
self.inner
}
fn as_inner(&self) -> &Self::Inner {
&self.inner
}
fn as_inner_mut(&mut self) -> &mut Self::Inner {
&mut self.inner
}
}
// Enums types to enable returning e.g a member expression OR another type of expression of this type
pub enum FunctionCallOrExpression<'ast, T, E: Expr<'ast, T>> {
@ -2190,7 +2111,8 @@ impl<'ast, T: Clone> Select<'ast, T> for ArrayExpression<'ast, T> {
_ => unreachable!(),
};
ArrayExpressionInner::Select(SelectExpression::new(array, index.into())).annotate(*ty, size)
ArrayExpressionInner::Select(SelectExpression::new(array, index.into()))
.annotate(*ty, *size)
}
}
@ -2236,7 +2158,7 @@ impl<'ast, T: Clone> Member<'ast, T> for UExpression<'ast, T> {
fn member(s: StructExpression<'ast, T>, id: MemberId) -> Self {
let ty = s.ty().members.iter().find(|member| id == member.id);
let bitwidth = match ty {
Some(crate::typed_absy::types::StructMember {
Some(crate::typed::types::StructMember {
ty: box Type::Uint(bitwidth),
..
}) => *bitwidth,
@ -2250,13 +2172,13 @@ impl<'ast, T: Clone> Member<'ast, T> for ArrayExpression<'ast, T> {
fn member(s: StructExpression<'ast, T>, id: MemberId) -> Self {
let ty = s.ty().members.iter().find(|member| id == member.id);
let (ty, size) = match ty {
Some(crate::typed_absy::types::StructMember {
Some(crate::typed::types::StructMember {
ty: box Type::Array(array_ty),
..
}) => (*array_ty.ty.clone(), array_ty.size.clone()),
_ => unreachable!(),
};
ArrayExpressionInner::Member(MemberExpression::new(s, id)).annotate(ty, size)
ArrayExpressionInner::Member(MemberExpression::new(s, id)).annotate(ty, *size)
}
}
@ -2264,7 +2186,7 @@ impl<'ast, T: Clone> Member<'ast, T> for StructExpression<'ast, T> {
fn member(s: StructExpression<'ast, T>, id: MemberId) -> Self {
let ty = s.ty().members.iter().find(|member| id == member.id);
let struct_ty = match ty {
Some(crate::typed_absy::types::StructMember {
Some(crate::typed::types::StructMember {
ty: box Type::Struct(struct_ty),
..
}) => struct_ty.clone(),
@ -2278,7 +2200,7 @@ impl<'ast, T: Clone> Member<'ast, T> for TupleExpression<'ast, T> {
fn member(s: StructExpression<'ast, T>, id: MemberId) -> Self {
let ty = s.ty().members.iter().find(|member| id == member.id);
let tuple_ty = match ty {
Some(crate::typed_absy::types::StructMember {
Some(crate::typed::types::StructMember {
ty: box Type::Tuple(tuple_ty),
..
}) => tuple_ty.clone(),
@ -2322,7 +2244,7 @@ impl<'ast, T: Clone> Element<'ast, T> for ArrayExpression<'ast, T> {
Type::Array(array_ty) => (*array_ty.ty.clone(), array_ty.size.clone()),
_ => unreachable!(),
};
ArrayExpressionInner::Element(ElementExpression::new(s, id)).annotate(ty, size)
ArrayExpressionInner::Element(ElementExpression::new(s, id)).annotate(ty, *size)
}
}
@ -2388,15 +2310,6 @@ impl<'ast, T: Field> Id<'ast, T> for TupleExpression<'ast, T> {
}
}
// `TypedExpressionList` does not have an Identifier variant
// However implementing `From<TypedExpression>` is required for `TypedExpressionList` to be `Expr`, which makes generic treatment of function calls possible
// This could maybe be avoided by splitting the `Expr` trait into many, but I did not find a way
impl<'ast, T: Field> Id<'ast, T> for TypedExpressionList<'ast, T> {
fn identifier(_: Identifier<'ast>) -> Self::Inner {
unreachable!()
}
}
pub trait FunctionCall<'ast, T>: Expr<'ast, T> {
fn function_call(
key: DeclarationFunctionKey<'ast, T>,
@ -2465,18 +2378,6 @@ impl<'ast, T: Field> FunctionCall<'ast, T> for StructExpression<'ast, T> {
}
}
impl<'ast, T: Field> FunctionCall<'ast, T> for TypedExpressionList<'ast, T> {
fn function_call(
key: DeclarationFunctionKey<'ast, T>,
generics: Vec<Option<UExpression<'ast, T>>>,
arguments: Vec<TypedExpression<'ast, T>>,
) -> Self::Inner {
TypedExpressionListInner::FunctionCall(FunctionCallExpression::new(
key, generics, arguments,
))
}
}
pub trait Block<'ast, T> {
fn block(statements: Vec<TypedStatement<'ast, T>>, value: Self) -> Self;
}
@ -2504,7 +2405,7 @@ impl<'ast, T: Field> Block<'ast, T> for ArrayExpression<'ast, T> {
fn block(statements: Vec<TypedStatement<'ast, T>>, value: Self) -> Self {
let array_ty = value.ty().clone();
ArrayExpressionInner::Block(BlockExpression::new(statements, value))
.annotate(*array_ty.ty, array_ty.size)
.annotate(*array_ty.ty, *array_ty.size)
}
}
@ -2625,7 +2526,7 @@ impl<'ast, T: Field> Constant for ArrayExpression<'ast, T> {
.collect::<Vec<_>>()
.into(),
)
.annotate(*array_ty.ty, array_ty.size),
.annotate(*array_ty.ty, *array_ty.size),
ArrayExpressionInner::Slice(box a, box from, box to) => {
let from = match from.into_inner() {
UExpressionInner::Value(from) => from as usize,
@ -2651,7 +2552,7 @@ impl<'ast, T: Field> Constant for ArrayExpression<'ast, T> {
.collect::<Vec<_>>()
.into(),
)
.annotate(*array_ty.ty, array_ty.size)
.annotate(*array_ty.ty, *array_ty.size)
}
ArrayExpressionInner::Repeat(box e, box count) => {
let count = match count.into_inner() {
@ -2664,7 +2565,7 @@ impl<'ast, T: Field> Constant for ArrayExpression<'ast, T> {
ArrayExpressionInner::Value(
vec![TypedExpressionOrSpread::Expression(e); count].into(),
)
.annotate(*array_ty.ty, array_ty.size)
.annotate(*array_ty.ty, *array_ty.size)
}
_ => unreachable!(),
}

View file

@ -1,5 +1,5 @@
use crate::typed_absy::types::DeclarationConstant;
use crate::typed_absy::GVariable;
use crate::typed::types::DeclarationConstant;
use crate::typed::GVariable;
use std::fmt;
#[derive(Clone, PartialEq, Eq, Hash)]
@ -8,7 +8,6 @@ pub struct GParameter<'ast, S> {
pub private: bool,
}
#[cfg(test)]
impl<'ast, S> From<GVariable<'ast, S>> for GParameter<'ast, S> {
fn from(v: GVariable<'ast, S>) -> Self {
GParameter {

View file

@ -1,7 +1,7 @@
// Generic walk through a typed AST. Not mutating in place
use crate::typed_absy::types::*;
use crate::typed_absy::*;
use crate::typed::types::*;
use crate::typed::*;
use zokrates_field::Field;
pub trait ResultFold<'ast, T: Field>: Sized {
@ -170,6 +170,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
Ok(Variable {
id: self.fold_name(v.id)?,
_type: self.fold_type(v._type)?,
is_mutable: v.is_mutable,
})
}
@ -180,6 +181,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
Ok(DeclarationVariable {
id: self.fold_name(v.id)?,
_type: self.fold_declaration_type(v._type)?,
is_mutable: v.is_mutable,
})
}
@ -194,10 +196,6 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
}
}
fn fold_types(&mut self, tys: Types<'ast, T>) -> Result<Types<'ast, T>, Self::Error> {
fold_types(self, tys)
}
fn fold_conditional_expression<
E: Expr<'ast, T> + PartialEq + Conditional<'ast, T> + ResultFold<'ast, T>,
>(
@ -273,7 +271,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
) -> Result<ArrayType<'ast, T>, Self::Error> {
Ok(ArrayType {
ty: box self.fold_type(*t.ty)?,
size: self.fold_uint_expression(t.size)?,
size: box self.fold_uint_expression(*t.size)?,
})
}
@ -322,6 +320,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
match t {
Array(array_type) => Ok(Array(self.fold_declaration_array_type(array_type)?)),
Struct(struct_type) => Ok(Struct(self.fold_declaration_struct_type(struct_type)?)),
Tuple(tuple_type) => Ok(Tuple(self.fold_declaration_tuple_type(tuple_type)?)),
t => Ok(t),
}
}
@ -332,7 +331,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
) -> Result<DeclarationArrayType<'ast, T>, Self::Error> {
Ok(DeclarationArrayType {
ty: box self.fold_declaration_type(*t.ty)?,
size: self.fold_declaration_constant(t.size)?,
size: box self.fold_declaration_constant(*t.size)?,
})
}
@ -386,6 +385,13 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
fold_statement(self, s)
}
fn fold_embed_call(
&mut self,
e: EmbedCall<'ast, T>,
) -> Result<EmbedCall<'ast, T>, Self::Error> {
fold_embed_call(self, e)
}
fn fold_expression_or_spread(
&mut self,
e: TypedExpressionOrSpread<'ast, T>,
@ -413,15 +419,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
&mut self,
e: TypedExpression<'ast, T>,
) -> Result<TypedExpression<'ast, T>, Self::Error> {
match e {
TypedExpression::FieldElement(e) => Ok(self.fold_field_expression(e)?.into()),
TypedExpression::Boolean(e) => Ok(self.fold_boolean_expression(e)?.into()),
TypedExpression::Uint(e) => Ok(self.fold_uint_expression(e)?.into()),
TypedExpression::Array(e) => Ok(self.fold_array_expression(e)?.into()),
TypedExpression::Struct(e) => Ok(self.fold_struct_expression(e)?.into()),
TypedExpression::Tuple(e) => Ok(self.fold_tuple_expression(e)?.into()),
TypedExpression::Int(e) => Ok(self.fold_int_expression(e)?.into()),
}
fold_expression(self, e)
}
fn fold_array_expression(
@ -445,21 +443,6 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
fold_tuple_expression(self, e)
}
fn fold_expression_list_inner(
&mut self,
tys: &Types<'ast, T>,
es: TypedExpressionListInner<'ast, T>,
) -> Result<TypedExpressionListInner<'ast, T>, Self::Error> {
fold_expression_list_inner(self, tys, es)
}
fn fold_expression_list(
&mut self,
es: TypedExpressionList<'ast, T>,
) -> Result<TypedExpressionList<'ast, T>, Self::Error> {
fold_expression_list(self, es)
}
fn fold_int_expression(
&mut self,
e: IntExpression<'ast, T>,
@ -523,16 +506,10 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
s: TypedStatement<'ast, T>,
) -> Result<Vec<TypedStatement<'ast, T>>, F::Error> {
let res = match s {
TypedStatement::Return(expressions) => TypedStatement::Return(
expressions
.into_iter()
.map(|e| f.fold_expression(e))
.collect::<Result<_, _>>()?,
),
TypedStatement::Return(e) => TypedStatement::Return(f.fold_expression(e)?),
TypedStatement::Definition(a, e) => {
TypedStatement::Definition(f.fold_assignee(a)?, f.fold_expression(e)?)
}
TypedStatement::Declaration(v) => TypedStatement::Declaration(f.fold_variable(v)?),
TypedStatement::Assertion(e, error) => {
TypedStatement::Assertion(f.fold_boolean_expression(e)?, error)
}
@ -548,18 +525,37 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
.flatten()
.collect(),
),
TypedStatement::MultipleDefinition(variables, elist) => TypedStatement::MultipleDefinition(
variables
.into_iter()
.map(|v| f.fold_assignee(v))
.collect::<Result<_, _>>()?,
f.fold_expression_list(elist)?,
TypedStatement::Log(s, e) => TypedStatement::Log(
s,
e.into_iter()
.map(|e| f.fold_expression(e))
.collect::<Result<Vec<_>, _>>()?,
),
TypedStatement::EmbedCallDefinition(assignee, embed_call) => {
TypedStatement::EmbedCallDefinition(
f.fold_assignee(assignee)?,
f.fold_embed_call(embed_call)?,
)
}
s => s,
};
Ok(vec![res])
}
pub fn fold_embed_call<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
e: EmbedCall<'ast, T>,
) -> Result<EmbedCall<'ast, T>, F::Error> {
Ok(EmbedCall {
arguments: e
.arguments
.into_iter()
.map(|s| f.fold_expression(s))
.collect::<Result<Vec<_>, _>>()?,
..e
})
}
pub fn fold_array_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
ty: &ArrayType<'ast, T>,
@ -1201,11 +1197,7 @@ pub fn fold_signature<'ast, T: Field, F: ResultFolder<'ast, T>>(
.into_iter()
.map(|o| f.fold_declaration_type(o))
.collect::<Result<_, _>>()?,
outputs: s
.outputs
.into_iter()
.map(|o| f.fold_declaration_type(o))
.collect::<Result<_, _>>()?,
output: box f.fold_declaration_type(*s.output)?,
})
}
@ -1224,6 +1216,21 @@ pub fn fold_declaration_constant<'ast, T: Field, F: ResultFolder<'ast, T>>(
}
}
pub fn fold_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
e: TypedExpression<'ast, T>,
) -> Result<TypedExpression<'ast, T>, F::Error> {
match e {
TypedExpression::FieldElement(e) => Ok(f.fold_field_expression(e)?.into()),
TypedExpression::Boolean(e) => Ok(f.fold_boolean_expression(e)?.into()),
TypedExpression::Uint(e) => Ok(f.fold_uint_expression(e)?.into()),
TypedExpression::Array(e) => Ok(f.fold_array_expression(e)?.into()),
TypedExpression::Struct(e) => Ok(f.fold_struct_expression(e)?.into()),
TypedExpression::Tuple(e) => Ok(f.fold_tuple_expression(e)?.into()),
TypedExpression::Int(e) => Ok(f.fold_int_expression(e)?.into()),
}
}
pub fn fold_array_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
e: ArrayExpression<'ast, T>,
@ -1236,58 +1243,6 @@ pub fn fold_array_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
})
}
pub fn fold_expression_list<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
es: TypedExpressionList<'ast, T>,
) -> Result<TypedExpressionList<'ast, T>, F::Error> {
let types = f.fold_types(es.types)?;
Ok(TypedExpressionList {
inner: f.fold_expression_list_inner(&types, es.inner)?,
types,
})
}
pub fn fold_types<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
tys: Types<'ast, T>,
) -> Result<Types<'ast, T>, F::Error> {
Ok(Types {
inner: tys
.inner
.into_iter()
.map(|t| f.fold_type(t))
.collect::<Result<_, _>>()?,
})
}
pub fn fold_expression_list_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
tys: &Types<'ast, T>,
es: TypedExpressionListInner<'ast, T>,
) -> Result<TypedExpressionListInner<'ast, T>, F::Error> {
match es {
TypedExpressionListInner::FunctionCall(function_call) => {
match f.fold_function_call_expression(tys, function_call)? {
FunctionCallOrExpression::FunctionCall(function_call) => {
Ok(TypedExpressionListInner::FunctionCall(function_call))
}
FunctionCallOrExpression::Expression(list) => Ok(list),
}
}
TypedExpressionListInner::EmbedCall(embed, generics, arguments) => {
Ok(TypedExpressionListInner::EmbedCall(
embed,
generics,
arguments
.into_iter()
.map(|a| f.fold_expression(a))
.collect::<Result<_, _>>()?,
))
}
}
}
pub fn fold_struct_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
f: &mut F,
e: StructExpression<'ast, T>,

View file

@ -1,69 +1,44 @@
use crate::typed_absy::{
use crate::typed::{
CoreIdentifier, Identifier, OwnedTypedModuleId, TypedExpression, UExpression, UExpressionInner,
};
use crate::typed_absy::{TryFrom, TryInto};
use crate::typed::{TryFrom, TryInto};
use serde::{de::Error, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
pub trait IntoTypes<'ast, T> {
fn into_types(self) -> Types<'ast, T>;
pub trait IntoType<'ast, T> {
fn into_type(self) -> Type<'ast, T>;
}
impl<'ast, T> IntoTypes<'ast, T> for Type<'ast, T> {
fn into_types(self) -> Types<'ast, T> {
Types { inner: vec![self] }
}
}
impl<'ast, T> IntoTypes<'ast, T> for StructType<'ast, T> {
fn into_types(self) -> Types<'ast, T> {
Types {
inner: vec![Type::Struct(self)],
}
}
}
impl<'ast, T> IntoTypes<'ast, T> for ArrayType<'ast, T> {
fn into_types(self) -> Types<'ast, T> {
Types {
inner: vec![Type::Array(self)],
}
}
}
impl<'ast, T> IntoTypes<'ast, T> for TupleType<'ast, T> {
fn into_types(self) -> Types<'ast, T> {
Types {
inner: vec![Type::Tuple(self)],
}
}
}
impl<'ast, T> IntoTypes<'ast, T> for UBitwidth {
fn into_types(self) -> Types<'ast, T> {
Types {
inner: vec![Type::Uint(self)],
}
}
}
impl<'ast, T> IntoTypes<'ast, T> for Types<'ast, T> {
fn into_types(self) -> Types<'ast, T> {
impl<'ast, T> IntoType<'ast, T> for Type<'ast, T> {
fn into_type(self) -> Type<'ast, T> {
self
}
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct Types<'ast, T> {
pub inner: Vec<Type<'ast, T>>,
impl<'ast, T> IntoType<'ast, T> for StructType<'ast, T> {
fn into_type(self) -> Type<'ast, T> {
Type::Struct(self)
}
}
impl<'ast, T> Types<'ast, T> {
pub fn new(types: Vec<Type<'ast, T>>) -> Self {
Self { inner: types }
impl<'ast, T> IntoType<'ast, T> for ArrayType<'ast, T> {
fn into_type(self) -> Type<'ast, T> {
Type::Array(self)
}
}
impl<'ast, T> IntoType<'ast, T> for TupleType<'ast, T> {
fn into_type(self) -> Type<'ast, T> {
Type::Tuple(self)
}
}
impl<'ast, T> IntoType<'ast, T> for UBitwidth {
fn into_type(self) -> Type<'ast, T> {
Type::Uint(self)
}
}
@ -294,6 +269,7 @@ impl<'ast, T> TryInto<usize> for DeclarationConstant<'ast, T> {
pub type MemberId = String;
#[allow(clippy::derive_hash_xor_eq)]
#[derive(Debug, Clone, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct GStructMember<S> {
#[serde(rename = "name")]
@ -306,7 +282,7 @@ pub type DeclarationStructMember<'ast, T> = GStructMember<DeclarationConstant<'a
pub type ConcreteStructMember = GStructMember<u32>;
pub type StructMember<'ast, T> = GStructMember<UExpression<'ast, T>>;
impl<'ast, S, R: PartialEq<S>> PartialEq<GStructMember<S>> for GStructMember<R> {
impl<S, R: PartialEq<S>> PartialEq<GStructMember<S>> for GStructMember<R> {
fn eq(&self, other: &GStructMember<S>) -> bool {
self.id == other.id && *self.ty == *other.ty
}
@ -335,9 +311,10 @@ impl<'ast, T> From<ConcreteStructMember> for StructMember<'ast, T> {
}
}
#[allow(clippy::derive_hash_xor_eq)]
#[derive(Clone, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Debug)]
pub struct GArrayType<S> {
pub size: S,
pub size: Box<S>,
#[serde(flatten)]
pub ty: Box<GType<S>>,
}
@ -346,9 +323,9 @@ pub type DeclarationArrayType<'ast, T> = GArrayType<DeclarationConstant<'ast, T>
pub type ConcreteArrayType = GArrayType<u32>;
pub type ArrayType<'ast, T> = GArrayType<UExpression<'ast, T>>;
impl<'ast, S, R: PartialEq<S>> PartialEq<GArrayType<S>> for GArrayType<R> {
impl<S, R: PartialEq<S>> PartialEq<GArrayType<S>> for GArrayType<R> {
fn eq(&self, other: &GArrayType<S>) -> bool {
*self.ty == *other.ty && self.size == other.size
*self.ty == *other.ty && *self.size == *other.size
}
}
@ -382,7 +359,7 @@ fn try_from_g_array_type<T: TryInto<U>, U>(
t: GArrayType<T>,
) -> Result<GArrayType<U>, SpecializationError> {
Ok(GArrayType {
size: t.size.try_into().map_err(|_| SpecializationError)?,
size: box (*t.size).try_into().map_err(|_| SpecializationError)?,
ty: box try_from_g_type(*t.ty)?,
})
}
@ -401,6 +378,7 @@ impl<'ast, T> From<ConcreteArrayType> for ArrayType<'ast, T> {
}
}
#[allow(clippy::derive_hash_xor_eq)]
#[derive(Clone, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Debug)]
pub struct GTupleType<S> {
pub elements: Vec<GType<S>>,
@ -416,7 +394,7 @@ pub type DeclarationTupleType<'ast, T> = GTupleType<DeclarationConstant<'ast, T>
pub type ConcreteTupleType = GTupleType<u32>;
pub type TupleType<'ast, T> = GTupleType<UExpression<'ast, T>>;
impl<'ast, S, R: PartialEq<S>> PartialEq<GTupleType<S>> for GTupleType<R> {
impl<S, R: PartialEq<S>> PartialEq<GTupleType<S>> for GTupleType<R> {
fn eq(&self, other: &GTupleType<S>) -> bool {
*self.elements == *other.elements
}
@ -506,7 +484,7 @@ pub type DeclarationStructType<'ast, T> = GStructType<DeclarationConstant<'ast,
pub type ConcreteStructType = GStructType<u32>;
pub type StructType<'ast, T> = GStructType<UExpression<'ast, T>>;
impl<'ast, S, R: PartialEq<S>> PartialEq<GStructType<S>> for GStructType<R> {
impl<S, R: PartialEq<S>> PartialEq<GStructType<S>> for GStructType<R> {
fn eq(&self, other: &GStructType<S>) -> bool {
self.canonical_location == other.canonical_location
&& self
@ -652,6 +630,7 @@ impl fmt::Display for UBitwidth {
}
}
#[allow(clippy::derive_hash_xor_eq)]
#[derive(Clone, Eq, Hash, PartialOrd, Ord, Debug)]
pub enum GType<S> {
FieldElement,
@ -779,7 +758,7 @@ pub type DeclarationType<'ast, T> = GType<DeclarationConstant<'ast, T>>;
pub type ConcreteType = GType<u32>;
pub type Type<'ast, T> = GType<UExpression<'ast, T>>;
impl<'ast, S, R: PartialEq<S>> PartialEq<GType<S>> for GType<R> {
impl<S, R: PartialEq<S>> PartialEq<GType<S>> for GType<R> {
fn eq(&self, other: &GType<S>) -> bool {
use self::GType::*;
@ -830,7 +809,7 @@ impl<S, U: Into<S>> From<(GType<S>, U)> for GArrayType<S> {
fn from(tup: (GType<S>, U)) -> Self {
GArrayType {
ty: box tup.0,
size: tup.1.into(),
size: box tup.1.into(),
}
}
}
@ -838,8 +817,8 @@ impl<S, U: Into<S>> From<(GType<S>, U)> for GArrayType<S> {
impl<S> GArrayType<S> {
pub fn new<U: Into<S>>(ty: GType<S>, size: U) -> Self {
GArrayType {
ty: Box::new(ty),
size: size.into(),
ty: box ty,
size: box size.into(),
}
}
}
@ -922,7 +901,7 @@ impl<'ast, T: fmt::Display + PartialEq + fmt::Debug> Type<'ast, T> {
(Array(l), Array(r)) => match l.ty.can_be_specialized_to(&r.ty) {
true => {
// check the size if types match
match (&l.size.as_inner(), &r.size) {
match (&l.size.as_inner(), &*r.size) {
// compare the sizes for concrete ones
(UExpressionInner::Value(v), DeclarationConstant::Concrete(c)) => {
(*v as u32) == *c
@ -953,7 +932,7 @@ impl ConcreteType {
GType::Boolean => 1,
GType::Uint(_) => 1,
GType::Array(array_type) => {
array_type.size as usize * array_type.ty.get_primitive_count()
*array_type.size as usize * array_type.ty.get_primitive_count()
}
GType::Tuple(tuple_type) => tuple_type
.elements
@ -1123,7 +1102,7 @@ pub fn check_type<'ast, T, S: Clone + PartialEq + PartialEq<u32>>(
(DeclarationType::Array(t0), GType::Array(t1)) => {
// both the inner type and the size must match
check_type(&t0.ty, &t1.ty, constants)
&& check_generic(&t0.size, Some(&t1.size), constants)
&& check_generic(&*t0.size, Some(&*t1.size), constants)
}
(DeclarationType::FieldElement, GType::FieldElement)
| (DeclarationType::Boolean, GType::Boolean) => true,
@ -1164,7 +1143,7 @@ impl<'ast, T> From<CanonicalConstantIdentifier<'ast>> for DeclarationConstant<'a
pub fn specialize_declaration_type<
'ast,
T: Clone,
S: Clone + PartialEq + From<u32> + fmt::Debug + From<CanonicalConstantIdentifier<'ast>>,
S: Clone + PartialEq + From<u32> + From<CanonicalConstantIdentifier<'ast>>,
>(
decl_ty: DeclarationType<'ast, T>,
generics: &GGenericsAssignment<'ast, S>,
@ -1172,10 +1151,10 @@ pub fn specialize_declaration_type<
Ok(match decl_ty {
DeclarationType::Int => unreachable!(),
DeclarationType::Array(t0) => {
let ty = box specialize_declaration_type(*t0.ty, generics)?;
let ty = specialize_declaration_type(*t0.ty, generics)?;
let size = t0.size.map(generics)?;
GType::Array(GArrayType { size, ty })
GType::Array(GArrayType::new(ty, size))
}
DeclarationType::Tuple(t0) => {
let elements = t0
@ -1244,12 +1223,22 @@ pub mod signature {
pub struct GSignature<S> {
pub generics: Vec<Option<S>>,
pub inputs: Vec<GType<S>>,
pub outputs: Vec<GType<S>>,
pub output: Box<GType<S>>,
}
impl<S> Default for GSignature<S> {
fn default() -> Self {
Self {
generics: vec![],
inputs: vec![],
output: box GType::Tuple(GTupleType::new(vec![])),
}
}
}
impl<S: PartialEq> PartialEq for GSignature<S> {
fn eq(&self, other: &Self) -> bool {
self.inputs == other.inputs && self.outputs == other.outputs
self.inputs == other.inputs && self.output == other.output
}
}
@ -1257,7 +1246,7 @@ pub mod signature {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.inputs
.partial_cmp(&other.inputs)
.map(|c| self.outputs.partial_cmp(&other.outputs).map(|d| c.then(d)))
.map(|c| self.output.partial_cmp(&other.output).map(|d| c.then(d)))
.unwrap()
}
}
@ -1271,17 +1260,7 @@ pub mod signature {
impl<S: Hash> Hash for GSignature<S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inputs.hash(state);
self.outputs.hash(state);
}
}
impl<S> Default for GSignature<S> {
fn default() -> Self {
GSignature {
generics: vec![],
inputs: vec![],
outputs: vec![],
}
self.output.hash(state);
}
}
@ -1297,13 +1276,13 @@ pub mod signature {
other
.inputs
.iter()
.chain(other.outputs.iter())
.zip(self.inputs.iter().chain(self.outputs.iter()))
.chain(std::iter::once(&*other.output))
.zip(self.inputs.iter().chain(std::iter::once(&*self.output)))
.all(|(decl_ty, ty)| check_type::<T, u32>(decl_ty, ty, &mut constants))
}
}
impl<'ast, T: Clone + PartialEq + fmt::Debug> DeclarationSignature<'ast, T> {
impl<'ast, T: Clone + PartialEq> DeclarationSignature<'ast, T> {
pub fn specialize(
&self,
values: Vec<Option<u32>>,
@ -1312,9 +1291,8 @@ pub mod signature {
// we keep track of the value of constants in a map, as a given constant can only have one value
let mut constants = ConcreteGenericsAssignment::default();
assert_eq!(self.inputs.len(), signature.inputs.len());
assert_eq!(self.outputs.len(), signature.outputs.len());
assert_eq!(self.generics.len(), values.len());
assert_eq!(self.inputs.len(), signature.inputs.len());
let decl_generics = self.generics.iter().map(|g| match g.clone().unwrap() {
DeclarationConstant::Generic(g) => g,
@ -1330,8 +1308,13 @@ pub mod signature {
let condition = self
.inputs
.iter()
.chain(self.outputs.iter())
.zip(signature.inputs.iter().chain(signature.outputs.iter()))
.chain(std::iter::once(&*self.output))
.zip(
signature
.inputs
.iter()
.chain(std::iter::once(&*signature.output)),
)
.all(|(decl_ty, ty)| check_type(decl_ty, ty, &mut constants));
if constants.0.len() != self.generics.len() {
@ -1344,11 +1327,11 @@ pub mod signature {
}
}
pub fn get_output_types(
pub fn get_output_type(
&self,
generics: Vec<Option<UExpression<'ast, T>>>,
inputs: Vec<Type<'ast, T>>,
) -> Result<Vec<Type<'ast, T>>, GenericIdentifier<'ast>> {
) -> Result<Type<'ast, T>, GenericIdentifier<'ast>> {
// we keep track of the value of constants in a map, as a given constant can only have one value
let mut constants = GenericsAssignment::default();
@ -1375,12 +1358,8 @@ pub mod signature {
.zip(inputs.iter())
.all(|(decl_ty, ty)| check_type(decl_ty, ty, &mut constants));
// get the outputs from the map
self.outputs
.clone()
.into_iter()
.map(|t| specialize_declaration_type(t, &constants))
.collect::<Result<_, _>>()
// get the specialized output
specialize_declaration_type(*self.output.clone(), &constants)
}
}
@ -1401,11 +1380,7 @@ pub mod signature {
.into_iter()
.map(try_from_g_type)
.collect::<Result<_, _>>()?,
outputs: t
.outputs
.into_iter()
.map(try_from_g_type)
.collect::<Result<_, _>>()?,
output: box try_from_g_type(*t.output)?,
})
}
@ -1453,21 +1428,7 @@ pub mod signature {
write!(f, ", ")?;
}
}
write!(f, ")")?;
match self.outputs.len() {
0 => write!(f, ""),
1 => write!(f, " -> {}", self.outputs[0]),
_ => {
write!(f, " -> (")?;
for (i, t) in self.outputs.iter().enumerate() {
write!(f, "{}", t)?;
if i < self.outputs.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
}
}
write!(f, ") -> {}", self.output)
}
}
@ -1486,8 +1447,8 @@ pub mod signature {
self
}
pub fn outputs(mut self, outputs: Vec<GType<S>>) -> Self {
self.outputs = outputs;
pub fn output(mut self, output: GType<S>) -> Self {
self.output = Box::new(output);
self
}
}
@ -1501,7 +1462,7 @@ pub mod signature {
fn signature() {
let s = ConcreteSignature::new()
.inputs(vec![ConcreteType::FieldElement, ConcreteType::Boolean])
.outputs(vec![ConcreteType::Boolean]);
.output(ConcreteType::Boolean);
assert_eq!(s.to_string(), String::from("(field, bool) -> bool"));
}

View file

@ -1,5 +1,5 @@
use crate::typed_absy::types::UBitwidth;
use crate::typed_absy::*;
use crate::typed::types::UBitwidth;
use crate::typed::*;
use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub};
use zokrates_field::Field;

View file

@ -1,14 +1,15 @@
use crate::typed_absy::types::{DeclarationConstant, GStructType, UBitwidth};
use crate::typed_absy::types::{GType, SpecializationError};
use crate::typed_absy::Identifier;
use crate::typed_absy::UExpression;
use crate::typed_absy::{TryFrom, TryInto};
use crate::typed::types::{DeclarationConstant, GStructType, UBitwidth};
use crate::typed::types::{GType, SpecializationError};
use crate::typed::Identifier;
use crate::typed::UExpression;
use crate::typed::{TryFrom, TryInto};
use std::fmt;
#[derive(Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Hash, Eq, PartialOrd, Ord, Debug)]
pub struct GVariable<'ast, S> {
pub id: Identifier<'ast>,
pub _type: GType<S>,
pub is_mutable: bool,
}
pub type DeclarationVariable<'ast, T> = GVariable<'ast, DeclarationConstant<'ast, T>>;
@ -21,7 +22,11 @@ impl<'ast, T> TryFrom<Variable<'ast, T>> for ConcreteVariable<'ast> {
fn try_from(v: Variable<'ast, T>) -> Result<Self, Self::Error> {
let _type = v._type.try_into()?;
Ok(Self { _type, id: v.id })
Ok(Self {
_type,
id: v.id,
is_mutable: v.is_mutable,
})
}
}
@ -29,48 +34,60 @@ impl<'ast, T> From<ConcreteVariable<'ast>> for Variable<'ast, T> {
fn from(v: ConcreteVariable<'ast>) -> Self {
let _type = v._type.into();
Self { _type, id: v.id }
Self {
_type,
id: v.id,
is_mutable: v.is_mutable,
}
}
}
pub fn try_from_g_variable<T: TryInto<U>, U>(
v: GVariable<T>,
) -> Result<GVariable<U>, SpecializationError> {
let _type = crate::typed_absy::types::try_from_g_type(v._type)?;
let _type = crate::typed::types::try_from_g_type(v._type)?;
Ok(GVariable { _type, id: v.id })
Ok(GVariable {
_type,
id: v.id,
is_mutable: v.is_mutable,
})
}
impl<'ast, S: Clone> GVariable<'ast, S> {
pub fn field_element<I: Into<Identifier<'ast>>>(id: I) -> Self {
Self::with_id_and_type(id, GType::FieldElement)
Self::immutable(id, GType::FieldElement)
}
pub fn boolean<I: Into<Identifier<'ast>>>(id: I) -> Self {
Self::with_id_and_type(id, GType::Boolean)
Self::immutable(id, GType::Boolean)
}
pub fn uint<I: Into<Identifier<'ast>>, W: Into<UBitwidth>>(id: I, bitwidth: W) -> Self {
Self::with_id_and_type(id, GType::uint(bitwidth))
}
#[cfg(test)]
pub fn field_array<I: Into<Identifier<'ast>>>(id: I, size: S) -> Self {
Self::array(id, GType::FieldElement, size)
Self::immutable(id, GType::uint(bitwidth))
}
pub fn array<I: Into<Identifier<'ast>>, U: Into<S>>(id: I, ty: GType<S>, size: U) -> Self {
Self::with_id_and_type(id, GType::array((ty, size.into())))
Self::immutable(id, GType::array((ty, size.into())))
}
pub fn struc<I: Into<Identifier<'ast>>>(id: I, ty: GStructType<S>) -> Self {
Self::with_id_and_type(id, GType::Struct(ty))
Self::immutable(id, GType::Struct(ty))
}
pub fn with_id_and_type<I: Into<Identifier<'ast>>>(id: I, _type: GType<S>) -> Self {
pub fn immutable<I: Into<Identifier<'ast>>>(id: I, _type: GType<S>) -> Self {
Self::new(id, _type, false)
}
pub fn mutable<I: Into<Identifier<'ast>>>(id: I, _type: GType<S>) -> Self {
Self::new(id, _type, true)
}
pub fn new<I: Into<Identifier<'ast>>>(id: I, _type: GType<S>, is_mutable: bool) -> Self {
GVariable {
id: id.into(),
_type,
is_mutable,
}
}
@ -84,9 +101,3 @@ impl<'ast, S: fmt::Display> fmt::Display for GVariable<'ast, S> {
write!(f, "{} {}", self._type, self.id,)
}
}
impl<'ast, S: fmt::Debug> fmt::Debug for GVariable<'ast, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Variable(type: {:?}, id: {:?})", self._type, self.id,)
}
}

File diff suppressed because it is too large Load diff

View file

@ -8,14 +8,16 @@
mod from_ast;
mod node;
pub mod parameter;
mod position;
pub mod types;
pub mod variable;
pub use crate::absy::node::{Node, NodeValue};
pub use crate::absy::parameter::{Parameter, ParameterNode};
use crate::absy::types::{UnresolvedSignature, UnresolvedType, UserTypeId};
pub use crate::absy::variable::{Variable, VariableNode};
use crate::embed::FlatEmbed;
pub use self::node::{Node, NodeValue};
pub use self::parameter::{Parameter, ParameterNode};
pub use self::position::Position;
use self::types::{UnresolvedSignature, UnresolvedType, UserTypeId};
pub use self::variable::{Variable, VariableNode};
use crate::common::FlatEmbed;
use std::path::{Path, PathBuf};
use std::fmt;
@ -42,7 +44,7 @@ pub struct Program<'ast> {
pub main: OwnedModuleId,
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SymbolIdentifier<'ast> {
pub id: Identifier<'ast>,
pub alias: Option<Identifier<'ast>>,
@ -75,7 +77,7 @@ impl<'ast> fmt::Display for SymbolIdentifier<'ast> {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CanonicalImport<'ast> {
pub source: &'ast Path,
pub id: SymbolIdentifier<'ast>,
@ -89,7 +91,7 @@ impl<'ast> fmt::Display for CanonicalImport<'ast> {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SymbolImport<'ast> {
pub module_id: OwnedModuleId,
pub symbol_id: Identifier<'ast>,
@ -343,7 +345,7 @@ impl<'ast> fmt::Display for Function<'ast> {
write!(
f,
"({}):\n{}",
"({}) {{\n{}\n}}",
self.arguments
.iter()
.map(|x| format!("{}", x))
@ -384,9 +386,9 @@ impl<'ast> fmt::Display for Assignee<'ast> {
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq)]
pub enum Statement<'ast> {
Return(ExpressionListNode<'ast>),
Declaration(VariableNode<'ast>),
Definition(AssigneeNode<'ast>, ExpressionNode<'ast>),
Return(Option<ExpressionNode<'ast>>),
Definition(VariableNode<'ast>, ExpressionNode<'ast>),
Assignment(AssigneeNode<'ast>, ExpressionNode<'ast>),
Assertion(ExpressionNode<'ast>, Option<String>),
For(
VariableNode<'ast>,
@ -394,7 +396,7 @@ pub enum Statement<'ast> {
ExpressionNode<'ast>,
Vec<StatementNode<'ast>>,
),
MultipleDefinition(Vec<AssigneeNode<'ast>>, ExpressionNode<'ast>),
Log(&'ast str, Vec<ExpressionNode<'ast>>),
}
pub type StatementNode<'ast> = Node<Statement<'ast>>;
@ -402,32 +404,41 @@ pub type StatementNode<'ast> = Node<Statement<'ast>>;
impl<'ast> fmt::Display for Statement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Return(ref expr) => write!(f, "return {}", expr),
Statement::Declaration(ref var) => write!(f, "{}", var),
Statement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
Statement::Return(ref expr) => {
write!(f, "return")?;
match expr {
Some(e) => write!(f, " {};", e),
None => write!(f, ";"),
}
}
Statement::Definition(ref var, ref rhs) => {
write!(f, "{} = {};", var, rhs)
}
Statement::Assignment(ref lhs, ref rhs) => write!(f, "{} = {};", lhs, rhs),
Statement::Assertion(ref e, ref message) => {
write!(f, "assert({}", e)?;
match message {
Some(m) => write!(f, ", \"{}\")", m),
None => write!(f, ")"),
Some(m) => write!(f, ", \"{}\");", m),
None => write!(f, ");"),
}
}
Statement::For(ref var, ref start, ref stop, ref list) => {
writeln!(f, "for {} in {}..{} do", var, start, stop)?;
writeln!(f, "for {} in {}..{} {{", var, start, stop)?;
for l in list {
writeln!(f, "\t\t{}", l)?;
}
write!(f, "\tendfor")
}
Statement::MultipleDefinition(ref ids, ref rhs) => {
for (i, id) in ids.iter().enumerate() {
write!(f, "{}", id)?;
if i < ids.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, " = {}", rhs)
write!(f, "\t}}")
}
Statement::Log(ref l, ref expressions) => write!(
f,
"log({}, {})",
l,
expressions
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
),
}
}
}
@ -510,12 +521,49 @@ impl<'ast> fmt::Display for Range<'ast> {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConditionalKind {
IfElse,
Ternary,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConditionalExpression<'ast> {
pub condition: Box<ExpressionNode<'ast>>,
pub consequence_statements: Vec<StatementNode<'ast>>,
pub consequence: Box<ExpressionNode<'ast>>,
pub alternative_statements: Vec<StatementNode<'ast>>,
pub alternative: Box<ExpressionNode<'ast>>,
pub kind: ConditionalKind,
}
impl<'ast> fmt::Display for ConditionalExpression<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ConditionalKind::IfElse => {
writeln!(f, "if {} {{", self.condition)?;
for cs in self.consequence_statements.iter() {
writeln!(f, "\t{}", cs)?;
}
writeln!(f, "\t{}", self.consequence)?;
write!(f, "}} else {{")?;
for als in self.alternative_statements.iter() {
writeln!(f, "\t{}", als)?;
}
writeln!(f, "\t{}", self.alternative)?;
write!(f, "}}")
}
ConditionalKind::Ternary => {
write!(
f,
"{} ? {} : {}",
self.condition, self.consequence, self.alternative
)
}
}
}
}
/// An expression
#[derive(Debug, Clone, PartialEq)]
pub enum Expression<'ast> {
@ -535,12 +583,7 @@ pub enum Expression<'ast> {
Pow(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
Neg(Box<ExpressionNode<'ast>>),
Pos(Box<ExpressionNode<'ast>>),
Conditional(
Box<ExpressionNode<'ast>>,
Box<ExpressionNode<'ast>>,
Box<ExpressionNode<'ast>>,
ConditionalKind,
),
Conditional(Box<ConditionalExpression<'ast>>),
FunctionCall(
Box<ExpressionNode<'ast>>,
Option<Vec<Option<ExpressionNode<'ast>>>>,
@ -589,18 +632,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
Expression::Neg(ref e) => write!(f, "(-{})", e),
Expression::Pos(ref e) => write!(f, "(+{})", e),
Expression::BooleanConstant(b) => write!(f, "{}", b),
Expression::Conditional(ref condition, ref consequent, ref alternative, ref kind) => {
match kind {
ConditionalKind::IfElse => write!(
f,
"if {} then {} else {} fi",
condition, consequent, alternative
),
ConditionalKind::Ternary => {
write!(f, "{} ? {} : {}", condition, consequent, alternative)
}
}
}
Expression::Conditional(ref c) => write!(f, "{}", c),
Expression::FunctionCall(ref i, ref g, ref p) => {
if let Some(g) = g {
write!(
@ -680,23 +712,3 @@ impl<'ast> fmt::Display for Expression<'ast> {
}
}
}
/// A list of expressions, used in return statements
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ExpressionList<'ast> {
pub expressions: Vec<ExpressionNode<'ast>>,
}
pub type ExpressionListNode<'ast> = Node<ExpressionList<'ast>>;
impl<'ast> fmt::Display for ExpressionList<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, param) in self.expressions.iter().enumerate() {
write!(f, "{}", param)?;
if i < self.expressions.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "")
}
}

View file

@ -1,4 +1,3 @@
use crate::parser::Position;
use std::fmt;
use zokrates_pest_ast::Span;
@ -53,7 +52,6 @@ pub trait NodeValue: fmt::Display + fmt::Debug + Sized + PartialEq {
Node::new(start, end, self)
}
#[cfg(test)]
fn mock(self) -> Node<Self> {
Node::new(Position::mock(), Position::mock(), self)
}
@ -81,11 +79,9 @@ impl<V: NodeValue> From<V> for Node<V> {
}
}
use crate::absy::types::UnresolvedType;
use crate::absy::*;
use super::*;
impl<'ast> NodeValue for Expression<'ast> {}
impl<'ast> NodeValue for ExpressionList<'ast> {}
impl<'ast> NodeValue for Assignee<'ast> {}
impl<'ast> NodeValue for Statement<'ast> {}
impl<'ast> NodeValue for SymbolDeclaration<'ast> {}

View file

@ -1,29 +1,23 @@
use crate::absy::{Node, VariableNode};
use super::{Node, VariableNode};
use std::fmt;
#[derive(Clone, PartialEq)]
pub struct Parameter<'ast> {
pub id: VariableNode<'ast>,
pub private: bool,
pub is_private: bool,
}
impl<'ast> Parameter<'ast> {
pub fn new(v: VariableNode<'ast>, private: bool) -> Self {
Parameter { id: v, private }
}
pub fn public(v: VariableNode<'ast>) -> Self {
Parameter {
id: v,
private: false,
}
pub fn new(v: VariableNode<'ast>, is_private: bool) -> Self {
Parameter { id: v, is_private }
}
pub fn private(v: VariableNode<'ast>) -> Self {
Parameter {
id: v,
private: true,
}
Self::new(v, true)
}
pub fn public(v: VariableNode<'ast>) -> Self {
Self::new(v, false)
}
}
@ -31,7 +25,7 @@ pub type ParameterNode<'ast> = Node<Parameter<'ast>>;
impl<'ast> fmt::Display for Parameter<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let visibility = if self.private { "private " } else { "" };
let visibility = if self.is_private { "private " } else { "" };
write!(
f,
"{}{} {}",
@ -47,7 +41,7 @@ impl<'ast> fmt::Debug for Parameter<'ast> {
write!(
f,
"Parameter(variable: {:?}, private: {:?})",
self.id, self.private
self.id, self.is_private
)
}
}

View file

@ -1,5 +1,5 @@
use crate::absy::ExpressionNode;
use crate::absy::UnresolvedTypeNode;
use super::ExpressionNode;
use super::UnresolvedTypeNode;
use std::fmt;
pub type Identifier<'ast> = &'ast str;
@ -76,24 +76,24 @@ impl<'ast> UnresolvedType<'ast> {
pub use self::signature::UnresolvedSignature;
mod signature {
use crate::absy::ConstantGenericNode;
use crate::untyped::ConstantGenericNode;
use std::fmt;
use crate::absy::UnresolvedTypeNode;
use crate::untyped::UnresolvedTypeNode;
#[derive(Clone, PartialEq, Default)]
pub struct UnresolvedSignature<'ast> {
pub generics: Vec<ConstantGenericNode<'ast>>,
pub inputs: Vec<UnresolvedTypeNode<'ast>>,
pub outputs: Vec<UnresolvedTypeNode<'ast>>,
pub output: Option<UnresolvedTypeNode<'ast>>,
}
impl<'ast> fmt::Debug for UnresolvedSignature<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Signature(inputs: {:?}, outputs: {:?})",
self.inputs, self.outputs
"Signature(inputs: {:?}, output: {:?})",
self.inputs, self.output
)
}
}
@ -107,14 +107,10 @@ mod signature {
write!(f, ", ")?;
}
}
write!(f, ") -> (")?;
for (i, t) in self.outputs.iter().enumerate() {
write!(f, "{}", t)?;
if i < self.outputs.len() - 1 {
write!(f, ", ")?;
}
match &self.output {
Some(ty) => write!(f, ") -> {}", ty),
None => write!(f, ")"),
}
write!(f, ")")
}
}
@ -133,8 +129,8 @@ mod signature {
self
}
pub fn outputs(mut self, outputs: Vec<UnresolvedTypeNode<'ast>>) -> Self {
self.outputs = outputs;
pub fn output(mut self, output: UnresolvedTypeNode<'ast>) -> Self {
self.output = Some(output);
self
}
}

View file

@ -0,0 +1,56 @@
use super::types::UnresolvedType;
use super::{Node, UnresolvedTypeNode};
use std::fmt;
use super::Identifier;
#[derive(Clone, PartialEq)]
pub struct Variable<'ast> {
pub is_mutable: bool,
pub id: Identifier<'ast>,
pub _type: UnresolvedTypeNode<'ast>,
}
pub type VariableNode<'ast> = Node<Variable<'ast>>;
impl<'ast> Variable<'ast> {
pub fn new<S: Into<&'ast str>>(
id: S,
t: UnresolvedTypeNode<'ast>,
is_mutable: bool,
) -> Variable<'ast> {
Variable {
is_mutable,
id: id.into(),
_type: t,
}
}
pub fn immutable<S: Into<&'ast str>>(id: S, t: UnresolvedTypeNode<'ast>) -> Self {
Self::new(id, t, false)
}
pub fn mutable<S: Into<&'ast str>>(id: S, t: UnresolvedTypeNode<'ast>) -> Self {
Self::new(id, t, true)
}
pub fn get_type(&self) -> &UnresolvedType<'ast> {
&self._type.value
}
}
impl<'ast> fmt::Display for Variable<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self._type, self.id,)
}
}
impl<'ast> fmt::Debug for Variable<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Variable(type: {:?}, id: {:?}, is_mutable: {:?})",
self._type, self.id, self.is_mutable
)
}
}

View file

@ -122,6 +122,12 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
variables.into_iter().map(|v| f.fold_variable(v)).collect(),
f.fold_expression_list(elist),
),
ZirStatement::Log(l, e) => ZirStatement::Log(
l,
e.into_iter()
.map(|(t, e)| (t, e.into_iter().map(|e| f.fold_expression(e)).collect()))
.collect(),
),
};
vec![res]
}
@ -164,11 +170,11 @@ pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>(
let e2 = f.fold_uint_expression(e2);
FieldElementExpression::Pow(box e1, box e2)
}
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
FieldElementExpression::Conditional(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond);
let cons = f.fold_field_expression(cons);
let alt = f.fold_field_expression(alt);
FieldElementExpression::IfElse(box cond, box cons, box alt)
FieldElementExpression::Conditional(box cond, box cons, box alt)
}
}
}
@ -255,11 +261,11 @@ pub fn fold_boolean_expression<'ast, T: Field, F: Folder<'ast, T>>(
let e = f.fold_boolean_expression(e);
BooleanExpression::Not(box e)
}
BooleanExpression::IfElse(box cond, box cons, box alt) => {
BooleanExpression::Conditional(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond);
let cons = f.fold_boolean_expression(cons);
let alt = f.fold_boolean_expression(alt);
BooleanExpression::IfElse(box cond, box cons, box alt)
BooleanExpression::Conditional(box cond, box cons, box alt)
}
}
}
@ -349,11 +355,11 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
UExpressionInner::Not(box e)
}
UExpressionInner::IfElse(box cond, box cons, box alt) => {
UExpressionInner::Conditional(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond);
let cons = f.fold_uint_expression(cons);
let alt = f.fold_uint_expression(alt);
UExpressionInner::IfElse(box cond, box cons, box alt)
UExpressionInner::Conditional(box cond, box cons, box alt)
}
}
}

View file

@ -1,11 +1,11 @@
use crate::typed_absy;
use crate::typed as typed_absy;
use crate::zir;
impl From<typed_absy::types::ConcreteSignature> for zir::types::Signature {
fn from(s: typed_absy::types::ConcreteSignature) -> zir::types::Signature {
zir::types::Signature {
inputs: s.inputs.into_iter().flat_map(from_type).collect(),
outputs: s.outputs.into_iter().flat_map(from_type).collect(),
outputs: from_type(*s.output),
}
}
}
@ -20,7 +20,7 @@ fn from_type(t: typed_absy::types::ConcreteType) -> Vec<zir::types::Type> {
}
typed_absy::types::ConcreteType::Array(array_type) => {
let inner = from_type(*array_type.ty);
(0..array_type.size).flat_map(|_| inner.clone()).collect()
(0..*array_type.size).flat_map(|_| inner.clone()).collect()
}
typed_absy::types::ConcreteType::Struct(members) => members
.into_iter()

View file

@ -1,7 +1,7 @@
use crate::zir::types::MemberId;
use std::fmt;
use crate::typed_absy::Identifier as CoreIdentifier;
use crate::typed::Identifier as CoreIdentifier;
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum Identifier<'ast> {

View file

@ -10,9 +10,10 @@ mod variable;
pub use self::parameter::Parameter;
pub use self::types::Type;
pub use self::variable::Variable;
use crate::common::{FlatEmbed, FormatString};
use crate::typed::ConcreteType;
pub use crate::zir::uint::{ShouldReduce, UExpression, UExpressionInner, UMetadata};
use crate::embed::FlatEmbed;
use crate::zir::types::Signature;
use std::convert::TryFrom;
use std::fmt;
@ -22,7 +23,7 @@ pub use self::folder::Folder;
pub use self::identifier::{Identifier, SourceIdentifier};
/// A typed program as a collection of modules, one of them being the main
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct ZirProgram<'ast, T> {
pub main: ZirFunction<'ast, T>,
}
@ -33,7 +34,7 @@ impl<'ast, T: fmt::Display> fmt::Display for ZirProgram<'ast, T> {
}
}
/// A typed function
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Eq)]
pub struct ZirFunction<'ast, T> {
/// Arguments of the function
pub arguments: Vec<Parameter<'ast>>,
@ -45,9 +46,9 @@ pub struct ZirFunction<'ast, T> {
impl<'ast, T: fmt::Display> fmt::Display for ZirFunction<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
writeln!(
f,
"({}) -> ({}):\n{}",
"({}) -> ({}) {{",
self.arguments
.iter()
.map(|x| format!("{}", x))
@ -58,13 +59,15 @@ impl<'ast, T: fmt::Display> fmt::Display for ZirFunction<'ast, T> {
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(", "),
self.statements
.iter()
.map(|x| format!("\t{}", x))
.collect::<Vec<_>>()
.join("\n")
)
.join(", ")
)?;
for s in &self.statements {
s.fmt_indented(f, 1)?;
writeln!(f)?;
}
writeln!(f, "}}")
}
}
@ -100,6 +103,12 @@ impl fmt::Display for RuntimeError {
}
}
impl RuntimeError {
pub fn mock() -> Self {
RuntimeError::SourceAssertion(String::default())
}
}
/// A statement in a `ZirFunction`
#[derive(Clone, PartialEq, Hash, Eq, Debug)]
pub enum ZirStatement<'ast, T> {
@ -112,11 +121,22 @@ pub enum ZirStatement<'ast, T> {
),
Assertion(BooleanExpression<'ast, T>, RuntimeError),
MultipleDefinition(Vec<ZirAssignee<'ast>>, ZirExpressionList<'ast, T>),
Log(
FormatString,
Vec<(ConcreteType, Vec<ZirExpression<'ast, T>>)>,
),
}
impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
self.fmt_indented(f, 0)
}
}
impl<'ast, T: fmt::Display> ZirStatement<'ast, T> {
fn fmt_indented(&self, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
write!(f, "{}", "\t".repeat(depth))?;
match self {
ZirStatement::Return(ref exprs) => {
write!(f, "return ")?;
for (i, expr) in exprs.iter().enumerate() {
@ -125,31 +145,29 @@ impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
write!(f, ", ")?;
}
}
write!(f, "")
write!(f, ";")
}
ZirStatement::Definition(ref lhs, ref rhs) => {
write!(f, "{} = {};", lhs, rhs)
}
ZirStatement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
ZirStatement::IfElse(ref condition, ref consequence, ref alternative) => {
write!(
f,
"if {} then {{{}}} else {{{}}} fi",
condition,
consequence
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join("\n"),
alternative
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join("\n")
)
writeln!(f, "if {} {{", condition)?;
for s in consequence {
s.fmt_indented(f, depth + 1)?;
writeln!(f)?;
}
writeln!(f, "{}}} else {{", "\t".repeat(depth))?;
for s in alternative {
s.fmt_indented(f, depth + 1)?;
writeln!(f)?;
}
write!(f, "{}}};", "\t".repeat(depth))
}
ZirStatement::Assertion(ref e, ref error) => {
write!(f, "assert({}", e)?;
match error {
RuntimeError::SourceAssertion(message) => write!(f, ", \"{}\")", message),
error => write!(f, ") // {}", error),
RuntimeError::SourceAssertion(message) => write!(f, ", \"{}\");", message),
error => write!(f, "); // {}", error),
}
}
ZirStatement::MultipleDefinition(ref ids, ref rhs) => {
@ -159,8 +177,24 @@ impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
write!(f, ", ")?;
}
}
write!(f, " = {}", rhs)
write!(f, " = {};", rhs)
}
ZirStatement::Log(ref l, ref expressions) => write!(
f,
"log(\"{}\"), {})",
l,
expressions
.iter()
.map(|(_, e)| format!(
"[{}]",
e.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
}
}
}
@ -278,7 +312,7 @@ pub enum FieldElementExpression<'ast, T> {
Box<FieldElementExpression<'ast, T>>,
Box<UExpression<'ast, T>>,
),
IfElse(
Conditional(
Box<BooleanExpression<'ast, T>>,
Box<FieldElementExpression<'ast, T>>,
Box<FieldElementExpression<'ast, T>>,
@ -329,7 +363,7 @@ pub enum BooleanExpression<'ast, T> {
Box<BooleanExpression<'ast, T>>,
),
Not(Box<BooleanExpression<'ast, T>>),
IfElse(
Conditional(
Box<BooleanExpression<'ast, T>>,
Box<BooleanExpression<'ast, T>>,
Box<BooleanExpression<'ast, T>>,
@ -418,10 +452,10 @@ impl<'ast, T: fmt::Display> fmt::Display for FieldElementExpression<'ast, T> {
FieldElementExpression::Mult(ref lhs, ref rhs) => write!(f, "({} * {})", lhs, rhs),
FieldElementExpression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
FieldElementExpression::Pow(ref lhs, ref rhs) => write!(f, "{}**{}", lhs, rhs),
FieldElementExpression::IfElse(ref condition, ref consequent, ref alternative) => {
FieldElementExpression::Conditional(ref condition, ref consequent, ref alternative) => {
write!(
f,
"if {} then {} else {} fi",
"if {} {{ {} }} else {{ {} }}",
condition, consequent, alternative
)
}
@ -454,11 +488,13 @@ impl<'ast, T: fmt::Display> fmt::Display for UExpression<'ast, T> {
UExpressionInner::LeftShift(ref e, ref by) => write!(f, "({} << {})", e, by),
UExpressionInner::RightShift(ref e, ref by) => write!(f, "({} >> {})", e, by),
UExpressionInner::Not(ref e) => write!(f, "!{}", e),
UExpressionInner::IfElse(ref condition, ref consequent, ref alternative) => write!(
f,
"if {} then {} else {} fi",
condition, consequent, alternative
),
UExpressionInner::Conditional(ref condition, ref consequent, ref alternative) => {
write!(
f,
"if {} {{ {} }} else {{ {} }}",
condition, consequent, alternative
)
}
}
}
}
@ -491,11 +527,13 @@ impl<'ast, T: fmt::Display> fmt::Display for BooleanExpression<'ast, T> {
BooleanExpression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs),
BooleanExpression::And(ref lhs, ref rhs) => write!(f, "{} && {}", lhs, rhs),
BooleanExpression::Not(ref exp) => write!(f, "!{}", exp),
BooleanExpression::IfElse(ref condition, ref consequent, ref alternative) => write!(
f,
"if {} then {} else {} fi",
condition, consequent, alternative
),
BooleanExpression::Conditional(ref condition, ref consequent, ref alternative) => {
write!(
f,
"if {} {{ {} }} else {{ {} }}",
condition, consequent, alternative
)
}
}
}
}
@ -547,39 +585,43 @@ impl<'ast, T: fmt::Debug> fmt::Debug for ZirExpressionList<'ast, T> {
// Common behaviour accross expressions
pub trait IfElse<'ast, T> {
fn if_else(condition: BooleanExpression<'ast, T>, consequence: Self, alternative: Self)
-> Self;
pub trait Conditional<'ast, T> {
fn conditional(
condition: BooleanExpression<'ast, T>,
consequence: Self,
alternative: Self,
) -> Self;
}
impl<'ast, T> IfElse<'ast, T> for FieldElementExpression<'ast, T> {
fn if_else(
impl<'ast, T> Conditional<'ast, T> for FieldElementExpression<'ast, T> {
fn conditional(
condition: BooleanExpression<'ast, T>,
consequence: Self,
alternative: Self,
) -> Self {
FieldElementExpression::IfElse(box condition, box consequence, box alternative)
FieldElementExpression::Conditional(box condition, box consequence, box alternative)
}
}
impl<'ast, T> IfElse<'ast, T> for BooleanExpression<'ast, T> {
fn if_else(
impl<'ast, T> Conditional<'ast, T> for BooleanExpression<'ast, T> {
fn conditional(
condition: BooleanExpression<'ast, T>,
consequence: Self,
alternative: Self,
) -> Self {
BooleanExpression::IfElse(box condition, box consequence, box alternative)
BooleanExpression::Conditional(box condition, box consequence, box alternative)
}
}
impl<'ast, T> IfElse<'ast, T> for UExpression<'ast, T> {
fn if_else(
impl<'ast, T> Conditional<'ast, T> for UExpression<'ast, T> {
fn conditional(
condition: BooleanExpression<'ast, T>,
consequence: Self,
alternative: Self,
) -> Self {
let bitwidth = consequence.bitwidth;
UExpressionInner::IfElse(box condition, box consequence, box alternative).annotate(bitwidth)
UExpressionInner::Conditional(box condition, box consequence, box alternative)
.annotate(bitwidth)
}
}

View file

@ -1,7 +1,7 @@
use crate::zir::Variable;
use std::fmt;
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Eq)]
pub struct Parameter<'ast> {
pub id: Variable<'ast>,
pub private: bool,

View file

@ -147,6 +147,19 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
.collect::<Result<_, _>>()?,
f.fold_expression_list(elist)?,
),
ZirStatement::Log(l, e) => {
let e = e
.into_iter()
.map(|(t, e)| {
e.into_iter()
.map(|e| f.fold_expression(e))
.collect::<Result<Vec<_>, _>>()
.map(|e| (t, e))
})
.collect::<Result<Vec<_>, _>>()?;
ZirStatement::Log(l, e)
}
};
Ok(vec![res])
}
@ -191,11 +204,11 @@ pub fn fold_field_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
let e2 = f.fold_uint_expression(e2)?;
FieldElementExpression::Pow(box e1, box e2)
}
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
FieldElementExpression::Conditional(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond)?;
let cons = f.fold_field_expression(cons)?;
let alt = f.fold_field_expression(alt)?;
FieldElementExpression::IfElse(box cond, box cons, box alt)
FieldElementExpression::Conditional(box cond, box cons, box alt)
}
})
}
@ -282,11 +295,11 @@ pub fn fold_boolean_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
let e = f.fold_boolean_expression(e)?;
BooleanExpression::Not(box e)
}
BooleanExpression::IfElse(box cond, box cons, box alt) => {
BooleanExpression::Conditional(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond)?;
let cons = f.fold_boolean_expression(cons)?;
let alt = f.fold_boolean_expression(alt)?;
BooleanExpression::IfElse(box cond, box cons, box alt)
BooleanExpression::Conditional(box cond, box cons, box alt)
}
})
}
@ -378,12 +391,12 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
UExpressionInner::Not(box e)
}
UExpressionInner::IfElse(box cond, box cons, box alt) => {
UExpressionInner::Conditional(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond)?;
let cons = f.fold_uint_expression(cons)?;
let alt = f.fold_uint_expression(alt)?;
UExpressionInner::IfElse(box cond, box cons, box alt)
UExpressionInner::Conditional(box cond, box cons, box alt)
}
})
}

View file

@ -4,12 +4,14 @@ use crate::zir::BooleanExpression;
use zokrates_field::Field;
impl<'ast, T: Field> UExpression<'ast, T> {
#[allow(clippy::should_implement_trait)]
pub fn add(self, other: Self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(bitwidth, other.bitwidth);
UExpressionInner::Add(box self, box other).annotate(bitwidth)
}
#[allow(clippy::should_implement_trait)]
pub fn sub(self, other: Self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(bitwidth, other.bitwidth);
@ -27,12 +29,14 @@ impl<'ast, T: Field> UExpression<'ast, T> {
UExpressionInner::Mult(box self, box other).annotate(bitwidth)
}
#[allow(clippy::should_implement_trait)]
pub fn div(self, other: Self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(bitwidth, other.bitwidth);
UExpressionInner::Div(box self, box other).annotate(bitwidth)
}
#[allow(clippy::should_implement_trait)]
pub fn rem(self, other: Self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(bitwidth, other.bitwidth);
@ -45,6 +49,7 @@ impl<'ast, T: Field> UExpression<'ast, T> {
UExpressionInner::Xor(box self, box other).annotate(bitwidth)
}
#[allow(clippy::should_implement_trait)]
pub fn not(self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
UExpressionInner::Not(box self).annotate(bitwidth)
@ -185,7 +190,7 @@ pub enum UExpressionInner<'ast, T> {
LeftShift(Box<UExpression<'ast, T>>, u32),
RightShift(Box<UExpression<'ast, T>>, u32),
Not(Box<UExpression<'ast, T>>),
IfElse(
Conditional(
Box<BooleanExpression<'ast, T>>,
Box<UExpression<'ast, T>>,
Box<UExpression<'ast, T>>,

View file

@ -0,0 +1,28 @@
[package]
name = "zokrates_bellman"
version = "0.1.0"
edition = "2021"
[features]
wasm = ["bellman/nolog", "bellman/wasm"]
multicore = ["bellman/multicore", "phase2/multicore"]
[dependencies]
zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
zokrates_proof_systems = { version = "0.1", path = "../zokrates_proof_systems", default-features = false }
bellman = { package = "bellman_ce", version = "^0.3", default-features = false }
pairing = { package = "pairing_ce", version = "^0.21" }
phase2 = { git = "https://github.com/Zokrates/phase2", default-features = false }
rand_0_4 = { version = "0.4", package = "rand" }#
getrandom = { version = "0.2", features = ["js", "wasm-bindgen"] }
hex = "0.4.2"
[dev-dependencies]
zokrates_interpreter = { version = "0.1", path = "../zokrates_interpreter", features = ["bellman"] }

View file

@ -4,19 +4,19 @@ use bellman::groth16::{
};
use pairing::{ff::to_hex, CurveAffine, Engine};
use crate::proof_system::{Backend, MpcBackend, NonUniversalBackend, Proof, SetupKeypair};
use zokrates_field::BellmanFieldExtensions;
use zokrates_field::Field;
use zokrates_proof_systems::{Backend, MpcBackend, NonUniversalBackend, Proof, SetupKeypair};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::bellman::Bellman;
use crate::proof_system::bellman::Computation;
use crate::proof_system::bellman::{parse_g1, parse_g2};
use crate::proof_system::groth16::{ProofPoints, VerificationKey, G16};
use crate::proof_system::Scheme;
use crate::Bellman;
use crate::Computation;
use crate::{parse_g1, parse_g2};
use phase2::MPCParameters;
use rand_0_4::Rng;
use std::io::{Read, Write};
use zokrates_ast::ir::{ProgIterator, Statement, Witness};
use zokrates_proof_systems::groth16::{ProofPoints, VerificationKey, G16};
use zokrates_proof_systems::Scheme;
const G16_WARNING: &str = "WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/toolbox/proving_schemes.html#g16-malleability for implications.";
@ -155,8 +155,8 @@ impl<T: Field + BellmanFieldExtensions> MpcBackend<T, G16> for Bellman {
pub mod serialization {
use super::*;
use crate::proof_system::{G1Affine, G2Affine};
use pairing::from_hex;
use zokrates_proof_systems::{G1Affine, G2Affine};
pub fn parameters_to_verification_key<T: Field + BellmanFieldExtensions>(
parameters: &Parameters<T::BellmanEngine>,
@ -186,29 +186,32 @@ pub mod serialization {
pub fn to_g2<T: BellmanFieldExtensions>(
g2: G2Affine,
) -> <T::BellmanEngine as Engine>::G2Affine {
let x = T::new_fq2(&(g2.0).0, &(g2.0).1);
let y = T::new_fq2(&(g2.1).0, &(g2.1).1);
<T::BellmanEngine as Engine>::G2Affine::from_xy_unchecked(x, y)
match g2 {
G2Affine::Fq2(g2) => {
let x = T::new_fq2(&(g2.0).0, &(g2.0).1);
let y = T::new_fq2(&(g2.1).0, &(g2.1).1);
<T::BellmanEngine as Engine>::G2Affine::from_xy_unchecked(x, y)
}
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use zokrates_field::Bn128Field;
use zokrates_interpreter::Interpreter;
use super::*;
use crate::flat_absy::{FlatParameter, FlatVariable};
use crate::ir::{Interpreter, Prog, Statement};
use zokrates_ast::common::{Parameter, Variable};
use zokrates_ast::ir::{Prog, Statement};
#[test]
fn verify() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
)],
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let keypair = <Bellman as NonUniversalBackend<Bn128Field, G16>>::setup(program.clone());

View file

@ -1,18 +1,20 @@
pub mod groth16;
use crate::ir::{CanonicalLinComb, ProgIterator, Statement, Witness};
use bellman::groth16::Proof;
use bellman::groth16::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
Parameters,
};
use bellman::pairing::ff::ScalarEngine;
use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError, Variable};
use bellman::{
Circuit, ConstraintSystem, LinearCombination, SynthesisError, Variable as BellmanVariable,
};
use std::collections::BTreeMap;
use zokrates_ast::common::Variable;
use zokrates_ast::ir::{CanonicalLinComb, ProgIterator, Statement, Witness};
use zokrates_field::BellmanFieldExtensions;
use zokrates_field::Field;
use crate::flat_absy::FlatVariable;
use rand_0_4::ChaChaRng;
pub use self::parse::*;
@ -44,7 +46,7 @@ impl<T: Field, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
fn bellman_combination<T: BellmanFieldExtensions, CS: ConstraintSystem<T::BellmanEngine>>(
l: CanonicalLinComb<T>,
cs: &mut CS,
symbols: &mut BTreeMap<FlatVariable, Variable>,
symbols: &mut BTreeMap<Variable, BellmanVariable>,
witness: &mut Witness<T>,
) -> LinearCombination<T::BellmanEngine> {
l.0.into_iter()
@ -81,20 +83,21 @@ fn bellman_combination<T: BellmanFieldExtensions, CS: ConstraintSystem<T::Bellma
.fold(LinearCombination::zero(), |acc, e| acc + e)
}
impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn synthesize<CS: ConstraintSystem<T::BellmanEngine>>(
impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>>
Circuit<T::BellmanEngine> for Computation<T, I>
{
fn synthesize<CS: ConstraintSystem<T::BellmanEngine>>(
self,
cs: &mut CS,
witness: Option<Witness<T>>,
) -> Result<(), SynthesisError> {
// mapping from IR variables
let mut symbols = BTreeMap::new();
let mut witness = witness.unwrap_or_else(Witness::empty);
let mut witness = self.witness.unwrap_or_else(Witness::empty);
assert!(symbols.insert(FlatVariable::one(), CS::one()).is_none());
assert!(symbols.insert(Variable::one(), CS::one()).is_none());
symbols.extend(self.arguments.iter().enumerate().map(|(index, p)| {
symbols.extend(self.program.arguments.iter().enumerate().map(|(index, p)| {
let wire = match p.private {
true => cs.alloc(
|| format!("PRIVATE_INPUT_{}", index),
@ -121,7 +124,7 @@ impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> Pr
(p.id, wire)
}));
for statement in self.statements {
for statement in self.program.statements {
if let Statement::Constraint(quad, lin, _) = statement {
let a = &bellman_combination(
quad.left.into_canonical(),
@ -177,7 +180,7 @@ impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> Co
pub fn public_inputs_values(&self) -> Vec<<T::BellmanEngine as ScalarEngine>::Fr> {
self.program
.public_inputs(self.witness.as_ref().unwrap())
.public_inputs_values(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_bellman())
.collect()
@ -192,21 +195,10 @@ impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> Co
}
}
impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>>
Circuit<T::BellmanEngine> for Computation<T, I>
{
fn synthesize<CS: ConstraintSystem<T::BellmanEngine>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
self.program.synthesize(cs, self.witness)
}
}
mod parse {
use super::*;
use crate::proof_system::{G1Affine, G2Affine};
use pairing_ce::CurveAffine;
use pairing::CurveAffine;
use zokrates_proof_systems::{G1Affine, G2Affine, G2AffineFq2};
fn to_hex(bytes: &[u8]) -> String {
let mut hex = hex::encode(bytes);
@ -239,21 +231,21 @@ mod parse {
let y1 = to_hex(iter.next().unwrap());
let y0 = to_hex(iter.next().unwrap());
G2Affine((x0, x1), (y0, y1))
G2Affine::Fq2(G2AffineFq2((x0, x1), (y0, y1)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::Interpreter;
use crate::ir::LinComb;
use zokrates_ast::ir::LinComb;
use zokrates_field::Bn128Field;
use zokrates_interpreter::Interpreter;
mod prove {
use super::*;
use crate::flat_absy::FlatParameter;
use crate::ir::Prog;
use zokrates_ast::flat::Parameter;
use zokrates_ast::ir::Prog;
#[test]
fn empty() {
@ -271,12 +263,9 @@ mod tests {
#[test]
fn identity() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
arguments: vec![Parameter::private(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
)],
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let interpreter = Interpreter::default();
@ -294,12 +283,9 @@ mod tests {
#[test]
fn public_identity() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
)],
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
};
let interpreter = Interpreter::default();
@ -319,10 +305,7 @@ mod tests {
let program: Prog<Bn128Field> = Prog {
arguments: vec![],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::one(),
FlatVariable::public(0),
)],
statements: vec![Statement::constraint(Variable::one(), Variable::public(0))],
};
let interpreter = Interpreter::default();
@ -340,18 +323,18 @@ mod tests {
// private variables can be unordered
let program: Prog<Bn128Field> = Prog {
arguments: vec![
FlatParameter::private(FlatVariable::new(42)),
FlatParameter::public(FlatVariable::new(51)),
Parameter::private(Variable::new(42)),
Parameter::public(Variable::new(51)),
],
return_count: 2,
statements: vec![
Statement::constraint(
LinComb::from(FlatVariable::new(42)) + LinComb::from(FlatVariable::new(51)),
FlatVariable::public(0),
LinComb::from(Variable::new(42)) + LinComb::from(Variable::new(51)),
Variable::public(0),
),
Statement::constraint(
LinComb::from(FlatVariable::one()) + LinComb::from(FlatVariable::new(42)),
FlatVariable::public(1),
LinComb::from(Variable::one()) + LinComb::from(Variable::new(42)),
Variable::public(1),
),
],
};
@ -370,11 +353,11 @@ mod tests {
#[test]
fn one() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(42))],
arguments: vec![Parameter::public(Variable::new(42))],
return_count: 1,
statements: vec![Statement::constraint(
LinComb::from(FlatVariable::new(42)) + LinComb::one(),
FlatVariable::public(0),
LinComb::from(Variable::new(42)) + LinComb::one(),
Variable::public(0),
)],
};
@ -394,13 +377,13 @@ mod tests {
fn with_directives() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![
FlatParameter::private(FlatVariable::new(42)),
FlatParameter::public(FlatVariable::new(51)),
Parameter::private(Variable::new(42)),
Parameter::public(Variable::new(51)),
],
return_count: 1,
statements: vec![Statement::constraint(
LinComb::from(FlatVariable::new(42)) + LinComb::from(FlatVariable::new(51)),
FlatVariable::public(0),
LinComb::from(Variable::new(42)) + LinComb::from(Variable::new(51)),
Variable::public(0),
)],
};

View file

@ -15,6 +15,7 @@
- [Imports](language/imports.md)
- [Comments](language/comments.md)
- [Macros](language/macros.md)
- [Logging](language/logging.md)
- [Toolbox](toolbox/index.md)
- [CLI](toolbox/cli.md)

View file

@ -8,28 +8,30 @@ Function calls help make programs clear and modular.
Arguments are passed by value.
Function parameters can be declared as mutable to allow for mutation within the function's body. However, mutable function arguments are still passed by value, so the original value can never be mutated.
```zokrates
{{#include ../../../zokrates_cli/examples/book/side_effects.zok}}
```
Generic paramaters, if any, must be compile-time constants. They are inferred by the compiler if that is possible, but can also be provided explicitly.
Generic parameters, if any, must be compile-time constants. They are inferred by the compiler if that is possible, but can also be provided explicitly.
```zokrates
{{#include ../../../zokrates_cli/examples/book/generic_call.zok}}
```
### If-expressions
### Conditional expressions
An if-expression allows you to branch your code depending on a boolean condition.
A conditional expression allows you to branch your code depending on a boolean condition.
```zokrates
{{#include ../../../zokrates_cli/examples/book/if_else.zok}}
{{#include ../../../zokrates_cli/examples/book/conditional.zok}}
```
The conditional expression can also be written using a ternary operator:
```zokrates
{{#include ../../../zokrates_cli/examples/book/ternary.zok}}
{{#include ../../../zokrates_cli/examples/book/conditional_ternary.zok}}
```
There are two important caveats when it comes to conditional expressions. Before we go into them, let's define two concepts:
@ -39,11 +41,11 @@ There are two important caveats when it comes to conditional expressions. Before
Now the two caveats:
- **Both branches are always executed**. No short-circuiting happens based on the value of the condition. Therefore, the complexity of a program in terms of the number of constraints it compiles down to is the *sum* of the cost of all branches.
```zokrates
{{#include ../../../zokrates_cli/examples/book/if_else_expensive.zok}}
{{#include ../../../zokrates_cli/examples/book/conditional_expensive.zok}}
```
- **An unsatisfied constraint inside any branch will make the whole execution fail, even if this branch is not logically executed**. Also, the compiler itself inserts assertions which can fail. This can lead to unexpected results:
```zokrates
{{#include ../../../zokrates_cli/examples/book/if_else_panic.zok}}
{{#include ../../../zokrates_cli/examples/book/conditional_panic.zok}}
```
The experimental flag `--branch-isolation` can be activated in the CLI in order to restrict any unsatisfied constraint to make the execution fail only if it is in a logically executed branch. This way, the execution of the program above will always succeed.

View file

@ -18,18 +18,4 @@ The generic parameters can be provided explicitly, especially when they cannot b
```zokrates
{{#include ../../../zokrates_cli/examples/book/explicit_generic_parameters.zok}}
```
Functions can return multiple values by providing them as a comma-separated list.
```zokrates
{{#include ../../../zokrates_cli/examples/book/multi_return.zok}}
```
### Variable declaration
When defining a variable as the return value of a function, types are provided when the variable needs to be declared:
```zokrates
{{#include ../../../zokrates_cli/examples/book/multi_def.zok}}
```

View file

@ -8,7 +8,7 @@ You can separate your code into multiple ZoKrates files using `import` statement
The preferred way to import a symbol is by module and name:
```zokrates
from "./path/to/my/module" import MySymbol
from "./path/to/my/module" import MySymbol;
// `MySymbol` is now in scope.
```
@ -16,7 +16,7 @@ from "./path/to/my/module" import MySymbol
To import multiple symbols with a single import statement, separate the symbols names with commas:
```zokrates
from "./path/to/my/module" import MySymbol, MyOtherSymbol
from "./path/to/my/module" import MySymbol, MyOtherSymbol;
```
#### Aliasing
@ -24,7 +24,7 @@ from "./path/to/my/module" import MySymbol, MyOtherSymbol
The `as` keyword enables renaming symbols:
```zokrates
from "./path/to/my/module" import MySymbol as MyAlias
from "./path/to/my/module" import MySymbol as MyAlias;
// `MySymbol` is now in scope under the alias MyAlias.
```
@ -32,11 +32,11 @@ from "./path/to/my/module" import MySymbol as MyAlias
The legacy way to import a symbol is by only specifying a module:
```
import "./path/to/my/module"
import "./path/to/my/module";
```
In this case, the name of the symbol is assumed to be `main` and the alias is assumed to be the module's filename so that the above is equivalent to
```zokrates
from "./path/to/my/module" import main as module
from "./path/to/my/module" import main as module;
// `main` is now in scope under the alias `module`.
```
@ -59,12 +59,12 @@ Constants declared with the `const` keyword are imported by name.
You can import a resource in the same folder directly, like this:
```zokrates
from "./mycode" import foo
from "./mycode" import foo;
```
Imports up the file-system tree are supported:
```zokrates
from "../../../mycode" import foo
from "../../../mycode" import foo;
```
### Absolute Imports

View file

@ -0,0 +1,11 @@
## Logging
ZoKrates supports a command to log messages during execution in the interpreter.
The values of expressions can be checked and basic string interpolation is supported:
```zokrates
{{#include ../../../zokrates_cli/examples/book/logging.zok}}
```
By default, logs get removed during compilation. In order to include them in the compiled program, the `--debug` flag has to be enabled.

View file

@ -3,21 +3,21 @@
The following table lists the precedence and associativity of all operators. Operators are listed top to bottom, in ascending precedence. Operators in the same cell have the same precedence. Operators are binary, unless the syntax is provided.
| Operator | Description | `field` | `u8/u16` `u32/u64` | `bool` | Associativity |
|----------------------------|------------------------------------------------------------|------------------------------|-------------------------------|-----------------------------|---------------|
| `**`<br> | Power | &check;[^1] | &nbsp; | &nbsp; | Left |
| `+x`<br>`-x`<br>`!x`<br> | Positive<br>Negative<br>Negation<br> | &check;<br>&check;<br>&nbsp; | &check;<br>&check;<br>&check; | &nbsp;<br>&nbsp;<br>&check; | Right |
| `*`<br>`/`<br>`%`<br> | Multiplication<br> Division<br> Remainder<br> | &check;<br>&check;<br>&nbsp; | &check;<br>&check;<br>&check; | &nbsp;<br>&nbsp;<br>&nbsp; | Left |
| `+`<br>`-`<br> | Addition<br> Subtraction<br> | &check; | &check; | &nbsp; | Left |
| `<<`<br>`>>`<br> | Left shift<br> Right shift<br> | &nbsp; | &check;[^2] | &nbsp; | Left |
| `&` | Bitwise AND | &nbsp; | &check; | &nbsp; | Left |
| <code>&#124;</code> | Bitwise OR | &nbsp; | &check; | &nbsp; | Left |
| `^` | Bitwise XOR | &nbsp; | &check; | &nbsp; | Left |
| `>=`<br>`>`<br>`<=`<br>`<` | Greater or equal<br>Greater<br>Lower or equal<br>Lower<br> | &check;[^3] | &check; | &nbsp; | Left |
| `!=`<br>`==`<br> | Not Equal<br>Equal<br> | &check; | &check; | &check; | Left |
| `&&` | Boolean AND | &nbsp; | &nbsp; | &check; | Left |
| <code>&#124;&#124;</code> | Boolean OR | &nbsp; | &nbsp; | &check; | Left |
| `c ? x : y`<br><br>`if c then x else y fi` | Conditional expression | &check; | &check; | &check; | Right | |
| Operator | Description | `field` | `u8/u16` `u32/u64` | `bool` | Associativity |
|--------------------------------------------|------------------------------------------------------------|------------------------------|-------------------------------|-----------------------------|---------------|
| `**`<br> | Power | &check;[^1] | &nbsp; | &nbsp; | Left |
| `+x`<br>`-x`<br>`!x`<br> | Positive<br>Negative<br>Negation<br> | &check;<br>&check;<br>&nbsp; | &check;<br>&check;<br>&check; | &nbsp;<br>&nbsp;<br>&check; | Right |
| `*`<br>`/`<br>`%`<br> | Multiplication<br> Division<br> Remainder<br> | &check;<br>&check;<br>&nbsp; | &check;<br>&check;<br>&check; | &nbsp;<br>&nbsp;<br>&nbsp; | Left |
| `+`<br>`-`<br> | Addition<br> Subtraction<br> | &check; | &check; | &nbsp; | Left |
| `<<`<br>`>>`<br> | Left shift<br> Right shift<br> | &nbsp; | &check;[^2] | &nbsp; | Left |
| `&` | Bitwise AND | &nbsp; | &check; | &nbsp; | Left |
| <code>&#124;</code> | Bitwise OR | &nbsp; | &check; | &nbsp; | Left |
| `^` | Bitwise XOR | &nbsp; | &check; | &nbsp; | Left |
| `>=`<br>`>`<br>`<=`<br>`<` | Greater or equal<br>Greater<br>Lower or equal<br>Lower<br> | &check;[^3] | &check; | &nbsp; | Left |
| `!=`<br>`==`<br> | Not Equal<br>Equal<br> | &check; | &check; | &check; | Left |
| `&&` | Boolean AND | &nbsp; | &nbsp; | &check; | Left |
| <code>&#124;&#124;</code> | Boolean OR | &nbsp; | &nbsp; | &check; | Left |
| `c ? x : y`<br><br>`if c { x } else { y }` | Conditional expression | &check; | &check; | &check; | Right |
[^1]: The exponent must be a compile-time constant of type `u32`

View file

@ -71,8 +71,8 @@ ZoKrates offers a special shorthand syntax to initialize an array with a constan
The following code provides examples for declaration and initialization:
```zokrates
field[3] a = [1, 2, 3] // initialize a field array with field values
bool[13] b = [false; 13] // initialize a bool array with value false
field[3] a = [1, 2, 3]; // initialize a field array with field values
bool[13] b = [false; 13]; // initialize a bool array with value false
```
#### Multidimensional Arrays
@ -95,16 +95,16 @@ ZoKrates provides some syntactic sugar to retrieve subsets of arrays.
The spread operator `...` applied to an array copies the elements of the existing array.
This can be used to conveniently compose new arrays, as shown in the following example:
```
field[3] a = [1, 2, 3]
field[4] c = [...a, 4] // initialize an array copying values from `a`, followed by 4
field[3] a = [1, 2, 3];
field[4] c = [...a, 4]; // initialize an array copying values from `a`, followed by 4
```
##### Slices
An array can also be assigned to by creating a copy of a subset of an existing array.
This operation is called slicing, and the following example shows how to slice in ZoKrates:
```
field[3] a = [1, 2, 3]
field[2] b = a[1..3] // initialize an array copying a slice from `a`
field[3] a = [1, 2, 3];
field[2] b = a[1..3]; // initialize an array copying a slice from `a`
```
### Tuples
@ -132,8 +132,8 @@ A struct definition starts with the `struct` keyword followed by a name. Afterwa
```zokrates
struct Point {
field x
field y
field x;
field y;
}
```

View file

@ -10,6 +10,13 @@ Variables need to be declared to be used. Declaration and definition are always
{{#include ../../../zokrates_cli/examples/book/declaration.zok}}
```
### Mutability
Variables are immutable by default. In order to declare a mutable variable, the `mut` keyword is used.
```zokrates
{{#include ../../../zokrates_cli/examples/book/mutable.zok}}
```
### Shadowing
Shadowing is not allowed.

View file

@ -59,11 +59,9 @@ In this example, the ABI specification is:
"type":"field"
}
],
"outputs":[
{
"type":"field"
}
]
"output": {
"type":"field"
}
}
```

View file

@ -28,7 +28,6 @@ ZoKrates supports different proving schemes. We identify the schemes by the refe
| [G16](https://eprint.iacr.org/2016/260) | `--proving-scheme g16` | ALTBN_128, BLS12_381 | No |
| [GM17](https://eprint.iacr.org/2017/540) | `--proving-scheme gm17` | ALTBN_128, BLS12_381, BLS12_377, BW6_761 | No |
| [Marlin](https://eprint.iacr.org/2019/1047) | `--proving-scheme marlin` | ALTBN_128, BLS12_381, BLS12_377, BW6_761 | Yes |
| [PGHR13](https://eprint.iacr.org/2013/279) | `--proving-scheme pghr13` | ALTBN_128 | No |
All schemes have a circuit-specific setup phase called `setup`. Universal schemes also feature a preliminary, circuit-agnostic step called `universal-setup`. The advantage of universal schemes is that only the `universal-setup` step requires trust, so that it can be run a single time and reused trustlessly for many programs.
@ -48,7 +47,6 @@ ZoKrates supports multiple backends. The options are the following:
| Backend | CLI flag | Proving schemes | Curves |
| ---- | -------- |-------------------|------------------------------------------|
| Bellman | `--backend bellman` | G16 | ALTBN_128, BLS12_381 |
| Libsnark | `--backend libsnark` | GM17, PGHR13 | ALTBN_128 |
| Ark | `--backend ark` | G16, GM17, MARLIN | ALTBN_128, BLS12_381, BLS12_377, BW6_761 |
Default: `bellman`
@ -59,12 +57,6 @@ When not using the default, the CLI flag has to be provided for the following co
- `generate-proof`
- `verify`
To include libsnark in the build, compile ZoKrates from [source](https://github.com/ZoKrates/ZoKrates/) with the `libsnark` feature:
```bash
cargo +nightly -Z package-features build --release --package zokrates_cli --features="libsnark"
```
Note, that this is only tested for Linux. If you are on another OS, consider using our Docker container, which includes a libsnark installation.
## G16 malleability
When using G16, developers should pay attention to the fact that an attacker, seeing a valid proof, can very easily generate a different but still valid proof. Therefore, depending on the use case, making sure on chain that the same proof cannot be submitted twice may *not* be enough to guarantee that attackers cannot replay proofs. Mechanisms to solve this issue include:

View file

@ -10,7 +10,7 @@ npm install zokrates-js
##### 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/) (`experiments.asyncWebAssembly` must be enabled).
Currently the only known bundler known to be fully compatible with `zokrates-js` is [Webpack](https://webpack.js.org/) (`experiments.syncWebAssembly` must be enabled).
The choice of this default was done to reflect the trends of the JS ecosystem.
```js
import { initialize } from 'zokrates-js';
@ -24,7 +24,7 @@ const { initialize } = require('zokrates-js')
## Example
```js
initialize().then((zokratesProvider) => {
const source = "def main(private field a) -> field: return a * a";
const source = "def main(private field a) -> field { return a * a; }";
// compilation
const artifacts = zokratesProvider.compile(source);
@ -88,7 +88,7 @@ Returns: `CompilationArtifacts`
Compilation:
```js
const artifacts = zokratesProvider.compile("def main() -> (): return");
const artifacts = zokratesProvider.compile("def main() { return; }");
```
Compilation with custom options:
@ -99,7 +99,7 @@ const options = {
resolveCallback: (currentLocation, importLocation) => {
console.log(currentLocation + ' is importing ' + importLocation);
return {
source: "def main() -> (): return",
source: "def main() { return; }",
location: importLocation
};
}
@ -135,13 +135,13 @@ Returns: `ComputationResult`
**Example:**
```js
const code = 'def main(private field a) -> (field): return a * a';
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"]
console.log(output); // Computation output: "4"
```
##### setup(program)

View file

@ -0,0 +1,17 @@
[package]
name = "zokrates_circom"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zokrates_core = { version = "0.7", path = "../zokrates_core", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
zokrates_field = { version = "0.5.0", path = "../zokrates_field", default-features = false }
byteorder = "1.4.3"
[dev-dependencies]
pretty_assertions = "1.2.1"
zkutil = "0.5.0"
bellman_ce = { version = "^0.3" }

View file

@ -0,0 +1,90 @@
mod r1cs;
mod witness;
pub use r1cs::write_r1cs;
pub use witness::write_witness;
#[cfg(test)]
mod tests {
use std::io::Cursor;
use bellman_ce::pairing::bn256::Bn256;
use zkutil::circom_circuit::{
create_rng, generate_random_parameters, prove, r1cs_from_bin, witness_from_bin,
CircomCircuit,
};
use zokrates_ast::{
flat::{Parameter, Variable},
ir::{LinComb, Prog, PublicInputs, QuadComb, Statement, Witness},
};
use zokrates_field::Bn128Field;
use super::*;
#[test]
fn setup_and_prove() {
let prog: Prog<Bn128Field> = Prog {
arguments: vec![
Parameter::private(Variable::new(0)),
Parameter::public(Variable::new(1)),
],
return_count: 1,
statements: vec![
Statement::Constraint(
QuadComb::from_linear_combinations(
LinComb::from(Variable::new(0)),
LinComb::from(Variable::new(0)),
),
LinComb::from(Variable::new(0)),
None,
),
Statement::Constraint(
(LinComb::from(Variable::new(0)) + LinComb::from(Variable::new(1))).into(),
Variable::public(0).into(),
None,
),
],
};
let mut r1cs = vec![];
write_r1cs(&mut r1cs, prog).unwrap();
let public_inputs: PublicInputs = vec![Variable::new(1)].into_iter().collect();
let witness: Witness<Bn128Field> = Witness(
vec![
(Variable::new(0), Bn128Field::from(1u32)),
(Variable::new(1), Bn128Field::from(1u32)),
(Variable::one(), Bn128Field::from(1u32)),
(Variable::public(0), Bn128Field::from(2u32)),
]
.into_iter()
.collect(),
);
let mut wtns = vec![];
write_witness(&mut wtns, witness, public_inputs).unwrap();
let (r1cs, mapping) = r1cs_from_bin(Cursor::new(r1cs)).unwrap();
let wtns = witness_from_bin::<Bn256, _>(Cursor::new(wtns)).unwrap();
let rng = create_rng();
let circuit = CircomCircuit {
r1cs: r1cs.clone(),
witness: None,
wire_mapping: None,
};
let params = generate_random_parameters(circuit, rng).unwrap();
let circuit = CircomCircuit {
r1cs,
witness: Some(wtns),
wire_mapping: Some(mapping),
};
let rng = create_rng();
assert!(prove(circuit, &params, rng).is_ok());
}
}

418
zokrates_circom/src/r1cs.rs Normal file
View file

@ -0,0 +1,418 @@
use byteorder::{LittleEndian, WriteBytesExt};
use std::collections::HashMap;
use std::io::Result;
use std::{io::Write, ops::Add};
use zokrates_ast::flat::Variable;
use zokrates_ast::ir::{Prog, Statement};
use zokrates_field::Field;
struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub n_wires: u32,
pub n_pub_out: u32,
pub n_pub_in: u32,
pub n_prv_in: u32,
pub n_labels: u64,
pub n_constraints: u32,
}
type LinComb<T> = Vec<(usize, T)>;
type Constraint<T> = (LinComb<T>, LinComb<T>, LinComb<T>);
fn write_header<W: Write>(writer: &mut W, header: Header) -> Result<()> {
writer.write_u32::<LittleEndian>(header.field_size)?;
writer.write_all(&header.prime_size)?;
writer.write_u32::<LittleEndian>(header.n_wires)?;
writer.write_u32::<LittleEndian>(header.n_pub_out)?;
writer.write_u32::<LittleEndian>(header.n_pub_in)?;
writer.write_u32::<LittleEndian>(header.n_prv_in)?;
writer.write_u64::<LittleEndian>(header.n_labels)?;
writer.write_u32::<LittleEndian>(header.n_constraints)?;
Ok(())
}
/// Returns the index of `var` in `variables`, adding `var` with incremented index if it does not yet exists.
///
/// # Arguments
///
/// * `variables` - A mutual map that maps all existing variables to their index.
/// * `var` - Variable to be searched for.
pub fn provide_variable_idx(variables: &mut HashMap<Variable, usize>, var: &Variable) -> usize {
let index = variables.len();
*variables.entry(*var).or_insert(index)
}
/// Calculates one R1CS row representation of a program and returns (V, A, B, C) so that:
/// * `V` contains all used variables and the index in the vector represents the used number in `A`, `B`, `C`
/// * `<A,x>*<B,x> = <C,x>` for a witness `x`
///
/// # Arguments
///
/// * `prog` - The program the representation is calculated for.
pub fn r1cs_program<T: Field>(prog: Prog<T>) -> (Vec<Variable>, usize, Vec<Constraint<T>>) {
let mut variables: HashMap<Variable, usize> = HashMap::new();
provide_variable_idx(&mut variables, &Variable::one());
for i in 0..prog.return_count {
provide_variable_idx(&mut variables, &Variable::public(i));
}
for x in prog.arguments.iter().filter(|p| !p.private) {
provide_variable_idx(&mut variables, &x.id);
}
// position where private part of witness starts
let private_inputs_offset = variables.len();
// first pass through statements to populate `variables`
for (quad, lin) in prog.statements.iter().filter_map(|s| match s {
Statement::Constraint(quad, lin, _) => Some((quad, lin)),
Statement::Directive(..) => None,
Statement::Log(..) => None,
}) {
for (k, _) in &quad.left.0 {
provide_variable_idx(&mut variables, k);
}
for (k, _) in &quad.right.0 {
provide_variable_idx(&mut variables, k);
}
for (k, _) in &lin.0 {
provide_variable_idx(&mut variables, k);
}
}
let mut constraints = vec![];
// second pass to convert program to raw sparse vectors
for (quad, lin) in prog.statements.into_iter().filter_map(|s| match s {
Statement::Constraint(quad, lin, _) => Some((quad, lin)),
Statement::Directive(..) => None,
Statement::Log(..) => None,
}) {
constraints.push((
quad.left
.0
.into_iter()
.map(|(k, v)| (*variables.get(&k).unwrap(), v))
.collect(),
quad.right
.0
.into_iter()
.map(|(k, v)| (*variables.get(&k).unwrap(), v))
.collect(),
lin.0
.into_iter()
.map(|(k, v)| (*variables.get(&k).unwrap(), v))
.collect(),
));
}
// Convert map back into list ordered by index
let mut variables_list = vec![Variable::new(0); variables.len()];
for (k, v) in variables.drain() {
assert_eq!(variables_list[v], Variable::new(0));
variables_list[v] = k;
}
(variables_list, private_inputs_offset, constraints)
}
pub fn write_r1cs<T: Field, W: Write>(writer: &mut W, p: Prog<T>) -> Result<()> {
let modulo_byte_count = T::max_value().to_biguint().add(1u32).to_bytes_le().len() as u32;
let n_pub_out = p.return_count as u32;
let n_pub_in = p.arguments.iter().filter(|a| !a.private).count() as u32;
let n_prv_in = p.arguments.iter().filter(|a| a.private).count() as u32;
let (vars, _, constraints) = r1cs_program(p);
let n_wires = vars.len();
let header = Header {
field_size: modulo_byte_count,
prime_size: T::max_value().to_biguint().add(1u32).to_bytes_le(),
n_wires: n_wires as u32,
n_pub_out,
n_pub_in,
n_prv_in,
n_labels: n_wires as u64,
n_constraints: constraints.len() as u32,
};
// magic
writer.write_all(&[0x72, 0x31, 0x63, 0x73])?;
// version
writer.write_u32::<LittleEndian>(1)?;
// section count
writer.write_u32::<LittleEndian>(3)?;
// section type: constraints
// type
writer.write_u32::<LittleEndian>(2)?;
// size: 4 per lc + (32 + 4) per summand
let size = constraints
.iter()
.map(|(a, b, c)| {
(3 * 4 // for each lc, 4 bytes for its size
+ (a.len() + b.len() + c.len()) // for each summand
* (modulo_byte_count as usize + 4)) // 4 bytes for the signal, `modulo_byte_count` bytes for the coefficient
as u64
})
.sum();
writer.write_u64::<LittleEndian>(size)?;
write_constraints(writer, constraints)?;
// section type: header
// type
writer.write_u32::<LittleEndian>(1)?;
// size
writer.write_u64::<LittleEndian>(32 + 32)?;
// header
write_header(writer, header)?;
// section type: wire2label
// type
writer.write_u32::<LittleEndian>(3)?;
// size
writer.write_u64::<LittleEndian>(n_wires as u64 * 8)?;
write_table(writer, &vars)?;
Ok(())
}
fn write_constraints<T: Field, W: Write>(
writer: &mut W,
constraints: Vec<Constraint<T>>,
) -> Result<()> {
for c in constraints {
write_lincomb(writer, c.0)?;
write_lincomb(writer, c.1)?;
write_lincomb(writer, c.2)?;
}
Ok(())
}
fn write_lincomb<T: Field, W: Write>(writer: &mut W, l: LinComb<T>) -> Result<()> {
writer.write_u32::<LittleEndian>(l.len() as u32)?;
for (var, coeff) in l {
writer.write_u32::<LittleEndian>(var as u32)?;
let mut res = vec![0u8; 32];
for (value, padded) in coeff.to_biguint().to_bytes_le().iter().zip(res.iter_mut()) {
*padded = *value;
}
writer.write_all(&res)?;
}
Ok(())
}
// for now we do not write any signal map
fn write_table<W: Write>(w: &mut W, variables: &[Variable]) -> Result<()> {
for (i, _) in variables.iter().enumerate() {
w.write_u64::<LittleEndian>(i as u64)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
use pretty_assertions::assert_eq;
use zkutil::r1cs_reader;
use zokrates_ast::{
flat::{Parameter, Variable},
ir::{LinComb, QuadComb, Statement},
};
use zokrates_field::Bn128Field;
#[test]
fn empty() {
let prog: Prog<Bn128Field> = Prog::default();
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x72, 0x31, 0x63, 0x73,
// version
0x01, 0x00, 0x00, 0x00,
// section count
0x03, 0x00, 0x00, 0x00,
// constraints section (empty)
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// n wires
0x01, 0x00, 0x00, 0x00,
// n pub outputs
0x00, 0x00, 0x00, 0x00,
// n pub inputs
0x00, 0x00, 0x00, 0x00,
// n priv
0x00, 0x00, 0x00, 0x00,
// n labels
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// n constraints
0x00, 0x00, 0x00, 0x00,
// wire map (variable one?)
0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_r1cs(&mut buf, prog).unwrap();
assert_eq!(buf, expected);
let c = Cursor::new(buf);
assert!(r1cs_reader::read(c).is_ok());
}
#[test]
fn return_one() {
let prog: Prog<Bn128Field> = Prog {
arguments: vec![],
return_count: 1,
statements: vec![Statement::Constraint(
LinComb::one().into(),
Variable::public(0).into(),
None,
)],
};
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
0x72, 0x31, 0x63, 0x73,
0x01, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size = 1 constraint = sum(4 /* write term_count_i */ + term_count_i * (4 + 32)) = 120
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x00, 0x00, 0x00, 0x00, // variable 0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x00, 0x00, 0x00, 0x00, // variable 0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x01, 0x00, 0x00, 0x00, // variable 1
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
// header
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulo size
0x20, 0x00, 0x00, 0x00,
// modulo
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
// wire map (one, pub0)
0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_r1cs(&mut buf, prog).unwrap();
assert_eq!(buf, expected);
let c = Cursor::new(buf);
assert!(r1cs_reader::read(c).is_ok());
}
#[test]
fn with_inputs() {
let prog: Prog<Bn128Field> = Prog {
arguments: vec![
Parameter::private(Variable::new(0)),
Parameter::public(Variable::new(1)),
],
return_count: 1,
statements: vec![
Statement::Constraint(
QuadComb::from_linear_combinations(
LinComb::from(Variable::new(0)),
LinComb::from(Variable::new(0)),
),
LinComb::from(Variable::new(0)),
None,
),
Statement::Constraint(
(LinComb::from(Variable::new(0)) + LinComb::from(Variable::new(1))).into(),
Variable::public(0).into(),
None,
),
],
};
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
0x72, 0x31, 0x63, 0x73,
0x01, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// first constraint
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
// second constraint
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x00, 0x00, 0x00, 0x00, // variable 0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x02, 0x00, 0x00, 0x00, // 2 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x02, 0x00, 0x00, 0x00, // variable 2
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x01, 0x00, 0x00, 0x00, // variable 1
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
// header
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulo size
0x20, 0x00, 0x00, 0x00,
// modulo
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
0x04, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
// wire map (one, ~out_0, _1, _0)
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_r1cs(&mut buf, prog).unwrap();
assert_eq!(buf, expected);
let c = Cursor::new(buf);
assert!(r1cs_reader::read(c).is_ok());
}
}

View file

@ -0,0 +1,219 @@
use std::{
io::{Result, Write},
ops::Add,
};
use byteorder::{LittleEndian, WriteBytesExt};
use zokrates_ast::{
flat::Variable,
ir::{PublicInputs, Witness},
};
use zokrates_field::Field;
pub struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub witness_size: u32,
}
fn write_header<W: Write>(writer: &mut W, header: Header) -> Result<()> {
writer.write_u32::<LittleEndian>(header.field_size)?;
writer.write_all(&header.prime_size)?;
writer.write_u32::<LittleEndian>(header.witness_size)?;
Ok(())
}
pub fn write_witness<T: Field, W: Write>(
writer: &mut W,
w: Witness<T>,
public_inputs: PublicInputs,
) -> Result<()> {
let modulo_byte_count = T::max_value().to_biguint().add(1u32).to_bytes_le().len() as u32;
let witness_size = w.0.len() as u32;
let header = Header {
field_size: modulo_byte_count,
prime_size: T::max_value().to_biguint().add(1u32).to_bytes_le(),
witness_size,
};
// magic "wtns"
writer.write_all(&[0x77, 0x74, 0x6e, 0x73])?;
// version
writer.write_u32::<LittleEndian>(2)?;
// section count
writer.write_u32::<LittleEndian>(2)?;
// section type: header
// type
writer.write_u32::<LittleEndian>(1)?;
// size
writer.write_u64::<LittleEndian>(8 + modulo_byte_count as u64)?;
// header
write_header(writer, header)?;
// section type: witness
// type
writer.write_u32::<LittleEndian>(2)?;
// size: `modulo_byte_count` per witness value
let size = witness_size as u64 * modulo_byte_count as u64;
writer.write_u64::<LittleEndian>(size)?;
write_witness_values(writer, w, public_inputs)?;
Ok(())
}
fn write_val<T: Field, W: Write>(writer: &mut W, v: &T, modulo_byte_count: usize) -> Result<()> {
let mut res = vec![0u8; modulo_byte_count];
for (value, padded) in v.to_biguint().to_bytes_le().iter().zip(res.iter_mut()) {
*padded = *value;
}
writer.write_all(&res)?;
Ok(())
}
fn write_witness_values<T: Field, W: Write>(
writer: &mut W,
mut w: Witness<T>,
public_inputs: PublicInputs,
) -> Result<()> {
let modulo_byte_count = T::max_value().to_biguint().add(1u32).to_bytes_le().len();
if let Some(value) = w.0.remove(&Variable::one()) {
write_val(writer, &value, modulo_byte_count)?;
}
let output_count = w.0.iter().filter(|(var, _)| var.is_output()).count();
for value in (0..output_count).map(|id| w.0.remove(&Variable::public(id)).unwrap()) {
write_val(writer, &value, modulo_byte_count)?;
}
for value in public_inputs.iter().map(|var| w.0.remove(var).unwrap()) {
write_val(writer, &value, modulo_byte_count)?;
}
for (_, val) in w.0.iter() {
write_val(writer, val, modulo_byte_count)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use zokrates_ast::{flat::Variable, ir::PublicInputs};
use zokrates_field::Bn128Field;
#[test]
fn empty() {
let w: Witness<Bn128Field> = Witness::default();
let public_inputs: PublicInputs = Default::default();
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x77, 0x74, 0x6e, 0x73,
// version
0x02, 0x00, 0x00, 0x00,
// section count
0x02, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// witness size
0x00, 0x00, 0x00, 0x00,
// witness section (empty)
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_witness(&mut buf, w, public_inputs).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn one_value() {
let mut w: Witness<Bn128Field> = Witness::default();
let public_inputs: PublicInputs = Default::default();
w.0.insert(Variable::public(0), 1.into());
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x77, 0x74, 0x6e, 0x73,
// version
0x02, 0x00, 0x00, 0x00,
// section count
0x02, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// witness size
0x01, 0x00, 0x00, 0x00,
// witness section
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// values
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_witness(&mut buf, w, public_inputs).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn one_and_pub_and_priv() {
let mut w: Witness<Bn128Field> = Witness::default();
let public_inputs: PublicInputs = vec![Variable::new(1)].into_iter().collect();
w.0.extend(vec![
(Variable::public(0), 42.into()),
(Variable::one(), 1.into()),
(Variable::new(0), 43.into()),
(Variable::new(1), 44.into()),
]);
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x77, 0x74, 0x6e, 0x73,
// version
0x02, 0x00, 0x00, 0x00,
// section count
0x02, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// witness size
0x04, 0x00, 0x00, 0x00,
// witness section
0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// values: ordering should be [one, ~out_0, _1, _0] because _1 is public
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_witness(&mut buf, w, public_inputs).unwrap();
assert_eq!(buf, expected);
}
}

View file

@ -1,15 +1,14 @@
[package]
name = "zokrates_cli"
version = "0.7.14"
version = "0.8.0"
authors = ["Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>", "Dennis Kuhnert <mail@kyroy.com>", "Thibaut Schaeffer <thibaut@schaeff.fr>"]
repository = "https://github.com/Zokrates/ZoKrates.git"
edition = "2018"
[features]
default = ["bellman", "ark"]
libsnark = ["zokrates_core/libsnark", "zokrates_common/libsnark"]
bellman = ["zokrates_core/bellman", "zokrates_common/bellman"]
ark = ["zokrates_core/ark", "zokrates_common/ark"]
bellman = ["zokrates_bellman", "zokrates_core/bellman", "zokrates_common/bellman"]
ark = ["zokrates_ark", "zokrates_core/ark", "zokrates_common/ark"]
[dependencies]
log = "0.4"
@ -20,10 +19,13 @@ serde_cbor = "0.11.2"
regex = "0.2"
zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false }
zokrates_abi = { version = "0.1", path = "../zokrates_abi" }
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
zokrates_core = { version = "0.7", path = "../zokrates_core", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
zokrates_interpreter = { version = "0.1", path = "../zokrates_interpreter", default-features = false }
zokrates_circom = { version = "0.1", path = "../zokrates_circom", default-features = false }
typed-arena = "1.4.1"
zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"}
zokrates_common = { version = "0.1", path = "../zokrates_common" }
zokrates_common = { version = "0.1", path = "../zokrates_common", default-features = false }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde = { version = "1.0", features = ["derive"] }
dirs = "3.0.1"
@ -35,6 +37,11 @@ hex = "0.3.1"
blake2 = "0.8.1"
sha2 = "0.10.0"
# Backends
zokrates_proof_systems = { version = "0.1", path = "../zokrates_proof_systems", default-features = false }
zokrates_ark = { version = "0.1", path = "../zokrates_ark", default-features = false, optional = true }
zokrates_bellman = { version = "0.1", path = "../zokrates_bellman", default-features = false, optional = true }
[dev-dependencies]
glob = "0.2.11"
assert_cli = "0.5"

View file

@ -1,6 +1,8 @@
// only using add, no need to flatten
def main(field a) -> field:
field b = a + 5
field c = a + b + a + 4
field d = a + c + a + b
return b + c + d
def main(field a) -> field {
field b = a + 5;
field c = a + b + a + 4;
field d = a + c + a + b;
return b + c + d;
}

View file

@ -1,13 +1,15 @@
type byte = u8
type uint32 = u32
type UInt32Array<N> = uint32[N]
type byte = u8;
type uint32 = u32;
type UInt32Array<N> = uint32[N];
type matrix<R, C> = field[R][C]
type matrix<R, C> = field[R][C];
def fill<R, C>(field v) -> matrix<R, C>:
return [[v; C]; R]
def fill<R, C>(field v) -> matrix<R, C> {
return [[v; C]; R];
}
def main(uint32 a, uint32 b) -> (UInt32Array<2>, matrix<2, 4>):
UInt32Array<2> res = [a, b]
matrix<2, 4> m = fill(1)
return res, m
def main(uint32 a, uint32 b) -> (UInt32Array<2>, matrix<2, 4>) {
UInt32Array<2> res = [a, b];
matrix<2, 4> m = fill(1);
return (res, m);
}

View file

@ -1,14 +1,16 @@
from "./basic_aliasing.zok" import matrix
from "./struct_aliasing.zok" import Buzz
from "./basic_aliasing.zok" import matrix;
from "./struct_aliasing.zok" import Buzz;
const u32 R = 2
const u32 C = 4
const u32 R = 2;
const u32 C = 4;
type matrix_2x4 = matrix<R, C>
type matrix_2x4 = matrix<R, C>;
def buzz<N>() -> Buzz<N>:
return Buzz { a: [0; N], b: [0; N] }
def buzz<N>() -> Buzz<N> {
return Buzz { a: [0; N], b: [0; N] };
}
def main(matrix_2x4 m) -> (Buzz<2>, matrix_2x4):
Buzz<2> b = buzz::<2>()
return b, m
def main(matrix_2x4 m) -> (Buzz<2>, matrix_2x4) {
Buzz<2> b = buzz::<2>();
return (b, m);
}

View file

@ -1,15 +1,16 @@
type FieldArray<N> = field[N]
type FieldArray<N> = field[N];
struct Foo<A, B> {
FieldArray<A> a
FieldArray<B> b
FieldArray<A> a;
FieldArray<B> b;
}
type Bar = Foo<2, 2>
type Buzz<A> = Foo<A, A>
type Bar = Foo<2, 2>;
type Buzz<A> = Foo<A, A>;
def main(Bar a) -> Buzz<2>:
Bar bar = Bar { a: [1, 2], b: [1, 2] }
Buzz<2> buzz = Buzz { a: [1, 2], b: [1, 2] }
assert(bar == buzz)
return buzz
def main(Bar a) -> Buzz<2> {
Bar bar = Bar { a: [1, 2], b: [1, 2] };
Buzz<2> buzz = Buzz { a: [1, 2], b: [1, 2] };
assert(bar == buzz);
return buzz;
}

View file

@ -1,6 +1,8 @@
def sub(field a) -> field:
a = a + 3
return a
def sub(field mut a) -> field {
a = a + 3;
return a;
}
def main() -> field:
return sub(4)
def main() -> field {
return sub(4);
}

View file

@ -1,11 +1,12 @@
def myFct<N, N2>(u64[N] ignored) -> u64[N2]:
assert(2*N == N2)
return [0; N2]
def myFct<N, N2>(u64[N] ignored) -> u64[N2] {
assert(2*N == N2);
return [0; N2];
}
const u32 N = 3
const u32 N = 3;
const u32 N2 = 2 * N;
const u32 N2 = 2*N
def main(u64[N] arg) -> bool:
u64[N2] someVariable = myFct(arg)
return true
def main(u64[N] arg) -> bool {
u64[N2] someVariable = myFct(arg);
return true;
}

View file

@ -1,8 +1,11 @@
def foo(field[2] a) -> bool:
return true
def foo(field[2] a) -> bool {
return true;
}
def foo(field[1] a) -> bool:
return true
def foo(field[1] a) -> bool {
return true;
}
def main() -> bool:
return foo([1])
def main() -> bool {
return foo([1]);
}

View file

@ -1,2 +1,3 @@
def main(field[3] a) -> field:
return a[0] + a[1] + a[2]
def main(field[3] a) -> field {
return a[0] + a[1] + a[2];
}

View file

@ -1,7 +1,8 @@
def main() -> u32:
u32[3] a = [1, 2, 3]
u32 c = 0
for u32 i in 0..3 do
c = c + a[i]
endfor
return c
def main() -> u32 {
u32[3] a = [1, 2, 3];
u32 mut c = 0;
for u32 i in 0..3 {
c = c + a[i];
}
return c;
}

View file

@ -1,7 +1,8 @@
def main() -> (u32[3]):
u32[3] a = [1, 2, 3]
u32[3] c = [4, 5, 6]
for u32 i in 0..3 do
c[i] = c[i] + a[i]
endfor
return c
def main() -> u32[3] {
u32[3] a = [1, 2, 3];
u32[3] mut c = [4, 5, 6];
for u32 i in 0..3 {
c[i] = c[i] + a[i];
}
return c;
}

View file

@ -1,9 +1,10 @@
def main(bool[3] a) -> (field[3]):
bool[3] c = [true, true || false, true]
a[1] = true || a[2]
a[2] = a[0]
field[3] result = [0; 3]
for u32 i in 0..3 do
result[i] = if a[i] then 33 else 0 fi
endfor
return result
def main(bool[3] mut a) -> field[3] {
bool[3] c = [true, true || false, true];
a[1] = true || a[2];
a[2] = a[0];
field[3] mut result = [0; 3];
for u32 i in 0..3 {
result[i] = a[i] ? 33 : 0;
}
return result;
}

Some files were not shown because too many files have changed in this diff Show more