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

Merge branch 'develop' of github.com:Zokrates/ZoKrates into accept-explicit-generics

This commit is contained in:
schaeff 2021-04-20 10:55:54 +02:00
commit 7b452db760
296 changed files with 6130 additions and 827 deletions

View file

@ -11,6 +11,7 @@ sudo: required
env:
global:
- CRATE_NAME=zokrates
matrix:
include:
@ -67,7 +68,7 @@ branches:
only:
# release tags
- /^\d+\.\d+\.\d+.*$/
- master
- deploy
notifications:
email:

View file

@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
https://github.com/Zokrates/ZoKrates/compare/latest...develop
## [0.7.0] - 2021-04-09
### Release
- https://github.com/Zokrates/ZoKrates/releases/tag/0.7.0
### Changes
- Re-export embed functions as stdlib modules, add field to uint casts to stdlib (#801, @dark64)
- Change left `<<` and right `>>` shifts to take `u32` as a second parameter (#783, @schaeff)
- Introduce u64 type, add keccak{256,384,512} and sha3{256,384,512} hash functions to stdlib (#772, @dark64)
- Add negative `-` and positive `+` unary operators, restricting accepted expressions in some places (exponent) to allow for better parsing (#762, @schaeff)
- Make embed functions generic, enabling unpacking to any width at minimal cost (#754, @schaeff)
- Add global `--verbose` flag to CLI for verbose logging, add `--ztf` flag to `compile` command, deprecate `--light` flag as its behaviour is now a default. (#751, @dark64)
- Introduce constant generics for `u32` values. Introduce literal inference (#695, @schaeff)
## [0.6.4] - 2021-03-19
### Release
- https://github.com/Zokrates/ZoKrates/releases/tag/0.6.4

18
Cargo.lock generated
View file

@ -2258,7 +2258,7 @@ dependencies = [
[[package]]
name = "zokrates_abi"
version = "0.1.3"
version = "0.1.4"
dependencies = [
"serde",
"serde_derive",
@ -2269,7 +2269,7 @@ dependencies = [
[[package]]
name = "zokrates_cli"
version = "0.6.4"
version = "0.7.0"
dependencies = [
"assert_cli",
"bincode",
@ -2294,7 +2294,7 @@ version = "0.1.0"
[[package]]
name = "zokrates_core"
version = "0.5.4"
version = "0.6.0"
dependencies = [
"ark-bls12-377",
"ark-bn254",
@ -2335,7 +2335,7 @@ dependencies = [
[[package]]
name = "zokrates_core_test"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"zokrates_test",
"zokrates_test_derive",
@ -2343,7 +2343,7 @@ dependencies = [
[[package]]
name = "zokrates_embed"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"bellman_ce",
"sapling-crypto_ce",
@ -2351,7 +2351,7 @@ dependencies = [
[[package]]
name = "zokrates_field"
version = "0.3.8"
version = "0.4.0"
dependencies = [
"ark-bls12-377",
"ark-bn254",
@ -2381,7 +2381,7 @@ dependencies = [
[[package]]
name = "zokrates_parser"
version = "0.1.6"
version = "0.2.0"
dependencies = [
"glob 0.2.11",
"pest",
@ -2390,7 +2390,7 @@ dependencies = [
[[package]]
name = "zokrates_pest_ast"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"from-pest",
"glob 0.2.11",
@ -2402,7 +2402,7 @@ dependencies = [
[[package]]
name = "zokrates_stdlib"
version = "0.1.8"
version = "0.2.0"
dependencies = [
"fs_extra",
"zokrates_test",

View file

@ -1,7 +0,0 @@
def id<N>() -> u32:
return N
def main():
assert(id::<5>() == 5)
assert(id::<6>() == 6)
return

View file

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

View file

@ -1 +0,0 @@
Introduce constant generics for `u32` values. Introduce literal inference

View file

@ -1 +0,0 @@
Add global `--verbose` flag to CLI for verbose logging, add `--ztf` flag to `compile` command, deprecate `--light` flag as its behaviour is now a default.

View file

@ -1 +0,0 @@
Make embed functions generic, enabling unpacking to any width at minimal cost

View file

@ -1 +0,0 @@
Change left `<<` and right `>>` shifts to take `u32` as a second parameter

View file

@ -0,0 +1 @@
Allow optional underscore before type suffix (e.g. `42_u32`)

View file

@ -0,0 +1 @@
Add [poseidon](https://www.poseidon-hash.info/) zk-friendly hashing algorithm to stdlib

View file

@ -0,0 +1 @@
Add the ability to import multiple symbols in a single import statement

View file

@ -0,0 +1 @@
Make function selection stricter in function calls

View file

@ -0,0 +1 @@
Detect assertion failures at compile time on constant expressions

View file

@ -28,7 +28,14 @@ main() {
test -f Cargo.lock || cargo generate-lockfile
cross build --bin zokrates --target $TARGET --release
case $TRAVIS_OS_NAME in
linux)
cross build --bin zokrates --package zokrates_cli --features="libsnark" --target $TARGET --release
;;
*)
cross build --bin zokrates --package zokrates_cli --target $TARGET --release
;;
esac
# Package artifacts
# Binary

View file

@ -1,11 +0,0 @@
def foo<N>(field[N] x) -> field[N]:
return x
def bar<N>(field[N] x) -> field[N]:
field[N] r = x
return r
def main(field[3] x) -> field[2]:
field[2] z = foo(x)[0..2]
return bar(z)

View file

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

View file

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

View file

@ -1,12 +1,12 @@
[package]
name = "zokrates_abi"
version = "0.1.3"
version = "0.1.4"
authors = ["Thibaut Schaeffer <thibaut@schaeff.fr>"]
edition = "2018"
[dependencies]
zokrates_field = { version = "0.3", path = "../zokrates_field", default-features = false }
zokrates_core = { version = "0.5", path = "../zokrates_core", default-features = false }
zokrates_field = { version = "0.4", path = "../zokrates_field", default-features = false }
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"

View file

@ -45,6 +45,7 @@ enum Value<T> {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
Field(T),
Boolean(bool),
Array(Vec<Value<T>>),
@ -56,6 +57,7 @@ enum CheckedValue<T> {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
Field(T),
Boolean(bool),
Array(Vec<CheckedValue<T>>),
@ -72,6 +74,7 @@ impl<T: Field> fmt::Display for Value<T> {
Value::U8(v) => write!(f, "{:#04x}", v),
Value::U16(v) => write!(f, "{:#06x}", v),
Value::U32(v) => write!(f, "{:#010x}", v),
Value::U64(v) => write!(f, "{:#018x}", v),
Value::Boolean(v) => write!(f, "{}", v),
Value::Array(v) => write!(
f,
@ -100,6 +103,7 @@ impl<T: Field> Value<T> {
(Value::U8(f), ConcreteType::Uint(UBitwidth::B8)) => Ok(CheckedValue::U8(f)),
(Value::U16(f), ConcreteType::Uint(UBitwidth::B16)) => Ok(CheckedValue::U16(f)),
(Value::U32(f), ConcreteType::Uint(UBitwidth::B32)) => Ok(CheckedValue::U32(f)),
(Value::U64(f), ConcreteType::Uint(UBitwidth::B64)) => Ok(CheckedValue::U64(f)),
(Value::Boolean(b), ConcreteType::Boolean) => Ok(CheckedValue::Boolean(b)),
(Value::Array(a), ConcreteType::Array(array_type)) => {
let size = array_type.size;
@ -162,6 +166,7 @@ impl<T: From<usize>> Encode<T> for CheckedValue<T> {
CheckedValue::U8(t) => vec![T::from(t as usize)],
CheckedValue::U16(t) => vec![T::from(t as usize)],
CheckedValue::U32(t) => vec![T::from(t as usize)],
CheckedValue::U64(t) => vec![T::from(t as usize)],
CheckedValue::Boolean(b) => vec![if b { 1.into() } else { 0.into() }],
CheckedValue::Array(a) => a.into_iter().flat_map(|v| v.encode()).collect(),
CheckedValue::Struct(s) => s.into_iter().flat_map(|(_, v)| v.encode()).collect(),
@ -205,6 +210,9 @@ impl<T: Field> Decode<T> for CheckedValue<T> {
ConcreteType::Uint(UBitwidth::B32) => {
CheckedValue::U32(raw.pop().unwrap().to_dec_string().parse().unwrap())
}
ConcreteType::Uint(UBitwidth::B64) => {
CheckedValue::U64(raw.pop().unwrap().to_dec_string().parse().unwrap())
}
ConcreteType::Boolean => {
let v = raw.pop().unwrap();
CheckedValue::Boolean(if v == 0.into() {
@ -276,6 +284,9 @@ impl<T: Field> TryFrom<serde_json::Value> for Value<T> {
10 => u32::from_str_radix(&s[2..], 16)
.map(Value::U32)
.map_err(|_| format!("Expected u32 value, found {}", s)),
18 => u64::from_str_radix(&s[2..], 16)
.map(Value::U64)
.map_err(|_| format!("Expected u64 value, found {}", s)),
_ => Err(format!("Cannot parse {} to any type", s)),
})
}
@ -306,6 +317,7 @@ impl<T: Field> CheckedValue<T> {
CheckedValue::U8(u) => serde_json::Value::String(format!("{:#04x}", u)),
CheckedValue::U16(u) => serde_json::Value::String(format!("{:#06x}", u)),
CheckedValue::U32(u) => serde_json::Value::String(format!("{:#010x}", u)),
CheckedValue::U64(u) => serde_json::Value::String(format!("{:#018x}", u)),
CheckedValue::Boolean(b) => serde_json::Value::Bool(b),
CheckedValue::Array(a) => {
serde_json::Value::Array(a.into_iter().map(|e| e.into_serde_json()).collect())

View file

@ -13,6 +13,12 @@ from "./path/to/my/module" import MySymbol
// `MySymbol` is now in scope.
```
To import multiple symbols with a single import statement, separate the symbols names with commas:
```zokrates
from "./path/to/my/module" import MySymbol, MyOtherSymbol
```
#### Aliasing
The `as` keyword enables renaming symbols:

View file

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

View file

@ -22,7 +22,7 @@ Note that for field elements, the division operation multiplies the numerator wi
Booleans are available in ZoKrates. When a boolean is used as a parameter of the main function, the program is constrained to only accept `0` or `1` for that parameter. A boolean can be asserted to be true using an `assert(bool)` statement.
### `u8/u16/u32`
### `u8/u16/u32/u64`
Unsigned integers represent positive numbers of the interval `[0, 2 ** bitwidth[`, where `bitwidth` is specified in the type's name, e.g., 32 bits in the case of u32. Their arithmetics are defined modulo `2 ** bitwidth`.
@ -34,9 +34,9 @@ The division operation calculates the standard floor division for integers. The
### Numeric inference
In the case of decimal literals like `42`, the compiler tries to find the appropriate type (`field`, `u8`, `u16` or `u32`) depending on the context. If it cannot converge to a single option, an error is returned. This means that there is no default type for decimal literals.
In the case of decimal literals like `42`, the compiler tries to find the appropriate type (`field`, `u8`, `u16`, `u32` or `u64`) depending on the context. If it cannot converge to a single option, an error is returned. This means that there is no default type for decimal literals.
All operations between literals have the semantics of the infered type.
All operations between literals have the semantics of the inferred type.
```zokrates
{{#include ../../../zokrates_cli/examples/book/numeric_inference.zok}}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_cli"
version = "0.6.4"
version = "0.7.0"
authors = ["Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>", "Dennis Kuhnert <mail@kyroy.com>", "Thibaut Schaeffer <thibaut@schaeff.fr>"]
repository = "https://github.com/JacobEberhardt/ZoKrates.git"
edition = "2018"
@ -16,9 +16,9 @@ cfg-if = "0.1"
clap = "2.26.2"
bincode = "0.8.0"
regex = "0.2"
zokrates_field = { version = "0.3", path = "../zokrates_field", default-features = false }
zokrates_field = { version = "0.4", path = "../zokrates_field", default-features = false }
zokrates_abi = { version = "0.1", path = "../zokrates_abi" }
zokrates_core = { version = "0.5", path = "../zokrates_core", default-features = false }
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"}
serde_json = "1.0"
dirs = "3.0.1"

View file

@ -1,3 +1,3 @@
def main() -> ():
assert(1f == 2f)
assert(1f + 1f == 2f)
return

View file

@ -0,0 +1,3 @@
def main():
assert(1f == 2f)
return

View file

@ -0,0 +1,3 @@
def main():
field a = - - 1
return

View file

@ -0,0 +1,3 @@
def main():
field a = 1**2**3 // parentheses are required here
return

View file

@ -0,0 +1,7 @@
def foo<N>(field[N] inputs) -> bool:
assert(N <= 5)
return true
def main():
bool b = foo([1, 2, 3, 4, 5, 6])
return

View file

@ -0,0 +1,5 @@
def foo() -> field:
return 1
def main() -> field:
return foo(42)

View file

@ -0,0 +1,5 @@
def foo() -> field:
return 1
def main() -> field:
return foo::<42>()

View file

@ -0,0 +1,2 @@
def main(u32 a, u32 b) -> u32:
return a >> b

View file

@ -0,0 +1,4 @@
def main() -> field:
field a = 1f
field b = 1_f // allow underscore between the literal and the suffix
return a + b

View file

@ -0,0 +1,5 @@
from "./bar" import Bar, main as f
def main() -> field:
Bar bar = Bar {}
return f()

View file

@ -2,5 +2,5 @@ def foo() -> field:
return 1
def main():
assert(foo() + (1 + 44*3) == 1)
assert(foo() + (1 + 44*3) == 134)
return

View file

@ -1,5 +1,5 @@
def foo(field a, field b) -> (field, field):
assert(a == b + 2)
assert(a == b)
return a, b
def main() -> field:

View file

@ -1,2 +0,0 @@
def main():
return

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_core"
version = "0.5.4"
version = "0.6.0"
edition = "2018"
authors = ["Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>", "Dennis Kuhnert <mail@kyroy.com>"]
repository = "https://github.com/JacobEberhardt/ZoKrates"
@ -28,10 +28,10 @@ serde_json = "1.0"
bincode = "0.8.0"
hex = "0.4.2"
regex = "0.2"
zokrates_field = { version = "0.3.0", path = "../zokrates_field", default-features = false }
zokrates_pest_ast = { version = "0.1.0", path = "../zokrates_pest_ast" }
zokrates_field = { version = "0.4.0", path = "../zokrates_field", default-features = false }
zokrates_pest_ast = { version = "0.2.0", path = "../zokrates_pest_ast" }
zokrates_common = { path = "../zokrates_common" }
zokrates_embed = { path = "../zokrates_embed" }
zokrates_embed = { version = "0.1.0", path = "../zokrates_embed" }
getrandom = { version = "0.2", features = ["js"] }
rand_0_4 = { version = "0.4", package = "rand" }
rand_0_7 = { version = "0.7", package = "rand" }

View file

@ -16,30 +16,45 @@ impl<'ast> From<pest::File<'ast>> for absy::Module<'ast> {
.map(absy::SymbolDeclarationNode::from),
),
)
.imports(prog.imports.into_iter().map(absy::ImportNode::from))
.imports(
prog.imports
.into_iter()
.map(absy::ImportDirective::from)
.flatten(),
)
}
}
impl<'ast> From<pest::ImportDirective<'ast>> for absy::ImportNode<'ast> {
fn from(import: pest::ImportDirective<'ast>) -> absy::ImportNode<'ast> {
impl<'ast> From<pest::ImportDirective<'ast>> for absy::ImportDirective<'ast> {
fn from(import: pest::ImportDirective<'ast>) -> absy::ImportDirective<'ast> {
use crate::absy::NodeValue;
match import {
pest::ImportDirective::Main(import) => {
pest::ImportDirective::Main(import) => absy::ImportDirective::Main(
imports::Import::new(None, std::path::Path::new(import.source.span.as_str()))
.alias(import.alias.map(|a| a.span.as_str()))
.span(import.span)
}
pest::ImportDirective::From(import) => {
let symbol_str = import.symbol.span.as_str();
imports::Import::new(
Some(import.symbol.span.as_str()),
std::path::Path::new(import.source.span.as_str()),
)
.alias(import.alias.map(|a| a.span.as_str()).or(Some(symbol_str)))
.span(import.span)
}
.span(import.span),
),
pest::ImportDirective::From(import) => absy::ImportDirective::From(
import
.symbols
.iter()
.map(|symbol| {
imports::Import::new(
Some(symbol.symbol.span.as_str()),
std::path::Path::new(import.source.span.as_str()),
)
.alias(
symbol
.alias
.as_ref()
.map(|a| a.span.as_str())
.or_else(|| Some(symbol.symbol.span.as_str())),
)
.span(symbol.span.clone())
})
.collect(),
),
}
}
}
@ -513,10 +528,12 @@ impl<'ast> From<pest::UnaryExpression<'ast>> for absy::ExpressionNode<'ast> {
fn from(unary: pest::UnaryExpression<'ast>) -> absy::ExpressionNode<'ast> {
use crate::absy::NodeValue;
let expression = Box::new(absy::ExpressionNode::from(*unary.expression));
match unary.op {
pest::UnaryOperator::Not(_) => {
absy::Expression::Not(Box::new(absy::ExpressionNode::from(*unary.expression)))
}
pest::UnaryOperator::Not(..) => absy::Expression::Not(expression),
pest::UnaryOperator::Neg(..) => absy::Expression::Neg(expression),
pest::UnaryOperator::Pos(..) => absy::Expression::Pos(expression),
}
.span(unary.span)
}
@ -581,6 +598,9 @@ impl<'ast> From<pest::DecimalLiteralExpression<'ast>> for absy::ExpressionNode<'
pest::DecimalSuffix::Field(_) => absy::Expression::FieldConstant(
BigUint::parse_bytes(&expression.value.span.as_str().as_bytes(), 10).unwrap(),
),
pest::DecimalSuffix::U64(_) => {
absy::Expression::U64Constant(expression.value.span.as_str().parse().unwrap())
}
pest::DecimalSuffix::U32(_) => {
absy::Expression::U32Constant(expression.value.span.as_str().parse().unwrap())
}
@ -605,6 +625,9 @@ impl<'ast> From<pest::HexLiteralExpression<'ast>> for absy::ExpressionNode<'ast>
use crate::absy::NodeValue;
match expression.value {
pest::HexNumberExpression::U64(e) => {
absy::Expression::U64Constant(u64::from_str_radix(&e.span.as_str(), 16).unwrap())
}
pest::HexNumberExpression::U32(e) => {
absy::Expression::U32Constant(u32::from_str_radix(&e.span.as_str(), 16).unwrap())
}
@ -681,6 +704,7 @@ impl<'ast> From<pest::Type<'ast>> for absy::UnresolvedTypeNode<'ast> {
pest::BasicType::U8(t) => UnresolvedType::Uint(8).span(t.span),
pest::BasicType::U16(t) => UnresolvedType::Uint(16).span(t.span),
pest::BasicType::U32(t) => UnresolvedType::Uint(32).span(t.span),
pest::BasicType::U64(t) => UnresolvedType::Uint(64).span(t.span),
},
pest::Type::Array(t) => {
let inner_type = match t.ty {
@ -690,6 +714,7 @@ impl<'ast> From<pest::Type<'ast>> for absy::UnresolvedTypeNode<'ast> {
pest::BasicType::U8(t) => UnresolvedType::Uint(8).span(t.span),
pest::BasicType::U16(t) => UnresolvedType::Uint(16).span(t.span),
pest::BasicType::U32(t) => UnresolvedType::Uint(32).span(t.span),
pest::BasicType::U64(t) => UnresolvedType::Uint(64).span(t.span),
},
pest::BasicOrStructType::Struct(t) => {
UnresolvedType::User(t.span.as_str().to_string()).span(t.span)

View file

@ -18,6 +18,7 @@ pub use crate::absy::variable::{Variable, VariableNode};
use crate::embed::FlatEmbed;
use std::path::{Path, PathBuf};
use crate::imports::ImportDirective;
use crate::imports::ImportNode;
use std::fmt;
@ -495,6 +496,7 @@ pub enum Expression<'ast> {
U8Constant(u8),
U16Constant(u16),
U32Constant(u32),
U64Constant(u64),
Identifier(Identifier<'ast>),
Add(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
Sub(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
@ -502,6 +504,8 @@ pub enum Expression<'ast> {
Div(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
Rem(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
Pow(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
Neg(Box<ExpressionNode<'ast>>),
Pos(Box<ExpressionNode<'ast>>),
IfElse(
Box<ExpressionNode<'ast>>,
Box<ExpressionNode<'ast>>,
@ -541,6 +545,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
Expression::U8Constant(ref i) => write!(f, "{}", i),
Expression::U16Constant(ref i) => write!(f, "{}", i),
Expression::U32Constant(ref i) => write!(f, "{}", i),
Expression::U64Constant(ref i) => write!(f, "{}", i),
Expression::IntConstant(ref i) => write!(f, "{}", i),
Expression::Identifier(ref var) => write!(f, "{}", var),
Expression::Add(ref lhs, ref rhs) => write!(f, "({} + {})", lhs, rhs),
@ -549,6 +554,8 @@ impl<'ast> fmt::Display for Expression<'ast> {
Expression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
Expression::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
Expression::Pow(ref lhs, ref rhs) => write!(f, "({}**{})", lhs, rhs),
Expression::Neg(ref e) => write!(f, "(-{})", e),
Expression::Pos(ref e) => write!(f, "(+{})", e),
Expression::BooleanConstant(b) => write!(f, "{}", b),
Expression::IfElse(ref condition, ref consequent, ref alternative) => write!(
f,
@ -624,6 +631,7 @@ impl<'ast> fmt::Debug for Expression<'ast> {
Expression::U8Constant(ref i) => write!(f, "U8({:x})", i),
Expression::U16Constant(ref i) => write!(f, "U16({:x})", i),
Expression::U32Constant(ref i) => write!(f, "U32({:x})", i),
Expression::U64Constant(ref i) => write!(f, "U64({:x})", i),
Expression::FieldConstant(ref i) => write!(f, "Field({:?})", i),
Expression::IntConstant(ref i) => write!(f, "Int({:?})", i),
Expression::Identifier(ref var) => write!(f, "Ide({})", var),
@ -633,6 +641,8 @@ impl<'ast> fmt::Debug for Expression<'ast> {
Expression::Div(ref lhs, ref rhs) => write!(f, "Div({:?}, {:?})", lhs, rhs),
Expression::Rem(ref lhs, ref rhs) => write!(f, "Rem({:?}, {:?})", lhs, rhs),
Expression::Pow(ref lhs, ref rhs) => write!(f, "Pow({:?}, {:?})", lhs, rhs),
Expression::Neg(ref e) => write!(f, "Neg({:?})", e),
Expression::Pos(ref e) => write!(f, "Pos({:?})", e),
Expression::BooleanConstant(b) => write!(f, "{}", b),
Expression::IfElse(ref condition, ref consequent, ref alternative) => write!(
f,

View file

@ -29,9 +29,11 @@ pub enum FlatEmbed {
U8ToBits,
U16ToBits,
U32ToBits,
U64ToBits,
U8FromBits,
U16FromBits,
U32FromBits,
U64FromBits,
}
impl FlatEmbed {
@ -65,6 +67,12 @@ impl FlatEmbed {
DeclarationType::Boolean,
32usize,
))]),
FlatEmbed::U64ToBits => DeclarationSignature::new()
.inputs(vec![DeclarationType::uint(64)])
.outputs(vec![DeclarationType::array((
DeclarationType::Boolean,
64usize,
))]),
FlatEmbed::U8FromBits => DeclarationSignature::new()
.outputs(vec![DeclarationType::uint(8)])
.inputs(vec![DeclarationType::array((
@ -83,6 +91,12 @@ impl FlatEmbed {
DeclarationType::Boolean,
32usize,
))]),
FlatEmbed::U64FromBits => DeclarationSignature::new()
.outputs(vec![DeclarationType::uint(64)])
.inputs(vec![DeclarationType::array((
DeclarationType::Boolean,
64usize,
))]),
#[cfg(feature = "bellman")]
FlatEmbed::Sha256Round => DeclarationSignature::new()
.inputs(vec![
@ -120,9 +134,11 @@ impl FlatEmbed {
FlatEmbed::U8ToBits => "_U8_TO_BITS",
FlatEmbed::U16ToBits => "_U16_TO_BITS",
FlatEmbed::U32ToBits => "_U32_TO_BITS",
FlatEmbed::U64ToBits => "_U64_TO_BITS",
FlatEmbed::U8FromBits => "_U8_FROM_BITS",
FlatEmbed::U16FromBits => "_U16_FROM_BITS",
FlatEmbed::U32FromBits => "_U32_FROM_BITS",
FlatEmbed::U64FromBits => "_U64_FROM_BITS",
}
}

View file

@ -857,6 +857,11 @@ impl<'ast, T: Field> Flattener<'ast, T> {
param_expressions: Vec<ZirExpression<'ast, T>>,
) -> Vec<FlatUExpression<T>> {
match embed {
crate::embed::FlatEmbed::U64ToBits => self.flatten_u_to_bits(
statements_flattened,
param_expressions[0].clone(),
64.into(),
),
crate::embed::FlatEmbed::U32ToBits => self.flatten_u_to_bits(
statements_flattened,
param_expressions[0].clone(),
@ -870,6 +875,9 @@ impl<'ast, T: Field> Flattener<'ast, T> {
crate::embed::FlatEmbed::U8ToBits => {
self.flatten_u_to_bits(statements_flattened, param_expressions[0].clone(), 8.into())
}
crate::embed::FlatEmbed::U64FromBits => {
vec![self.flatten_bits_to_u(statements_flattened, param_expressions, 64.into())]
}
crate::embed::FlatEmbed::U32FromBits => {
vec![self.flatten_bits_to_u(statements_flattened, param_expressions, 32.into())]
}
@ -1125,7 +1133,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
let _ = self.get_bits(
FlatUExpression::with_field(FlatExpression::Add(
box FlatExpression::Sub(box r.into(), box d.clone()),
box FlatExpression::Number(T::from(2usize.pow(target_bitwidth.to_usize() as u32))),
box FlatExpression::Number(T::from(2_u128.pow(target_bitwidth.to_usize() as u32))),
)),
target_bitwidth.to_usize(),
target_bitwidth,
@ -1255,18 +1263,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
box FlatExpression::Sub(box new_left, box new_right),
))
}
UExpressionInner::LeftShift(box e, box by) => {
assert_eq!(by.bitwidth(), UBitwidth::B32);
let by = match by.into_inner() {
UExpressionInner::Value(n) => n,
by => unimplemented!(
"Variable shifts are unimplemented, found {} << {}",
e,
by.annotate(UBitwidth::B32)
),
};
UExpressionInner::LeftShift(box e, by) => {
let e = self.flatten_uint_expression(statements_flattened, e);
let e_bits = e.bits.unwrap();
@ -1284,18 +1281,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
.collect::<Vec<_>>(),
)
}
UExpressionInner::RightShift(box e, box by) => {
assert_eq!(by.bitwidth(), UBitwidth::B32);
let by = match by.into_inner() {
UExpressionInner::Value(n) => n,
by => unimplemented!(
"Variable shifts are unimplemented, found {} >> {}",
e,
by.annotate(UBitwidth::B32)
),
};
UExpressionInner::RightShift(box e, by) => {
let e = self.flatten_uint_expression(statements_flattened, e);
let e_bits = e.bits.unwrap();
@ -2077,7 +2063,8 @@ impl<'ast, T: Field> Flattener<'ast, T> {
.collect();
match embed {
FlatEmbed::U32FromBits
FlatEmbed::U64FromBits
| FlatEmbed::U32FromBits
| FlatEmbed::U16FromBits
| FlatEmbed::U8FromBits => {
let bits = exprs

View file

@ -56,6 +56,25 @@ impl From<io::Error> for Error {
}
}
#[derive(PartialEq, Clone)]
pub enum ImportDirective<'ast> {
Main(ImportNode<'ast>),
From(Vec<ImportNode<'ast>>),
}
impl<'ast> IntoIterator for ImportDirective<'ast> {
type Item = ImportNode<'ast>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
let vec = match self {
ImportDirective::Main(v) => vec![v],
ImportDirective::From(v) => v,
};
vec.into_iter()
}
}
type ImportPath<'ast> = &'ast Path;
#[derive(PartialEq, Clone)]
@ -179,6 +198,17 @@ impl Importer {
.start_end(pos.0, pos.1),
);
}
"EMBED/u64_to_bits" => {
let alias = alias.unwrap_or("u64_to_bits");
symbols.push(
SymbolDeclaration {
id: &alias,
symbol: Symbol::Flat(FlatEmbed::U64ToBits),
}
.start_end(pos.0, pos.1),
);
}
"EMBED/u32_to_bits" => {
let alias = alias.unwrap_or("u32_to_bits");
@ -212,6 +242,17 @@ impl Importer {
.start_end(pos.0, pos.1),
);
}
"EMBED/u64_from_bits" => {
let alias = alias.unwrap_or("u64_from_bits");
symbols.push(
SymbolDeclaration {
id: &alias,
symbol: Symbol::Flat(FlatEmbed::U64FromBits),
}
.start_end(pos.0, pos.1),
);
}
"EMBED/u32_from_bits" => {
let alias = alias.unwrap_or("u32_from_bits");

View file

@ -25,7 +25,7 @@ pub fn prepare_public_inputs<T: Field>(public_inputs: Vec<T>) -> (Vec<[u8; 32]>,
let mut public_inputs_arr: Vec<[u8; 32]> = vec![[0u8; 32]; public_inputs_length];
for (index, value) in public_inputs.into_iter().enumerate() {
public_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
public_inputs_arr[index] = vec_as_u8_32_array(&value.to_byte_vector());
}
(public_inputs_arr, public_inputs_length)
@ -62,21 +62,21 @@ pub fn prepare_setup<T: Field>(
a_vec.push((
row as i32,
idx as i32,
vec_as_u8_32_array(&val.into_byte_vector()),
vec_as_u8_32_array(&val.to_byte_vector()),
));
}
for &(idx, ref val) in &b[row] {
b_vec.push((
row as i32,
idx as i32,
vec_as_u8_32_array(&val.into_byte_vector()),
vec_as_u8_32_array(&val.to_byte_vector()),
));
}
for &(idx, ref val) in &c[row] {
c_vec.push((
row as i32,
idx as i32,
vec_as_u8_32_array(&val.into_byte_vector()),
vec_as_u8_32_array(&val.to_byte_vector()),
));
}
}
@ -177,10 +177,10 @@ pub fn prepare_generate_proof<T: Field>(
//convert inputs
for (index, value) in public_inputs.into_iter().enumerate() {
public_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
public_inputs_arr[index] = vec_as_u8_32_array(&value.to_byte_vector());
}
for (index, value) in private_inputs.into_iter().enumerate() {
private_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
private_inputs_arr[index] = vec_as_u8_32_array(&value.to_byte_vector());
}
(

View file

@ -134,6 +134,7 @@ impl fmt::Display for ErrorInner {
#[derive(Debug)]
struct FunctionQuery<'ast, T> {
id: Identifier<'ast>,
generics_count: Option<usize>,
inputs: Vec<Type<'ast, T>>,
/// Output types are optional as we try to infer them
outputs: Vec<Option<Type<'ast, T>>>,
@ -141,6 +142,17 @@ struct FunctionQuery<'ast, T> {
impl<'ast, T: fmt::Display> fmt::Display for FunctionQuery<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(count) = self.generics_count {
write!(
f,
"<{}>",
(0..count)
.map(|_| String::from("_"))
.collect::<Vec<_>>()
.join(", ")
)?
}
write!(f, "(")?;
for (i, t) in self.inputs.iter().enumerate() {
write!(f, "{}", t)?;
@ -181,11 +193,13 @@ impl<'ast, T: Field> FunctionQuery<'ast, T> {
/// Create a new query.
fn new(
id: Identifier<'ast>,
generics: &Option<Vec<Option<UExpression<'ast, T>>>>,
inputs: &[Type<'ast, T>],
outputs: &[Option<Type<'ast, T>>],
) -> Self {
FunctionQuery {
id,
generics_count: generics.as_ref().map(|g| g.len()),
inputs: inputs.to_owned(),
outputs: outputs.to_owned(),
}
@ -194,6 +208,8 @@ impl<'ast, T: Field> FunctionQuery<'ast, T> {
/// match a `FunctionKey` against this `FunctionQuery`
fn match_func(&self, func: &DeclarationFunctionKey<'ast>) -> bool {
self.id == func.id
&& self.generics_count.map(|count| count == func.signature.generics.len()).unwrap_or(true) // we do not look at the values here, this will be checked when inlining anyway
&& self.inputs.len() == func.signature.inputs.len()
&& self
.inputs
.iter()
@ -1340,7 +1356,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
let arguments_types: Vec<_> =
arguments_checked.iter().map(|a| a.get_type()).collect();
let query = FunctionQuery::new(&fun_id, &arguments_types, &assignee_types);
let query = FunctionQuery::new(&fun_id, &generics_checked, &arguments_types, &assignee_types);
let functions = self.find_functions(&query);
@ -1733,6 +1749,44 @@ impl<'ast, T: Field> Checker<'ast, T> {
}),
}
}
Expression::Neg(box e) => {
let e = self.check_expression(e, module_id, &types)?;
match e {
TypedExpression::Int(e) => Ok(IntExpression::Neg(box e).into()),
TypedExpression::FieldElement(e) => {
Ok(FieldElementExpression::Neg(box e).into())
}
TypedExpression::Uint(e) => Ok((-e).into()),
e => Err(ErrorInner {
pos: Some(pos),
message: format!(
"Unary operator `-` cannot be applied to {} of type {}",
e,
e.get_type()
),
}),
}
}
Expression::Pos(box e) => {
let e = self.check_expression(e, module_id, &types)?;
match e {
TypedExpression::Int(e) => Ok(IntExpression::Pos(box e).into()),
TypedExpression::FieldElement(e) => {
Ok(FieldElementExpression::Pos(box e).into())
}
TypedExpression::Uint(e) => Ok(UExpression::pos(e).into()),
e => Err(ErrorInner {
pos: Some(pos),
message: format!(
"Unary operator `+` cannot be applied to {} of type {}",
e,
e.get_type()
),
}),
}
}
Expression::IfElse(box condition, box consequence, box alternative) => {
let condition_checked = self.check_expression(condition, module_id, &types)?;
let consequence_checked = self.check_expression(consequence, module_id, &types)?;
@ -1802,6 +1856,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
Expression::U8Constant(n) => Ok(UExpressionInner::Value(n.into()).annotate(8).into()),
Expression::U16Constant(n) => Ok(UExpressionInner::Value(n.into()).annotate(16).into()),
Expression::U32Constant(n) => Ok(UExpressionInner::Value(n.into()).annotate(32).into()),
Expression::U64Constant(n) => Ok(UExpressionInner::Value(n.into()).annotate(64).into()),
Expression::FunctionCall(fun_id, generics, arguments) => {
// check the generic arguments, if any
let generics_checked: Option<Vec<Option<UExpression<'ast, T>>>> = generics
@ -1844,7 +1899,8 @@ impl<'ast, T: Field> Checker<'ast, T> {
// outside of multidef, function calls must have a single return value
// we use type inference to determine the type of the return, so we don't specify it
let query = FunctionQuery::new(&fun_id, &arguments_types, &[None]);
let query =
FunctionQuery::new(&fun_id, &generics_checked, &arguments_types, &[None]);
let functions = self.find_functions(&query);

View file

@ -562,6 +562,15 @@ pub fn fold_field_expression<'ast, T: Field>(
let e2 = f.fold_uint_expression(e2);
zir::FieldElementExpression::Pow(box e1, box e2)
}
typed_absy::FieldElementExpression::Neg(box e) => {
let e = f.fold_field_expression(e);
zir::FieldElementExpression::Sub(
box zir::FieldElementExpression::Number(T::zero()),
box e,
)
}
typed_absy::FieldElementExpression::Pos(box e) => f.fold_field_expression(e),
typed_absy::FieldElementExpression::IfElse(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond);
let cons = f.fold_field_expression(cons);
@ -846,21 +855,41 @@ pub fn fold_uint_expression_inner<'ast, T: Field>(
}
typed_absy::UExpressionInner::LeftShift(box e, box by) => {
let e = f.fold_uint_expression(e);
let by = f.fold_uint_expression(by);
zir::UExpressionInner::LeftShift(box e, box by)
let by = match by.as_inner() {
typed_absy::UExpressionInner::Value(by) => by,
_ => unreachable!("static analysis should have made sure that this is constant"),
};
zir::UExpressionInner::LeftShift(box e, *by as u32)
}
typed_absy::UExpressionInner::RightShift(box e, box by) => {
let e = f.fold_uint_expression(e);
let by = f.fold_uint_expression(by);
zir::UExpressionInner::RightShift(box e, box by)
let by = match by.as_inner() {
typed_absy::UExpressionInner::Value(by) => by,
_ => unreachable!("static analysis should have made sure that this is constant"),
};
zir::UExpressionInner::RightShift(box e, *by as u32)
}
typed_absy::UExpressionInner::Not(box e) => {
let e = f.fold_uint_expression(e);
zir::UExpressionInner::Not(box e)
}
typed_absy::UExpressionInner::Neg(box e) => {
let bitwidth = e.bitwidth();
f.fold_uint_expression(typed_absy::UExpressionInner::Value(0).annotate(bitwidth) - e)
.into_inner()
}
typed_absy::UExpressionInner::Pos(box e) => {
let e = f.fold_uint_expression(e);
e.into_inner()
}
typed_absy::UExpressionInner::FunctionCall(..) => {
unreachable!("function calls should have been removed")
}

View file

@ -10,6 +10,7 @@ mod flatten_complex_types;
mod propagation;
mod redefinition;
mod reducer;
mod shift_checker;
mod uint_optimizer;
mod unconstrained_vars;
mod variable_read_remover;
@ -20,6 +21,7 @@ use self::flatten_complex_types::Flattener;
use self::propagation::Propagator;
use self::redefinition::RedefinitionOptimizer;
use self::reducer::reduce_program;
use self::shift_checker::ShiftChecker;
use self::uint_optimizer::UintOptimizer;
use self::unconstrained_vars::UnconstrainedVariableDetector;
use self::variable_read_remover::VariableReadRemover;
@ -85,6 +87,8 @@ impl<'ast, T: Field> TypedProgram<'ast, T> {
let r = VariableReadRemover::apply(r);
// check array accesses are in bounds
let r = BoundsChecker::check(r).map_err(Error::from)?;
// detect non constant shifts
let r = ShiftChecker::check(r).map_err(Error::from)?;
// convert to zir, removing complex types
let zir = Flattener::flatten(r);
// optimize uint expressions

View file

@ -438,6 +438,11 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
true => {
let r: Option<TypedExpression<'ast, T>> = match embed {
FlatEmbed::U32ToField => None, // todo
FlatEmbed::U64FromBits => Some(process_u_from_bits(
assignees.clone(),
arguments.clone(),
UBitwidth::B64,
)),
FlatEmbed::U32FromBits => Some(process_u_from_bits(
assignees.clone(),
arguments.clone(),
@ -453,6 +458,11 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
arguments.clone(),
UBitwidth::B8,
)),
FlatEmbed::U64ToBits => Some(process_u_to_bits(
assignees.clone(),
arguments.clone(),
UBitwidth::B64,
)),
FlatEmbed::U32ToBits => Some(process_u_to_bits(
assignees.clone(),
arguments.clone(),
@ -647,6 +657,17 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
Ok(statements)
}
TypedStatement::Assertion(e) => {
let e_str = e.to_string();
let expr = self.fold_boolean_expression(e)?;
match expr {
BooleanExpression::Value(v) if !v => Err(Error::Type(format!(
"Assertion failed on expression `{}`",
e_str
))),
_ => Ok(vec![TypedStatement::Assertion(expr)]),
}
}
s @ TypedStatement::PushCallLog(..) => Ok(vec![s]),
s @ TypedStatement::PopCallLog => Ok(vec![s]),
s => fold_statement(self, s),
@ -862,6 +883,23 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
e => UExpressionInner::Not(box e.annotate(bitwidth)),
}
}
UExpressionInner::Neg(box e) => {
let e = self.fold_uint_expression(e)?.into_inner();
match e {
UExpressionInner::Value(v) => UExpressionInner::Value(
(0u128.wrapping_sub(v))
% 2_u128.pow(bitwidth.to_usize().try_into().unwrap()),
),
e => UExpressionInner::Neg(box e.annotate(bitwidth)),
}
}
UExpressionInner::Pos(box e) => {
let e = self.fold_uint_expression(e)?.into_inner();
match e {
UExpressionInner::Value(v) => UExpressionInner::Value(v),
e => UExpressionInner::Pos(box e.annotate(bitwidth)),
}
}
UExpressionInner::Select(box array, box index) => {
let array = self.fold_array_expression(array)?;
let index = self.fold_uint_expression(index)?;
@ -980,6 +1018,14 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
}
(e1, e2) => FieldElementExpression::Div(box e1, box e2),
},
FieldElementExpression::Neg(box e) => match self.fold_field_expression(e)? {
FieldElementExpression::Number(n) => FieldElementExpression::Number(T::zero() - n),
e => FieldElementExpression::Neg(box e),
},
FieldElementExpression::Pos(box e) => match self.fold_field_expression(e)? {
FieldElementExpression::Number(n) => FieldElementExpression::Number(n),
e => FieldElementExpression::Pos(box e),
},
FieldElementExpression::Pow(box e1, box e2) => {
let e1 = self.fold_field_expression(e1)?;
let e2 = self.fold_uint_expression(e2)?;
@ -1361,8 +1407,8 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
let e2 = self.fold_uint_expression(e2)?;
match (e1.as_inner(), e2.as_inner()) {
(UExpressionInner::Value(v1), UExpressionInner::Value(v2)) => {
BooleanExpression::Value(v1 == v2)
(UExpressionInner::Value(n1), UExpressionInner::Value(n2)) => {
BooleanExpression::Value(n1 == n2)
}
_ => BooleanExpression::UintEq(box e1, box e2),
}

View file

@ -0,0 +1,55 @@
use crate::typed_absy::TypedProgram;
use crate::typed_absy::{
result_folder::fold_uint_expression_inner, result_folder::ResultFolder, UBitwidth,
UExpressionInner,
};
use zokrates_field::Field;
pub struct ShiftChecker;
impl ShiftChecker {
pub fn check<T: Field>(p: TypedProgram<T>) -> Result<TypedProgram<T>, Error> {
ShiftChecker.fold_program(p)
}
}
pub type Error = String;
impl<'ast, T: Field> ResultFolder<'ast, T> for ShiftChecker {
type Error = Error;
fn fold_uint_expression_inner(
&mut self,
bitwidth: UBitwidth,
e: UExpressionInner<'ast, T>,
) -> Result<UExpressionInner<'ast, T>, Error> {
match e {
UExpressionInner::LeftShift(box e, box by) => {
let e = self.fold_uint_expression(e)?;
let by = self.fold_uint_expression(by)?;
match by.as_inner() {
UExpressionInner::Value(_) => Ok(UExpressionInner::LeftShift(box e, box by)),
by => Err(format!(
"Cannot shift by a variable value, found `{} << {}`",
e,
by.clone().annotate(UBitwidth::B32)
)),
}
}
UExpressionInner::RightShift(box e, box by) => {
let e = self.fold_uint_expression(e)?;
let by = self.fold_uint_expression(by)?;
match by.as_inner() {
UExpressionInner::Value(_) => Ok(UExpressionInner::RightShift(box e, box by)),
by => Err(format!(
"Cannot shift by a variable value, found `{} >> {}`",
e,
by.clone().annotate(UBitwidth::B32)
)),
}
}
e => fold_uint_expression_inner(self, bitwidth, e),
}
}
}

View file

@ -117,7 +117,7 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
let range = e.bitwidth.to_usize();
let range_max: T = (2_usize.pow(range as u32) - 1).into();
let range_max: T = (2_u128.pow(range as u32) - 1).into();
assert!(range < max_bitwidth / 2);
@ -314,20 +314,11 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
.annotate(range)
.with_max(range_max)
}
LeftShift(box e, box by) => {
LeftShift(box e, by) => {
// reduce both terms
let e = self.fold_uint_expression(e);
let by = self.fold_uint_expression(by);
let by_max: usize = by
.metadata
.clone()
.unwrap()
.max
.to_dec_string()
.parse()
.unwrap();
let e_max: usize = e
let e_max: u128 = e
.metadata
.clone()
.unwrap()
@ -336,22 +327,15 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
.parse()
.unwrap();
let max = T::from((e_max << by_max) & (2_usize.pow(range as u32) - 1));
let max = T::from((e_max << by) & (2_u128.pow(range as u32) - 1));
UExpression::left_shift(force_reduce(e), force_reduce(by)).with_max(max)
UExpression::left_shift(force_reduce(e), by).with_max(max)
}
RightShift(box e, box by) => {
RightShift(box e, by) => {
// reduce both terms
let e = self.fold_uint_expression(e);
let by = self.fold_uint_expression(by);
// if we don't know the amount by which we shift, the most conservative case (which leads to the biggest value) is 0
let by_u = match by.as_inner() {
UExpressionInner::Value(by) => *by,
_ => 0,
};
let e_max: usize = e
let e_max: u128 = e
.metadata
.clone()
.unwrap()
@ -360,11 +344,11 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
.parse()
.unwrap();
let max = (e_max & (2_usize.pow(range as u32) - 1)) >> by_u;
let max = (e_max & (2_u128.pow(range as u32) - 1)) >> by;
let max = T::from(max);
UExpression::right_shift(force_reduce(e), force_reduce(by)).with_max(max)
UExpression::right_shift(force_reduce(e), by).with_max(max)
}
IfElse(box condition, box consequence, box alternative) => {
let condition = self.fold_boolean_expression(condition);
@ -423,6 +407,21 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
)],
ZirStatement::MultipleDefinition(lhs, rhs) => match rhs {
ZirExpressionList::EmbedCall(embed, generics, arguments) => match embed {
FlatEmbed::U64FromBits => {
assert_eq!(lhs.len(), 1);
self.register(
lhs[0].clone(),
UMetadata {
max: T::from(2).pow(64) - T::from(1),
should_reduce: ShouldReduce::False,
},
);
vec![ZirStatement::MultipleDefinition(
lhs,
ZirExpressionList::EmbedCall(embed, generics, arguments),
)]
}
FlatEmbed::U32FromBits => {
assert_eq!(lhs.len(), 1);
self.register(
@ -679,30 +678,14 @@ mod tests {
#[test]
fn right_shift() {
// left argument in range, we reduce (no effect) and the max is the original max, as we could be shifting by 0
uint_test!(0xff_u32, true, 2, true, right_shift, 0xff_u32);
uint_test!(2, true, 2, true, right_shift, 2_u32);
// left argument out of range, we reduce and the max is the type max, shifted
uint_test!(
0xffffffffffff_u128,
true,
2,
true,
right_shift,
0xffffffff_u32
);
fn right_shift_test(e_max: u128, by: u32, output_max: u32) {
let left = e_with_max(e_max);
let right = UExpressionInner::Value(by as u128)
.annotate(crate::zir::types::UBitwidth::B32)
.with_max(by);
let right = by;
let left_expected = force_reduce(left.clone());
let right_expected = force_reduce(right.clone());
let right_expected = right;
assert_eq!(
UintOptimizer::new()
@ -718,25 +701,25 @@ mod tests {
#[test]
fn left_shift() {
uint_test!(0xff_u32, true, 2, true, left_shift, 0xff_u32 << 2);
uint_test!(
0xffffffff_u32,
true,
2,
true,
left_shift,
0xffffffff_u32 << 2
);
fn left_shift_test(e_max: u128, by: u32, output_max: u32) {
let left = e_with_max(e_max);
// left argument out of range, we reduce and the max is the type max, shifted
uint_test!(
0xffffffffffff_u128,
true,
2,
true,
left_shift,
0xffffffff_u32 << 2
)
let right = by;
let left_expected = force_reduce(left.clone());
let right_expected = right;
assert_eq!(
UintOptimizer::new()
.fold_uint_expression(UExpression::left_shift(left.clone(), right.clone())),
UExpression::left_shift(left_expected, right_expected).with_max(output_max)
);
}
left_shift_test(0xff_u128, 2, 0xff << 2);
left_shift_test(2, 2, 2 << 2);
left_shift_test(0xffffffffffff_u128, 2, 0xffffffff << 2);
}
#[test]

View file

@ -357,6 +357,16 @@ 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::Neg(box e) => {
let e = f.fold_field_expression(e);
FieldElementExpression::Neg(box e)
}
FieldElementExpression::Pos(box e) => {
let e = f.fold_field_expression(e);
FieldElementExpression::Pos(box e)
}
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond);
let cons = f.fold_field_expression(cons);
@ -591,6 +601,16 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
UExpressionInner::Not(box e)
}
UExpressionInner::Neg(box e) => {
let e = f.fold_uint_expression(e);
UExpressionInner::Neg(box e)
}
UExpressionInner::Pos(box e) => {
let e = f.fold_uint_expression(e);
UExpressionInner::Pos(box e)
}
UExpressionInner::FunctionCall(key, generics, exps) => {
let generics = generics
.into_iter()

View file

@ -8,7 +8,7 @@ use crate::typed_absy::{
use num_bigint::BigUint;
use std::convert::TryFrom;
use std::fmt;
use std::ops::{Add, Div, Mul, Not, Rem, Sub};
use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub};
use zokrates_field::Field;
type TypedExpressionPair<'ast, T> = (TypedExpression<'ast, T>, TypedExpression<'ast, T>);
@ -134,6 +134,8 @@ impl<'ast, T: Field> TypedExpression<'ast, T> {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum IntExpression<'ast, T> {
Value(BigUint),
Pos(Box<IntExpression<'ast, T>>),
Neg(Box<IntExpression<'ast, T>>),
Add(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
Sub(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
Mult(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
@ -202,6 +204,14 @@ impl<'ast, T> Not for IntExpression<'ast, T> {
}
}
impl<'ast, T> Neg for IntExpression<'ast, T> {
type Output = Self;
fn neg(self) -> Self {
IntExpression::Neg(box self)
}
}
impl<'ast, T> IntExpression<'ast, T> {
pub fn pow(self, other: Self) -> Self {
IntExpression::Pow(box self, box other)
@ -226,12 +236,18 @@ impl<'ast, T> IntExpression<'ast, T> {
pub fn right_shift(self, by: UExpression<'ast, T>) -> Self {
IntExpression::RightShift(box self, box by)
}
pub fn pos(self) -> Self {
IntExpression::Pos(box self)
}
}
impl<'ast, T: fmt::Display> fmt::Display for IntExpression<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
IntExpression::Value(ref v) => write!(f, "{}", v),
IntExpression::Pos(ref e) => write!(f, "(+{})", e),
IntExpression::Neg(ref e) => write!(f, "(-{})", e),
IntExpression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
IntExpression::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
IntExpression::Pow(ref lhs, ref rhs) => write!(f, "({} ** {})", lhs, rhs),
@ -297,6 +313,8 @@ impl<'ast, T: Field> FieldElementExpression<'ast, T> {
box Self::try_from_int(e1)?,
box Self::try_from_int(e2)?,
)),
IntExpression::Pos(box e) => Ok(Self::Pos(box Self::try_from_int(e)?)),
IntExpression::Neg(box e) => Ok(Self::Neg(box Self::try_from_int(e)?)),
IntExpression::IfElse(box condition, box consequence, box alternative) => {
Ok(Self::IfElse(
box condition,
@ -374,6 +392,8 @@ impl<'ast, T: Field> UExpression<'ast, T> {
Add(box e1, box e2) => {
Ok(Self::try_from_int(e1, bitwidth)? + Self::try_from_int(e2, bitwidth)?)
}
Pos(box e) => Ok(Self::pos(Self::try_from_int(e, bitwidth)?)),
Neg(box e) => Ok(Self::neg(Self::try_from_int(e, bitwidth)?)),
Sub(box e1, box e2) => {
Ok(Self::try_from_int(e1, bitwidth)? - Self::try_from_int(e2, bitwidth)?)
}

View file

@ -809,6 +809,8 @@ pub enum FieldElementExpression<'ast, T> {
Box<FieldElementExpression<'ast, T>>,
Box<FieldElementExpression<'ast, T>>,
),
Neg(Box<FieldElementExpression<'ast, T>>),
Pos(Box<FieldElementExpression<'ast, T>>),
FunctionCall(
DeclarationFunctionKey<'ast>,
Vec<Option<UExpression<'ast, T>>>,
@ -1232,6 +1234,8 @@ 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::Neg(ref e) => write!(f, "(-{})", e),
FieldElementExpression::Pos(ref e) => write!(f, "(+{})", e),
FieldElementExpression::IfElse(ref condition, ref consequent, ref alternative) => {
write!(
f,
@ -1289,6 +1293,8 @@ impl<'ast, T: fmt::Display> fmt::Display for UExpression<'ast, T> {
UExpressionInner::RightShift(ref e, ref by) => write!(f, "({} >> {})", e, by),
UExpressionInner::LeftShift(ref e, ref by) => write!(f, "({} << {})", e, by),
UExpressionInner::Not(ref e) => write!(f, "!{}", e),
UExpressionInner::Neg(ref e) => write!(f, "(-{})", e),
UExpressionInner::Pos(ref e) => write!(f, "(+{})", e),
UExpressionInner::Select(ref id, ref index) => write!(f, "{}[{}]", id, index),
UExpressionInner::FunctionCall(ref k, ref generics, ref p) => {
write!(f, "{}", k.id,)?;
@ -1516,6 +1522,8 @@ impl<'ast, T: fmt::Debug> fmt::Debug for FieldElementExpression<'ast, T> {
}
FieldElementExpression::Div(ref lhs, ref rhs) => write!(f, "Div({:?}, {:?})", lhs, rhs),
FieldElementExpression::Pow(ref lhs, ref rhs) => write!(f, "Pow({:?}, {:?})", lhs, rhs),
FieldElementExpression::Neg(ref e) => write!(f, "Neg({:?})", e),
FieldElementExpression::Pos(ref e) => write!(f, "Pos({:?})", e),
FieldElementExpression::IfElse(ref condition, ref consequent, ref alternative) => {
write!(
f,

View file

@ -417,6 +417,16 @@ 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::Neg(box e) => {
let e = f.fold_field_expression(e)?;
FieldElementExpression::Neg(box e)
}
FieldElementExpression::Pos(box e) => {
let e = f.fold_field_expression(e)?;
FieldElementExpression::Pos(box e)
}
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond)?;
let cons = f.fold_field_expression(cons)?;
@ -659,6 +669,16 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
UExpressionInner::Not(box e)
}
UExpressionInner::Neg(box e) => {
let e = f.fold_uint_expression(e)?;
UExpressionInner::Neg(box e)
}
UExpressionInner::Pos(box e) => {
let e = f.fold_uint_expression(e)?;
UExpressionInner::Pos(box e)
}
UExpressionInner::FunctionCall(key, generics, exps) => {
let generics = generics
.into_iter()

View file

@ -387,6 +387,8 @@ pub enum UBitwidth {
B16 = 16,
#[serde(rename = "32")]
B32 = 32,
#[serde(rename = "64")]
B64 = 64,
}
impl UBitwidth {
@ -401,6 +403,7 @@ impl From<usize> for UBitwidth {
8 => UBitwidth::B8,
16 => UBitwidth::B16,
32 => UBitwidth::B32,
64 => UBitwidth::B64,
_ => unreachable!(),
}
}
@ -512,6 +515,7 @@ impl<'de, S: Deserialize<'de>> Deserialize<'de> for GType<S> {
"u8" => strict_type(mapping, GType::Uint(UBitwidth::B8)),
"u16" => strict_type(mapping, GType::Uint(UBitwidth::B16)),
"u32" => strict_type(mapping, GType::Uint(UBitwidth::B32)),
"u64" => strict_type(mapping, GType::Uint(UBitwidth::B64)),
t => Err(D::Error::custom(format!("invalid type `{}`", t))),
}
}
@ -989,6 +993,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());
let decl_generics = self.generics.iter().map(|g| match g.clone().unwrap() {

View file

@ -1,6 +1,6 @@
use crate::typed_absy::types::UBitwidth;
use crate::typed_absy::*;
use std::ops::{Add, Div, Mul, Not, Rem, Sub};
use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub};
use zokrates_field::Field;
type Bitwidth = usize;
@ -69,6 +69,15 @@ impl<'ast, T> Not for UExpression<'ast, T> {
}
}
impl<'ast, T> Neg for UExpression<'ast, T> {
type Output = Self;
fn neg(self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
UExpressionInner::Neg(box self).annotate(bitwidth)
}
}
impl<'ast, T: Field> UExpression<'ast, T> {
pub fn xor(self, other: Self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
@ -88,6 +97,11 @@ impl<'ast, T: Field> UExpression<'ast, T> {
UExpressionInner::And(box self, box other).annotate(bitwidth)
}
pub fn pos(self) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
UExpressionInner::Pos(box self).annotate(bitwidth)
}
pub fn left_shift(self, by: UExpression<'ast, T>) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(by.bitwidth, UBitwidth::B32);
@ -173,6 +187,8 @@ pub enum UExpressionInner<'ast, T> {
And(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
Or(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
Not(Box<UExpression<'ast, T>>),
Neg(Box<UExpression<'ast, T>>),
Pos(Box<UExpression<'ast, T>>),
FunctionCall(
DeclarationFunctionKey<'ast>,
Vec<Option<UExpression<'ast, T>>>,

View file

@ -308,17 +308,15 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
UExpressionInner::Or(box left, box right)
}
UExpressionInner::LeftShift(box e, box by) => {
UExpressionInner::LeftShift(box e, by) => {
let e = f.fold_uint_expression(e);
let by = f.fold_uint_expression(by);
UExpressionInner::LeftShift(box e, box by)
UExpressionInner::LeftShift(box e, by)
}
UExpressionInner::RightShift(box e, box by) => {
UExpressionInner::RightShift(box e, by) => {
let e = f.fold_uint_expression(e);
let by = f.fold_uint_expression(by);
UExpressionInner::RightShift(box e, box by)
UExpressionInner::RightShift(box e, by)
}
UExpressionInner::Not(box e) => {
let e = f.fold_uint_expression(e);

View file

@ -18,6 +18,8 @@ pub enum UBitwidth {
B16 = 16,
#[serde(rename = "32")]
B32 = 32,
#[serde(rename = "64")]
B64 = 64,
}
impl UBitwidth {
@ -26,6 +28,7 @@ impl UBitwidth {
UBitwidth::B8 => 8,
UBitwidth::B16 => 16,
UBitwidth::B32 => 32,
UBitwidth::B64 => 64,
}
}
}
@ -36,6 +39,7 @@ impl From<usize> for UBitwidth {
8 => UBitwidth::B8,
16 => UBitwidth::B16,
32 => UBitwidth::B32,
64 => UBitwidth::B64,
_ => unreachable!(),
}
}

View file

@ -57,16 +57,14 @@ impl<'ast, T: Field> UExpression<'ast, T> {
UExpressionInner::And(box self, box other).annotate(bitwidth)
}
pub fn left_shift(self, by: UExpression<'ast, T>) -> UExpression<'ast, T> {
pub fn left_shift(self, by: u32) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(by.bitwidth(), UBitwidth::B32);
UExpressionInner::LeftShift(box self, box by).annotate(bitwidth)
UExpressionInner::LeftShift(box self, by).annotate(bitwidth)
}
pub fn right_shift(self, by: UExpression<'ast, T>) -> UExpression<'ast, T> {
pub fn right_shift(self, by: u32) -> UExpression<'ast, T> {
let bitwidth = self.bitwidth;
assert_eq!(by.bitwidth(), UBitwidth::B32);
UExpressionInner::RightShift(box self, box by).annotate(bitwidth)
UExpressionInner::RightShift(box self, by).annotate(bitwidth)
}
}
@ -170,8 +168,8 @@ pub enum UExpressionInner<'ast, T> {
Xor(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
And(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
Or(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
LeftShift(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
RightShift(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
LeftShift(Box<UExpression<'ast, T>>, u32),
RightShift(Box<UExpression<'ast, T>>, u32),
Not(Box<UExpression<'ast, T>>),
IfElse(
Box<BooleanExpression<'ast, T>>,

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_core_test"
version = "0.1.5"
version = "0.2.0"
authors = ["schaeff <thibaut@schaeff.fr>"]
edition = "2018"

View file

@ -0,0 +1,46 @@
{
"entry_point": "./tests/tests/left_rotation.zok",
"max_constraint_count": 34,
"tests": [
{
"input": {
"values": ["0"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
},
{
"input": {
"values": ["1"]
},
"output": {
"Ok": {
"values": ["4"]
}
}
},
{
"input": {
"values": ["42"]
},
"output": {
"Ok": {
"values": ["168"]
}
}
},
{
"input": {
"values": ["2147483658"]
},
"output": {
"Ok": {
"values": ["42"]
}
}
}
]
}

View file

@ -0,0 +1,5 @@
def rotl32<N>(u32 x) -> u32:
return ((x << N) | (x >> (32 - N)))
def main(u32 i) -> u32:
return rotl32::<2>(i)

View file

@ -0,0 +1,46 @@
{
"entry_point": "./tests/tests/left_rotation_bits.zok",
"max_constraint_count": 34,
"tests": [
{
"input": {
"values": ["0"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
},
{
"input": {
"values": ["1"]
},
"output": {
"Ok": {
"values": ["4"]
}
}
},
{
"input": {
"values": ["42"]
},
"output": {
"Ok": {
"values": ["168"]
}
}
},
{
"input": {
"values": ["2147483658"]
},
"output": {
"Ok": {
"values": ["42"]
}
}
}
]
}

View file

@ -0,0 +1,9 @@
import "EMBED/u32_to_bits" as to_bits
import "EMBED/u32_from_bits" as from_bits
def rotl32<N>(u32 e) -> u32:
bool[32] b = to_bits(e)
return from_bits([...b[N..], ...b[..N]])
def main(u32 i) -> u32:
return rotl32::<2>(i)

View file

@ -1,3 +1,3 @@
def dep(field a) -> field: // this costs 2 constraits per call
def dep(field a) -> field: // this costs 2 constraints per call
field res = a ** 4
return res

View file

@ -1,6 +1,6 @@
{
"entry_point": "./tests/tests/memoize/memoize.zok",
"max_constraint_count": 14,
"max_constraint_count": 32,
"tests": [
{
"input": {

View file

@ -0,0 +1,15 @@
{
"entry_point": "./tests/tests/neg_pos.zok",
"tests": [
{
"input": {
"values": ["1", "2", "1", "2"]
},
"output": {
"Ok": {
"values": ["21888242871839275222246405745257275088548364400416034343698204186575808495615", "21888242871839275222246405745257275088548364400416034343698204186575808495616", "21888242871839275222246405745257275088548364400416034343698204186575808495616", "21888242871839275222246405745257275088548364400416034343698204186575808495616", "254", "255", "255", "255"]
}
}
}
]
}

View file

@ -0,0 +1,15 @@
def main(field x, field y, u8 z, u8 t) -> (field[4], u8[4]):
field a = -y // should parse to neg
field b = x - y // should parse to sub
field c = x + - y // should parse to add(neg)
field d = x - + y // should parse to sub(pos)
u8 e = -t // should parse to neg
u8 f = z - t // should parse to sub
u8 g = z + - t // should parse to add(neg)
u8 h = z - + t // should parse to sub(pos)
assert(-0x00 == 0x00)
assert(-0f == 0)
return [a, b, c, d], [e, f, g, h]

View file

@ -13,6 +13,9 @@ def main():
assert(0x00 ^ 0x00 == 0x00)
assert(0 - 2 ** 2 == -4)
assert(-2**2 == -4)
//check if all statements have evalutated to true
assert(a * b * c * d * e * f == 1)
return

View file

@ -0,0 +1,46 @@
{
"entry_point": "./tests/tests/right_rotation.zok",
"max_constraint_count": 34,
"tests": [
{
"input": {
"values": ["0"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
},
{
"input": {
"values": ["1"]
},
"output": {
"Ok": {
"values": ["1073741824"]
}
}
},
{
"input": {
"values": ["42"]
},
"output": {
"Ok": {
"values": ["2147483658"]
}
}
},
{
"input": {
"values": ["2147483658"]
},
"output": {
"Ok": {
"values": ["2684354562"]
}
}
}
]
}

View file

@ -0,0 +1,5 @@
def rotr32<N>(u32 x) -> u32:
return (x >> N) | (x << (32 - N))
def main(u32 i) -> u32:
return rotr32::<2>(i)

View file

@ -0,0 +1,46 @@
{
"entry_point": "./tests/tests/right_rotation_bits.zok",
"max_constraint_count": 34,
"tests": [
{
"input": {
"values": ["0"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
},
{
"input": {
"values": ["1"]
},
"output": {
"Ok": {
"values": ["1073741824"]
}
}
},
{
"input": {
"values": ["42"]
},
"output": {
"Ok": {
"values": ["2147483658"]
}
}
},
{
"input": {
"values": ["2147483658"]
},
"output": {
"Ok": {
"values": ["2684354562"]
}
}
}
]
}

View file

@ -0,0 +1,9 @@
import "EMBED/u32_to_bits" as to_bits
import "EMBED/u32_from_bits" as from_bits
def rotr32<N>(u32 e) -> u32:
bool[32] b = to_bits(e)
return from_bits([...b[32-N..], ...b[..32-N]])
def main(u32 i) -> u32:
return rotr32::<2>(i)

View file

@ -1,36 +0,0 @@
{
"entry_point": "./tests/tests/uint/add.zok",
"max_constraint_count": 29,
"tests": [
{
"input": {
"values": ["0xff", "0x01"]
},
"output": {
"Ok": {
"values": ["0x00"]
}
}
},
{
"input": {
"values": ["0x00", "0x01"]
},
"output": {
"Ok": {
"values": ["0x01"]
}
}
},
{
"input": {
"values": ["0xff", "0xff"]
},
"output": {
"Ok": {
"values": ["0xfe"]
}
}
}
]
}

View file

@ -1,36 +0,0 @@
{
"entry_point": "./tests/tests/uint/and.zok",
"max_constraint_count": 27,
"tests": [
{
"input": {
"values": ["0xff", "0xff"]
},
"output": {
"Ok": {
"values": ["0xff"]
}
}
},
{
"input": {
"values": ["0xff", "0x00"]
},
"output": {
"Ok": {
"values": ["0x00"]
}
}
},
{
"input": {
"values": ["0x23", "0x34"]
},
"output": {
"Ok": {
"values": ["0x20"]
}
}
}
]
}

View file

@ -1,26 +0,0 @@
{
"entry_point": "./tests/tests/uint/div.zok",
"max_constraint_count": 43,
"tests": [
{
"input": {
"values": ["255", "1"]
},
"output": {
"Ok": {
"values": ["255"]
}
}
},
{
"input": {
"values": ["42", "10"]
},
"output": {
"Ok": {
"values": ["4"]
}
}
}
]
}

View file

@ -1,6 +0,0 @@
def main(u8 x, u8 y) -> u8:
assert(0x02 / 0x02 == 0x01)
assert(0x04 / 0x02 == 0x02)
assert(0x05 / 0x02 == 0x02)
assert(0xff / 0x03 == 0x55)
return x / y

View file

@ -1,26 +0,0 @@
{
"entry_point": "./tests/tests/uint/div_rem.zok",
"max_constraint_count": 43,
"tests": [
{
"input": {
"values": ["255", "1"]
},
"output": {
"Ok": {
"values": ["255", "0"]
}
}
},
{
"input": {
"values": ["42", "10"]
},
"output": {
"Ok": {
"values": ["4", "2"]
}
}
}
]
}

View file

@ -1,2 +0,0 @@
def main(u8 n, u8 d) -> (u8, u8):
return n / d, n % d

View file

@ -1,34 +1,34 @@
{
"entry_point": "./tests/tests/uint/from_to_bits.zok",
"max_constraint_count": 34,
"max_constraint_count": 128,
"tests": [
{
"input": {
"values": ["0x00000000", "0x0000", "0x00"]
"values": ["0x0000000000000000", "0x00000000", "0x0000", "0x00"]
},
"output": {
"Ok": {
"values": ["0x00000000", "0x0000", "0x00"]
"values": ["0x0000000000000000", "0x00000000", "0x0000", "0x00"]
}
}
},
{
"input": {
"values": ["0xffffffff", "0xffff", "0xff"]
"values": ["0xffffffffffffffff", "0xffffffff", "0xffff", "0xff"]
},
"output": {
"Ok": {
"values": ["0xffffffff", "0xffff", "0xff"]
"values": ["0xffffffffffffffff", "0xffffffff", "0xffff", "0xff"]
}
}
},
{
"input": {
"values": ["0x12345678", "0x1234", "0x12"]
"values": ["0x1234567812345678", "0x12345678", "0x1234", "0x12"]
},
"output": {
"Ok": {
"values": ["0x12345678", "0x1234", "0x12"]
"values": ["0x1234567812345678", "0x12345678", "0x1234", "0x12"]
}
}
}

View file

@ -1,3 +1,5 @@
import "EMBED/u64_to_bits" as to_bits_64
import "EMBED/u64_from_bits" as from_bits_64
import "EMBED/u32_to_bits" as to_bits_32
import "EMBED/u32_from_bits" as from_bits_32
import "EMBED/u16_to_bits" as to_bits_16
@ -5,8 +7,9 @@ import "EMBED/u16_from_bits" as from_bits_16
import "EMBED/u8_to_bits" as to_bits_8
import "EMBED/u8_from_bits" as from_bits_8
def main(u32 e, u16 f, u8 g) -> (u32, u16, u8):
def main(u64 d, u32 e, u16 f, u8 g) -> (u64, u32, u16, u8):
bool[64] d_bits = to_bits_64(d)
bool[32] e_bits = to_bits_32(e)
bool[16] f_bits = to_bits_16(f)
bool[8] g_bits = to_bits_8(g)
return from_bits_32(e_bits), from_bits_16(f_bits), from_bits_8(g_bits)
return from_bits_64(d_bits), from_bits_32(e_bits), from_bits_16(f_bits), from_bits_8(g_bits)

View file

@ -1,6 +1,6 @@
{
"entry_point": "./tests/tests/uint/if_else.zok",
"max_constraint_count": 28,
"max_constraint_count": 31,
"tests": [
{
"input": {

View file

@ -1,16 +0,0 @@
{
"entry_point": "./tests/tests/uint/mul.zok",
"max_constraint_count": 37,
"tests": [
{
"input": {
"values": ["2", "2"]
},
"output": {
"Ok": {
"values": ["4"]
}
}
}
]
}

View file

@ -1,36 +0,0 @@
{
"entry_point": "./tests/tests/uint/or.zok",
"max_constraint_count": 27,
"tests": [
{
"input": {
"values": ["0xff", "0xff"]
},
"output": {
"Ok": {
"values": ["0xff"]
}
}
},
{
"input": {
"values": ["0xff", "0x00"]
},
"output": {
"Ok": {
"values": ["0xff"]
}
}
},
{
"input": {
"values": ["0x23", "0x34"]
},
"output": {
"Ok": {
"values": ["0x37"]
}
}
}
]
}

View file

@ -1,26 +0,0 @@
{
"entry_point": "./tests/tests/uint/rem.zok",
"max_constraint_count": 43,
"tests": [
{
"input": {
"values": ["255", "1"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
},
{
"input": {
"values": ["42", "10"]
},
"output": {
"Ok": {
"values": ["2"]
}
}
}
]
}

View file

@ -1,7 +0,0 @@
def main(u8 x, u8 y) -> u8:
assert(0x02 % 0x02 == 0x00)
assert(0x04 % 0x02 == 0x00)
assert(0x05 % 0x02 == 0x01)
assert(0xff % 0x03 == 0x00)
assert(0xff % 0x01 == 0x00)
return x % y

View file

@ -1,26 +0,0 @@
{
"entry_point": "./tests/tests/uint/shift.zok",
"max_constraint_count": 34,
"tests": [
{
"input": {
"values": ["0x12345678"]
},
"output": {
"Ok": {
"values": ["0x01234567"]
}
}
},
{
"input": {
"values": ["0x01234567"]
},
"output": {
"Ok": {
"values": ["0x00123456"]
}
}
}
]
}

View file

@ -0,0 +1,36 @@
{
"entry_point": "./tests/tests/uint/u16/add.zok",
"max_constraint_count": 53,
"tests": [
{
"input": {
"values": ["0xffff", "0x0001"]
},
"output": {
"Ok": {
"values": ["0x0000"]
}
}
},
{
"input": {
"values": ["0x1000", "0x1000"]
},
"output": {
"Ok": {
"values": ["0x2000"]
}
}
},
{
"input": {
"values": ["0xffff", "0xffff"]
},
"output": {
"Ok": {
"values": ["0xfffe"]
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main(u16 a, u16 b) -> u16:
return a + b

View file

@ -0,0 +1,36 @@
{
"entry_point": "./tests/tests/uint/u16/and.zok",
"max_constraint_count": 51,
"tests": [
{
"input": {
"values": ["0xffff", "0xffff"]
},
"output": {
"Ok": {
"values": ["0xffff"]
}
}
},
{
"input": {
"values": ["0xffff", "0x0000"]
},
"output": {
"Ok": {
"values": ["0x0000"]
}
}
},
{
"input": {
"values": ["0x1234", "0x5678"]
},
"output": {
"Ok": {
"values": ["0x1230"]
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main(u16 a, u16 b) -> u16:
return a & b

View file

@ -0,0 +1,36 @@
{
"entry_point": "./tests/tests/uint/u16/div.zok",
"max_constraint_count": 88,
"tests": [
{
"input": {
"values": ["0x1000", "0x1000"]
},
"output": {
"Ok": {
"values": ["0x0001"]
}
}
},
{
"input": {
"values": ["0x1000", "0x0002"]
},
"output": {
"Ok": {
"values": ["0x0800"]
}
}
},
{
"input": {
"values": ["0x1001", "0x0002"]
},
"output": {
"Ok": {
"values": ["0x0800"]
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main(u16 x, u16 y) -> u16:
return x / y

View file

@ -0,0 +1,26 @@
{
"entry_point": "./tests/tests/uint/u16/eq.zok",
"max_constraint_count": 37,
"tests": [
{
"input": {
"values": ["0x0002", "0x0002"]
},
"output": {
"Ok": {
"values": ["1"]
}
}
},
{
"input": {
"values": ["0x0002", "0x0004"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main(u16 a, u16 b) -> bool:
return a == b

View file

@ -0,0 +1,26 @@
{
"entry_point": "./tests/tests/uint/u16/gt.zok",
"max_constraint_count": 697,
"tests": [
{
"input": {
"values": ["0x0004", "0x0002"]
},
"output": {
"Ok": {
"values": ["1"]
}
}
},
{
"input": {
"values": ["0x0002", "0x0002"]
},
"output": {
"Ok": {
"values": ["0"]
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main(u16 a, u16 b) -> bool:
return a > b

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