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

Merge branch 'develop' of github.com:Zokrates/ZoKrates into zokrates-test-crate

This commit is contained in:
schaeff 2019-09-17 12:12:27 +02:00
commit 5d40fa7083
43 changed files with 3604 additions and 3103 deletions

51
Cargo.lock generated
View file

@ -167,7 +167,7 @@ name = "bstr"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
@ -203,7 +203,7 @@ name = "c2-chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -277,7 +277,7 @@ name = "colored"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -381,7 +381,7 @@ dependencies = [
"arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -400,7 +400,7 @@ version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -824,12 +824,7 @@ dependencies = [
[[package]]
name = "lazy_static"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -984,7 +979,7 @@ dependencies = [
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -996,7 +991,7 @@ name = "native-tls"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1108,7 +1103,7 @@ dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1274,7 +1269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1487,7 +1482,7 @@ dependencies = [
"crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1668,7 +1663,7 @@ name = "schannel"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1900,7 +1895,7 @@ name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1984,7 +1979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2057,6 +2052,11 @@ dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "typed-arena"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typenum"
version = "1.10.0"
@ -2240,7 +2240,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2293,7 +2293,7 @@ dependencies = [
"ff_ce 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2307,6 +2307,7 @@ dependencies = [
"serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"typed-arena 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"zokrates_embed 0.1.0",
"zokrates_field 0.3.3",
@ -2335,7 +2336,7 @@ version = "0.3.3"
dependencies = [
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ff_ce 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2377,7 +2378,7 @@ version = "0.1.2"
dependencies = [
"from-pest 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pest-ast 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"zokrates_field 0.3.3",
@ -2503,8 +2504,7 @@ dependencies = [
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum libgit2-sys 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "48441cb35dc255da8ae72825689a95368bf510659ae1ad55dc4aa88cb1789bf1"
"checksum libssh2-sys 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "126a1f4078368b163bfdee65fbab072af08a1b374a5551b21e87ade27b1fbf9d"
@ -2637,6 +2637,7 @@ dependencies = [
"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e"
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b"
"checksum typed-arena 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c6c06a92aef38bb4dc5b0df00d68496fc31307c5344c867bb61678c6e1671ec5"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2"
"checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874"

View file

@ -71,11 +71,19 @@ import "utils/pack/unpack128"
Unpacks a field element to 128 field elements.
#### unpack256
#### pack256
```zokrates
import "utils/pack/unpack256"
import "utils/pack/pack256"
```
Unpacks a field element to 256 field elements.
Packs 256 field elements as one. Overflows can occur.
#### nonStrictUnpack256
```zokrates
import "utils/pack/nonStrictUnpack256"
```
Unpacks a field element to 256 field elements. Uniqueness of the output is not guaranteed.

View file

@ -58,4 +58,4 @@ Then run the different phases of the protocol:
./zokrates export-verifier
```
The CLI commands are explained in more detail in the [CLI reference](reference/cli.html).
The CLI commands are explained in more detail in the [CLI reference](reference/cli.md).

View file

@ -27,10 +27,10 @@ fn main() {
})
}
fn resolve(
location: &Option<String>,
source: &String,
) -> Result<(BufReader<File>, String, String), io::Error> {
fn resolve<'a>(
location: Option<String>,
source: &'a str,
) -> Result<(BufReader<File>, String, &'a str), io::Error> {
#[cfg(feature = "github")]
{
if is_github_import(source) {

View file

@ -1,4 +1,4 @@
import "BELLMAN/sha256round" as sha256
import "EMBED/sha256round" as sha256
def main(private field[256] expected) -> (field):

View file

@ -15,7 +15,8 @@ wasm = ["wasmi", "parity-wasm", "rustc-hex"]
libc = "0.2.0"
num = {version = "0.1.36", default-features = false}
num-bigint = {version = "0.1.36", default-features = false}
lazy_static = "0.1.*"
lazy_static = "1.4"
typed-arena = "1.4.1"
reduce = "0.1.1"
# serialization and deserialization
serde = "1.0"

View file

@ -4,36 +4,34 @@ use types::Type;
use zokrates_field::field::Field;
use zokrates_pest_ast as pest;
impl<'ast, T: Field> From<pest::File<'ast>> for absy::Prog<'ast, T> {
fn from(prog: pest::File<'ast>) -> absy::Prog<T> {
absy::Prog {
impl<'ast, T: Field> From<pest::File<'ast>> for absy::Module<'ast, T> {
fn from(prog: pest::File<'ast>) -> absy::Module<T> {
absy::Module {
functions: prog
.functions
.into_iter()
.map(|f| absy::FunctionNode::from(f))
.map(|f| absy::FunctionDeclarationNode::from(f))
.collect(),
imports: prog
.imports
.into_iter()
.map(|i| absy::ImportNode::from(i))
.collect(),
imported_functions: vec![],
}
}
}
impl<'ast> From<pest::ImportDirective<'ast>> for absy::ImportNode {
impl<'ast> From<pest::ImportDirective<'ast>> for absy::ImportNode<'ast> {
fn from(import: pest::ImportDirective<'ast>) -> absy::ImportNode {
use absy::NodeValue;
imports::Import::new(import.source.value)
.alias(import.alias.map(|a| a.value))
imports::Import::new(import.source.span.as_str())
.alias(import.alias.map(|a| a.span.as_str()))
.span(import.span)
}
}
impl<'ast, T: Field> From<pest::Function<'ast>> for absy::FunctionNode<'ast, T> {
fn from(function: pest::Function<'ast>) -> absy::FunctionNode<T> {
impl<'ast, T: Field> From<pest::Function<'ast>> for absy::FunctionDeclarationNode<'ast, T> {
fn from(function: pest::Function<'ast>) -> absy::FunctionDeclarationNode<T> {
use absy::NodeValue;
let span = function.span;
@ -56,8 +54,9 @@ impl<'ast, T: Field> From<pest::Function<'ast>> for absy::FunctionNode<'ast, T>
.collect(),
);
absy::Function::<T> {
id: function.id.span.as_str(),
let id = function.id.span.as_str();
let function = absy::Function::<T> {
arguments: function
.parameters
.into_iter()
@ -70,6 +69,12 @@ impl<'ast, T: Field> From<pest::Function<'ast>> for absy::FunctionNode<'ast, T>
.collect(),
signature,
}
.span(span.clone());
absy::FunctionDeclaration {
id,
symbol: absy::FunctionSymbol::Here(function),
}
.span(span)
}
}
@ -132,7 +137,7 @@ fn statements_from_multi_assignment<'ast, T: Field>(
let multi_def = absy::Statement::MultipleDefinition(
lhs,
absy::Expression::FunctionCall(
assignment.function_id.value,
&assignment.function_id.span.as_str(),
assignment
.arguments
.into_iter()
@ -447,7 +452,7 @@ impl<'ast, T: Field> From<pest::PostfixExpression<'ast>> for absy::ExpressionNod
match expression.access[0].clone() {
pest::Access::Call(a) => absy::Expression::FunctionCall(
expression.id.value,
&expression.id.span.as_str(),
a.expressions
.into_iter()
.map(|e| absy::ExpressionNode::from(e))
@ -549,92 +554,108 @@ mod tests {
#[test]
fn return_forty_two() {
let source = "def main() -> (field): return 42
";
let source = "def main() -> (field): return 42";
let ast = pest::generate_ast(&source).unwrap();
let expected: absy::Prog<FieldPrime> = absy::Prog {
functions: vec![absy::Function {
let expected: absy::Module<FieldPrime> = absy::Module {
functions: vec![absy::FunctionDeclaration {
id: &source[4..8],
arguments: vec![],
statements: vec![absy::Statement::Return(
absy::ExpressionList {
expressions: vec![
absy::Expression::FieldConstant(FieldPrime::from(42)).into()
],
symbol: absy::FunctionSymbol::Here(
absy::Function {
arguments: vec![],
statements: vec![absy::Statement::Return(
absy::ExpressionList {
expressions: vec![absy::Expression::FieldConstant(
FieldPrime::from(42),
)
.into()],
}
.into(),
)
.into()],
signature: absy::Signature::new()
.inputs(vec![])
.outputs(vec![Type::FieldElement]),
}
.into(),
)
.into()],
signature: absy::Signature::new()
.inputs(vec![])
.outputs(vec![Type::FieldElement]),
),
}
.into()],
imports: vec![],
imported_functions: vec![],
};
assert_eq!(absy::Prog::<FieldPrime>::from(ast), expected);
assert_eq!(absy::Module::<FieldPrime>::from(ast), expected);
}
#[test]
fn return_true() {
let source = "def main() -> (bool): return true
";
let source = "def main() -> (bool): return true";
let ast = pest::generate_ast(&source).unwrap();
let expected: absy::Prog<FieldPrime> = absy::Prog {
functions: vec![absy::Function {
let expected: absy::Module<FieldPrime> = absy::Module {
functions: vec![absy::FunctionDeclaration {
id: &source[4..8],
arguments: vec![],
statements: vec![absy::Statement::Return(
absy::ExpressionList {
expressions: vec![absy::Expression::BooleanConstant(true).into()],
symbol: absy::FunctionSymbol::Here(
absy::Function {
arguments: vec![],
statements: vec![absy::Statement::Return(
absy::ExpressionList {
expressions: vec![absy::Expression::BooleanConstant(true).into()],
}
.into(),
)
.into()],
signature: absy::Signature::new()
.inputs(vec![])
.outputs(vec![Type::Boolean]),
}
.into(),
)
.into()],
signature: absy::Signature::new()
.inputs(vec![])
.outputs(vec![Type::Boolean]),
),
}
.into()],
imports: vec![],
imported_functions: vec![],
};
assert_eq!(absy::Prog::<FieldPrime>::from(ast), expected);
assert_eq!(absy::Module::<FieldPrime>::from(ast), expected);
}
#[test]
fn arguments() {
let source = "def main(private field a, bool b) -> (field): return 42
";
let source = "def main(private field a, bool b) -> (field): return 42";
let ast = pest::generate_ast(&source).unwrap();
let expected: absy::Prog<FieldPrime> = absy::Prog {
functions: vec![absy::Function {
let expected: absy::Module<FieldPrime> = absy::Module {
functions: vec![absy::FunctionDeclaration {
id: &source[4..8],
arguments: vec![
absy::Parameter::private(absy::Variable::field_element(&source[23..24]).into())
.into(),
absy::Parameter::public(absy::Variable::boolean(&source[31..32]).into()).into(),
],
statements: vec![absy::Statement::Return(
absy::ExpressionList {
expressions: vec![
absy::Expression::FieldConstant(FieldPrime::from(42)).into()
symbol: absy::FunctionSymbol::Here(
absy::Function {
arguments: vec![
absy::Parameter::private(
absy::Variable::field_element(&source[23..24]).into(),
)
.into(),
absy::Parameter::public(
absy::Variable::boolean(&source[31..32]).into(),
)
.into(),
],
statements: vec![absy::Statement::Return(
absy::ExpressionList {
expressions: vec![absy::Expression::FieldConstant(
FieldPrime::from(42),
)
.into()],
}
.into(),
)
.into()],
signature: absy::Signature::new()
.inputs(vec![Type::FieldElement, Type::Boolean])
.outputs(vec![Type::FieldElement]),
}
.into(),
)
.into()],
signature: absy::Signature::new()
.inputs(vec![Type::FieldElement, Type::Boolean])
.outputs(vec![Type::FieldElement]),
),
}
.into()],
imports: vec![],
imported_functions: vec![],
};
assert_eq!(absy::Prog::<FieldPrime>::from(ast), expected);
assert_eq!(absy::Module::<FieldPrime>::from(ast), expected);
}
}

View file

@ -13,24 +13,103 @@ pub mod variable;
pub use crate::absy::node::{Node, NodeValue};
pub use crate::absy::parameter::{Parameter, ParameterNode};
pub use crate::absy::variable::{Variable, VariableNode};
use crate::types::Signature;
use crate::types::{FunctionIdentifier, Signature};
use embed::FlatEmbed;
use crate::flat_absy::*;
use crate::imports::ImportNode;
use std::fmt;
use zokrates_field::field::Field;
use std::collections::HashMap;
/// An identifier of a function or a variable
pub type Identifier<'ast> = &'ast str;
#[derive(Clone, PartialEq)]
pub struct Prog<'ast, T: Field> {
/// Functions of the program
pub functions: Vec<FunctionNode<'ast, T>>,
pub imports: Vec<ImportNode>,
pub imported_functions: Vec<FlatFunction<T>>,
/// The identifier of a `Module`, typically a path or uri
pub type ModuleId = String;
/// A collection of `Module`s
pub type Modules<'ast, T> = HashMap<ModuleId, Module<'ast, T>>;
/// A collection of `FunctionDeclaration`. Duplicates are allowed here as they are fine syntatically.
pub type FunctionDeclarations<'ast, T> = Vec<FunctionDeclarationNode<'ast, T>>;
/// A `Program` is a collection of `Module`s and an id of the main `Module`
pub struct Program<'ast, T: Field> {
pub modules: HashMap<ModuleId, Module<'ast, T>>,
pub main: ModuleId,
}
impl<'ast, T: Field> fmt::Display for Prog<'ast, T> {
/// A declaration of a `FunctionSymbol`, be it from an import or a function definition
#[derive(PartialEq, Debug, Clone)]
pub struct FunctionDeclaration<'ast, T: Field> {
pub id: Identifier<'ast>,
pub symbol: FunctionSymbol<'ast, T>,
}
impl<'ast, T: Field> fmt::Display for FunctionDeclaration<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.symbol {
FunctionSymbol::Here(ref fun) => write!(f, "def {}{}", self.id, fun),
FunctionSymbol::There(ref import) => write!(f, "import {} as {}", import, self.id),
FunctionSymbol::Flat(ref flat_fun) => write!(
f,
"def {}{}:\n\t// hidden",
self.id,
flat_fun.signature::<T>()
),
}
}
}
type FunctionDeclarationNode<'ast, T> = Node<FunctionDeclaration<'ast, T>>;
/// A module as a collection of `FunctionDeclaration`s
#[derive(Clone, PartialEq)]
pub struct Module<'ast, T: Field> {
/// Functions of the module
pub functions: FunctionDeclarations<'ast, T>,
pub imports: Vec<ImportNode<'ast>>, // we still use `imports` as they are not directly converted into `FunctionDeclaration`s after the importer is done, `imports` is empty
}
/// A function, be it defined in this module, imported from another module or a flat embed
#[derive(Debug, Clone, PartialEq)]
pub enum FunctionSymbol<'ast, T: Field> {
Here(FunctionNode<'ast, T>),
There(FunctionImportNode<'ast>),
Flat(FlatEmbed),
}
/// A function import
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionImport<'ast> {
/// the id of the function in the target module. Note: there may be many candidates as imports statements do not specify the signature
pub function_id: Identifier<'ast>,
/// the id of the module to import from
pub module_id: ModuleId,
}
type FunctionImportNode<'ast> = Node<FunctionImport<'ast>>;
impl<'ast> FunctionImport<'ast> {
pub fn with_id_in_module<S: Into<Identifier<'ast>>, U: Into<ModuleId>>(
function_id: S,
module_id: U,
) -> Self {
FunctionImport {
function_id: function_id.into(),
module_id: module_id.into(),
}
}
}
impl<'ast> fmt::Display for FunctionImport<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} from {}", self.function_id, self.module_id)
}
}
impl<'ast, T: Field> fmt::Display for Module<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut res = vec![];
res.extend(
@ -39,12 +118,6 @@ impl<'ast, T: Field> fmt::Display for Prog<'ast, T> {
.map(|x| format!("{}", x))
.collect::<Vec<_>>(),
);
res.extend(
self.imported_functions
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>(),
);
res.extend(
self.functions
.iter()
@ -55,21 +128,16 @@ impl<'ast, T: Field> fmt::Display for Prog<'ast, T> {
}
}
impl<'ast, T: Field> fmt::Debug for Prog<'ast, T> {
impl<'ast, T: Field> fmt::Debug for Module<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"program(\n\timports:\n\t\t{}\n\tfunctions:\n\t\t{}{}\n)",
"module(\n\timports:\n\t\t{}\n\tfunctions:\n\t\t{}\n)",
self.imports
.iter()
.map(|x| format!("{:?}", x))
.collect::<Vec<_>>()
.join("\n\t\t"),
self.imported_functions
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join("\n\t\t"),
self.functions
.iter()
.map(|x| format!("{:?}", x))
@ -79,10 +147,9 @@ impl<'ast, T: Field> fmt::Debug for Prog<'ast, T> {
}
}
/// A function defined locally
#[derive(Clone, PartialEq)]
pub struct Function<'ast, T: Field> {
/// Name of the program
pub id: Identifier<'ast>,
/// Arguments of the function
pub arguments: Vec<ParameterNode<'ast>>,
/// Vector of statements that are executed when running the function
@ -97,8 +164,7 @@ impl<'ast, T: Field> fmt::Display for Function<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"def {}({}):\n{}",
self.id,
"({}):\n{}",
self.arguments
.iter()
.map(|x| format!("{}", x))
@ -117,8 +183,7 @@ impl<'ast, T: Field> fmt::Debug for Function<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Function(id: {:?}, arguments: {:?}, ...):\n{}",
self.id,
"Function(arguments: {:?}, ...):\n{}",
self.arguments,
self.statements
.iter()
@ -129,6 +194,7 @@ impl<'ast, T: Field> fmt::Debug for Function<'ast, T> {
}
}
/// Something that we can assign to
#[derive(Clone, PartialEq)]
pub enum Assignee<'ast, T: Field> {
Identifier(Identifier<'ast>),
@ -152,6 +218,7 @@ impl<'ast, T: Field> fmt::Display for Assignee<'ast, T> {
}
}
/// A statement in a `Function`
#[derive(Clone, PartialEq)]
pub enum Statement<'ast, T: Field> {
Return(ExpressionListNode<'ast, T>),
@ -214,6 +281,7 @@ impl<'ast, T: Field> fmt::Debug for Statement<'ast, T> {
}
}
/// An element of an inline array, can be a spread `...a` or an expression `a`
#[derive(Clone, PartialEq)]
pub enum SpreadOrExpression<'ast, T: Field> {
Spread(SpreadNode<'ast, T>),
@ -238,6 +306,7 @@ impl<'ast, T: Field> fmt::Debug for SpreadOrExpression<'ast, T> {
}
}
/// The index in an array selector. Can be a range or an expression.
#[derive(Clone, PartialEq)]
pub enum RangeOrExpression<'ast, T: Field> {
Range(RangeNode<T>),
@ -276,11 +345,13 @@ impl<'ast, T: Field> fmt::Debug for Spread<'ast, T> {
}
}
/// A spread
#[derive(Clone, PartialEq)]
pub struct Spread<'ast, T: Field> {
pub expression: ExpressionNode<'ast, T>,
}
/// A range
#[derive(Clone, PartialEq)]
pub struct Range<T: Field> {
pub from: Option<T>,
@ -312,6 +383,7 @@ impl<'ast, T: Field> fmt::Debug for Range<T> {
}
}
/// An expression
#[derive(Clone, PartialEq)]
pub enum Expression<'ast, T: Field> {
FieldConstant(T),
@ -327,7 +399,7 @@ pub enum Expression<'ast, T: Field> {
Box<ExpressionNode<'ast, T>>,
Box<ExpressionNode<'ast, T>>,
),
FunctionCall(String, Vec<ExpressionNode<'ast, T>>),
FunctionCall(FunctionIdentifier<'ast>, Vec<ExpressionNode<'ast, T>>),
Lt(Box<ExpressionNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
Le(Box<ExpressionNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
Eq(Box<ExpressionNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
@ -433,6 +505,7 @@ impl<'ast, T: Field> fmt::Debug for Expression<'ast, T> {
}
}
/// A list of expressions, used in return statements
#[derive(Clone, PartialEq)]
pub struct ExpressionList<'ast, T: Field> {
pub expressions: Vec<ExpressionNode<'ast, T>>,

View file

@ -33,6 +33,15 @@ pub trait NodeValue: fmt::Display + Sized + PartialEq {
Node::new(start, start.col(delta), self)
}
fn start_end(self, start: Position, end: Position) -> Node<Self> {
Node::new(start, end, self)
}
#[cfg(test)]
fn mock(self) -> Node<Self> {
Node::new(Position::mock(), Position::mock(), self)
}
fn span(self, span: Span) -> Node<Self> {
let from = span.start_pos().line_col();
let to = span.end_pos().line_col();
@ -50,7 +59,6 @@ pub trait NodeValue: fmt::Display + Sized + PartialEq {
}
}
#[cfg(test)]
impl<V: NodeValue> From<V> for Node<V> {
fn from(v: V) -> Node<V> {
let mock_position = Position { col: 42, line: 42 };
@ -66,13 +74,15 @@ impl<'ast, T: Field> NodeValue for Expression<'ast, T> {}
impl<'ast, T: Field> NodeValue for ExpressionList<'ast, T> {}
impl<'ast, T: Field> NodeValue for Assignee<'ast, T> {}
impl<'ast, T: Field> NodeValue for Statement<'ast, T> {}
impl<'ast, T: Field> NodeValue for FunctionDeclaration<'ast, T> {}
impl<'ast, T: Field> NodeValue for Function<'ast, T> {}
impl<'ast, T: Field> NodeValue for Prog<'ast, T> {}
impl<'ast, T: Field> NodeValue for Spread<'ast, T> {}
impl<T: Field> NodeValue for Range<T> {}
impl<'ast, T: Field> NodeValue for Module<'ast, T> {}
impl<'ast> NodeValue for FunctionImport<'ast> {}
impl<'ast> NodeValue for Variable<'ast> {}
impl<'ast> NodeValue for Parameter<'ast> {}
impl NodeValue for Import {}
impl<'ast> NodeValue for Import<'ast> {}
impl<'ast, T: Field> NodeValue for Spread<'ast, T> {}
impl<T: Field> NodeValue for Range<T> {}
impl<T: NodeValue> std::cmp::PartialEq for Node<T> {
fn eq(&self, other: &Node<T>) -> bool {

View file

@ -1,6 +1,6 @@
use crate::absy::Node;
use crate::types::Type;
use std::fmt;
use types::Type;
use crate::absy::Identifier;

View file

@ -3,17 +3,18 @@
//! @file compile.rs
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
//! @date 2018
use absy::Prog;
use flat_absy::FlatProg;
use absy::{Module, ModuleId, Program};
use flatten::Flattener;
use imports::{self, Importer};
use ir;
use optimizer::Optimize;
use semantics::{self, Checker};
use static_analysis::Analyse;
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::io::BufRead;
use typed_arena::Arena;
use zokrates_field::field::Field;
use zokrates_pest_ast as pest;
@ -123,34 +124,24 @@ impl fmt::Display for CompileErrorInner {
}
}
pub type Resolve<S, E> = fn(Option<String>, &str) -> Result<(S, String, &str), E>;
pub fn compile<T: Field, R: BufRead, S: BufRead, E: Into<imports::Error>>(
reader: &mut R,
location: Option<String>,
resolve_option: Option<fn(&Option<String>, &String) -> Result<(S, String, String), E>>,
resolve_option: Option<Resolve<S, E>>,
) -> Result<ir::Prog<T>, CompileErrors> {
let compiled = compile_aux(reader, location, resolve_option)?;
Ok(ir::Prog::from(compiled).optimize())
}
let arena = Arena::new();
pub fn compile_aux<T: Field, R: BufRead, S: BufRead, E: Into<imports::Error>>(
reader: &mut R,
location: Option<String>,
resolve_option: Option<fn(&Option<String>, &String) -> Result<(S, String, String), E>>,
) -> Result<FlatProg<T>, CompileErrors> {
let mut source = String::new();
reader.read_to_string(&mut source).unwrap();
let ast = pest::generate_ast(&source)
.map_err(|e| CompileErrors::from(CompileErrorInner::from(e).with_context(&location)))?;
let program_ast_without_imports: Prog<T> = Prog::from(ast);
let program_ast = Importer::new().apply_imports(
program_ast_without_imports,
location.clone(),
resolve_option,
)?;
let source = arena.alloc(source);
let compiled = compile_program(source, location.clone(), resolve_option, &arena)?;
// check semantics
let typed_ast = Checker::check(program_ast).map_err(|errors| {
let typed_ast = Checker::check(compiled).map_err(|errors| {
CompileErrors(
errors
.into_iter()
@ -168,7 +159,59 @@ pub fn compile_aux<T: Field, R: BufRead, S: BufRead, E: Into<imports::Error>>(
// analyse (constant propagation after call resolution)
let program_flattened = program_flattened.analyse();
Ok(program_flattened)
// convert to ir
let ir_prog = ir::Prog::from(program_flattened);
// optimize
let optimized_ir_prog = ir_prog.optimize();
Ok(optimized_ir_prog)
}
pub fn compile_program<'ast, T: Field, S: BufRead, E: Into<imports::Error>>(
source: &'ast str,
location: Option<String>,
resolve_option: Option<Resolve<S, E>>,
arena: &'ast Arena<String>,
) -> Result<Program<'ast, T>, CompileErrors> {
let mut modules = HashMap::new();
let main = compile_module(
&source,
location.clone(),
resolve_option,
&mut modules,
&arena,
)?;
let location = location.unwrap_or("???".to_string());
modules.insert(location.clone(), main);
Ok(Program {
main: location,
modules,
})
}
pub fn compile_module<'ast, T: Field, S: BufRead, E: Into<imports::Error>>(
source: &'ast str,
location: Option<String>,
resolve_option: Option<Resolve<S, E>>,
modules: &mut HashMap<ModuleId, Module<'ast, T>>,
arena: &'ast Arena<String>,
) -> Result<Module<'ast, T>, CompileErrors> {
let ast = pest::generate_ast(&source)
.map_err(|e| CompileErrors::from(CompileErrorInner::from(e).with_context(&location)))?;
let module_without_imports: Module<T> = Module::from(ast);
Importer::new().apply_imports(
module_without_imports,
location.clone(),
resolve_option,
modules,
&arena,
)
}
#[cfg(test)]
@ -190,12 +233,7 @@ mod test {
let res: Result<ir::Prog<FieldPrime>, CompileErrors> = compile(
&mut r,
Some(String::from("./path/to/file")),
None::<
fn(
&Option<String>,
&String,
) -> Result<(BufReader<Empty>, String, String), io::Error>,
>,
None::<Resolve<BufReader<Empty>, io::Error>>,
);
assert!(res
@ -216,12 +254,7 @@ mod test {
let res: Result<ir::Prog<FieldPrime>, CompileErrors> = compile(
&mut r,
Some(String::from("./path/to/file")),
None::<
fn(
&Option<String>,
&String,
) -> Result<(BufReader<Empty>, String, String), io::Error>,
>,
None::<Resolve<BufReader<Empty>, io::Error>>,
);
assert!(res.is_ok());
}

426
zokrates_core/src/embed.rs Normal file
View file

@ -0,0 +1,426 @@
use crate::helpers::{DirectiveStatement, Helper, RustHelper};
use bellman::pairing::ff::ScalarEngine;
use flat_absy::{
FlatExpression, FlatExpressionList, FlatFunction, FlatParameter, FlatStatement, FlatVariable,
};
use reduce::Reduce;
use std::collections::HashMap;
use types::{FunctionKey, Signature, Type};
use zokrates_embed::{generate_sha256_round_constraints, BellmanConstraint};
use zokrates_field::field::Field;
/// A low level function that contains non-deterministic introduction of variables. It is carried as is until
/// the flattening step when it can be inlined.
#[derive(Debug, Clone, PartialEq)]
pub enum FlatEmbed {
Sha256Round,
Unpack,
}
impl FlatEmbed {
pub fn signature<T: Field>(&self) -> Signature {
match self {
FlatEmbed::Sha256Round => Signature::new()
.inputs(vec![
Type::FieldElementArray(512),
Type::FieldElementArray(256),
])
.outputs(vec![Type::FieldElementArray(256)]),
FlatEmbed::Unpack => Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElementArray(T::get_required_bits())]),
}
}
pub fn key<T: Field>(&self) -> FunctionKey<'static> {
FunctionKey::with_id(self.id()).signature(self.signature::<T>())
}
pub fn id(&self) -> &'static str {
match self {
FlatEmbed::Sha256Round => "_SHA256_ROUND",
FlatEmbed::Unpack => "_UNPACK",
}
}
/// Actually get the `FlatFunction` that this `FlatEmbed` represents
pub fn synthetize<T: Field>(&self) -> FlatFunction<T> {
match self {
FlatEmbed::Sha256Round => sha256_round(),
FlatEmbed::Unpack => unpack(),
}
}
}
// util to convert a vector of `(variable_id, coefficient)` to a flat_expression
fn flat_expression_from_vec<T: Field>(
v: Vec<(usize, <<T as Field>::BellmanEngine as ScalarEngine>::Fr)>,
) -> FlatExpression<T> {
match v
.into_iter()
.map(|(key, val)| {
FlatExpression::Mult(
box FlatExpression::Number(T::from_bellman(val)),
box FlatExpression::Identifier(FlatVariable::new(key)),
)
})
.reduce(|acc, e| FlatExpression::Add(box acc, box e))
{
Some(e @ FlatExpression::Mult(..)) => {
FlatExpression::Add(box FlatExpression::Number(T::zero()), box e)
} // the R1CS serializer only recognizes Add
Some(e) => e,
None => FlatExpression::Number(T::zero()),
}
}
impl<T: Field> From<BellmanConstraint<T::BellmanEngine>> for FlatStatement<T> {
fn from(c: zokrates_embed::BellmanConstraint<T::BellmanEngine>) -> FlatStatement<T> {
let rhs_a = flat_expression_from_vec(c.a);
let rhs_b = flat_expression_from_vec(c.b);
let lhs = flat_expression_from_vec(c.c);
FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b))
}
}
/// Returns a flat function which computes a sha256 round
///
/// # Remarks
///
/// The variables inside the function are set in this order:
/// - constraint system variables
/// - arguments
pub fn sha256_round<T: Field>() -> FlatFunction<T> {
// Define iterators for all indices at hand
let (r1cs, input_indices, current_hash_indices, output_indices) =
generate_sha256_round_constraints::<T::BellmanEngine>();
// indices of the input
let input_indices = input_indices.into_iter();
// indices of the current hash
let current_hash_indices = current_hash_indices.into_iter();
// indices of the output
let output_indices = output_indices.into_iter();
let variable_count = r1cs.aux_count + 1; // auxiliary and ONE
// indices of the sha256round constraint system variables
let cs_indices = (0..variable_count).into_iter();
// indices of the arguments to the function
// apply an offset of `variable_count` to get the indice of our dummy `input` argument
let input_argument_indices = input_indices
.clone()
.into_iter()
.map(|i| i + variable_count);
// apply an offset of `variable_count` to get the indice of our dummy `current_hash` argument
let current_hash_argument_indices = current_hash_indices
.clone()
.into_iter()
.map(|i| i + variable_count);
// define the signature of the resulting function
let signature = Signature {
inputs: vec![
Type::FieldElementArray(input_indices.len()),
Type::FieldElementArray(current_hash_indices.len()),
],
outputs: vec![Type::FieldElementArray(output_indices.len())],
};
// define parameters to the function based on the variables
let arguments = input_argument_indices
.clone()
.chain(current_hash_argument_indices.clone())
.map(|i| FlatParameter {
id: FlatVariable::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(),
FlatExpression::Number(T::from(1)),
);
let input_binding_statements =
// bind input and current_hash to inputs
input_indices.clone().chain(current_hash_indices).zip(input_argument_indices.clone().chain(current_hash_argument_indices.clone())).map(|(cs_index, argument_index)| {
FlatStatement::Condition(
FlatVariable::new(cs_index).into(),
FlatVariable::new(argument_index).into(),
)
});
// insert flattened statements to represent constraints
let constraint_statements = r1cs.constraints.into_iter().map(|c| c.into());
// define which subset of the witness is returned
let outputs: Vec<FlatExpression<T>> = output_indices
.map(|o| FlatExpression::Identifier(FlatVariable::new(o)))
.collect();
// insert a directive to set the witness based on the bellman gadget and inputs
let directive_statement = FlatStatement::Directive(DirectiveStatement {
outputs: cs_indices.map(|i| FlatVariable::new(i)).collect(),
inputs: input_argument_indices
.chain(current_hash_argument_indices)
.map(|i| FlatVariable::new(i).into())
.collect(),
helper: Helper::Rust(RustHelper::Sha256Round),
});
// insert a statement to return the subset of the witness
let return_statement = FlatStatement::Return(FlatExpressionList {
expressions: outputs,
});
let statements = std::iter::once(directive_statement)
.chain(std::iter::once(one_binding_statement))
.chain(input_binding_statements)
.chain(constraint_statements)
.chain(std::iter::once(return_statement))
.collect();
FlatFunction {
arguments,
statements,
signature,
}
}
fn use_variable(
layout: &mut HashMap<String, FlatVariable>,
name: String,
index: &mut usize,
) -> FlatVariable {
let var = FlatVariable::new(*index);
layout.insert(name, var);
*index = *index + 1;
var
}
/// A `FlatFunction` which returns a bit decomposition of a field element
///
/// # Remarks
/// * the return value of the `FlatFunction` is not deterministic: as we decompose over log_2(p) + 1 bits, some
/// elements can have multiple representations: For example, `unpack(0)` is `[0, ..., 0]` but also `unpack(p)`
pub fn unpack<T: Field>() -> FlatFunction<T> {
let nbits = T::get_required_bits();
let mut counter = 0;
let mut layout = HashMap::new();
let arguments = vec![FlatParameter {
id: FlatVariable::new(0),
private: true,
}];
// o0, ..., o253 = ToBits(i0)
let directive_inputs = vec![FlatExpression::Identifier(use_variable(
&mut layout,
format!("i0"),
&mut counter,
))];
let directive_outputs: Vec<FlatVariable> = (0..T::get_required_bits())
.map(|index| use_variable(&mut layout, format!("o{}", index), &mut counter))
.collect();
let helper = Helper::bits();
let signature = Signature {
inputs: vec![Type::FieldElement],
outputs: vec![Type::FieldElementArray(nbits)],
};
let outputs = directive_outputs
.iter()
.enumerate()
.filter(|(index, _)| *index >= T::get_required_bits() - nbits)
.map(|(_, o)| FlatExpression::Identifier(o.clone()))
.collect();
// o253, o252, ... o{253 - (nbits - 1)} are bits
let mut statements: Vec<FlatStatement<T>> = (0..nbits)
.map(|index| {
let bit = FlatExpression::Identifier(FlatVariable::new(T::get_required_bits() - index));
FlatStatement::Condition(
bit.clone(),
FlatExpression::Mult(box bit.clone(), box bit.clone()),
)
})
.collect();
// sum check: o253 + o252 * 2 + ... + o{253 - (nbits - 1)} * 2**(nbits - 1)
let mut lhs_sum = FlatExpression::Number(T::from(0));
for i in 0..nbits {
lhs_sum = FlatExpression::Add(
box lhs_sum,
box FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(T::get_required_bits() - i)),
box FlatExpression::Number(T::from(2).pow(i)),
),
);
}
statements.push(FlatStatement::Condition(
lhs_sum,
FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(0)),
box FlatExpression::Number(T::from(1)),
),
));
statements.insert(
0,
FlatStatement::Directive(DirectiveStatement {
inputs: directive_inputs,
outputs: directive_outputs,
helper: helper,
}),
);
statements.push(FlatStatement::Return(FlatExpressionList {
expressions: outputs,
}));
FlatFunction {
arguments,
statements,
signature,
}
}
#[cfg(test)]
mod tests {
use super::*;
use zokrates_field::field::FieldPrime;
#[cfg(test)]
mod split {
use super::*;
#[test]
fn split254() {
let unpack: FlatFunction<FieldPrime> = unpack();
assert_eq!(
unpack.arguments,
vec![FlatParameter::private(FlatVariable::new(0))]
);
assert_eq!(
unpack.statements.len(),
FieldPrime::get_required_bits() + 1 + 1 + 1
); // 128 bit checks, 1 directive, 1 sum check, 1 return
assert_eq!(
unpack.statements[0],
FlatStatement::Directive(DirectiveStatement::new(
(0..FieldPrime::get_required_bits())
.map(|i| FlatVariable::new(i + 1))
.collect(),
Helper::bits(),
vec![FlatVariable::new(0)]
))
);
assert_eq!(
*unpack.statements.last().unwrap(),
FlatStatement::Return(FlatExpressionList {
expressions: (0..FieldPrime::get_required_bits())
.map(|i| FlatExpression::Identifier(FlatVariable::new(i + 1)))
.collect()
})
);
}
}
#[cfg(test)]
mod sha256 {
use super::*;
#[test]
fn generate_sha256_constraints() {
let compiled = sha256_round();
// function should have a signature of 768 inputs and 256 outputs
assert_eq!(
compiled.signature,
Signature::new()
.inputs(vec![
Type::FieldElementArray(512),
Type::FieldElementArray(256)
])
.outputs(vec![Type::FieldElementArray(256)])
);
// function should have 768 inputs
assert_eq!(compiled.arguments.len(), 768,);
// function should return 256 values
assert_eq!(
compiled
.statements
.iter()
.filter_map(|s| match s {
FlatStatement::Return(v) => Some(v),
_ => None,
})
.next()
.unwrap()
.expressions
.len(),
256,
);
// directive should take 768 inputs and return n_var outputs
let directive = compiled
.statements
.iter()
.filter_map(|s| match s {
FlatStatement::Directive(d) => Some(d.clone()),
_ => None,
})
.next()
.unwrap();
assert_eq!(directive.inputs.len(), 768);
assert_eq!(directive.outputs.len(), 26935);
// function input should be offset by variable_count
assert_eq!(
compiled.arguments[0].id,
FlatVariable::new(directive.outputs.len() + 1)
);
// bellman variable #0: index 0 should equal 1
assert_eq!(
compiled.statements[1],
FlatStatement::Condition(
FlatVariable::new(0).into(),
FlatExpression::Number(FieldPrime::from(1))
)
);
// bellman input #0: index 1 should equal zokrates input #0: index v_count
assert_eq!(
compiled.statements[2],
FlatStatement::Condition(
FlatVariable::new(1).into(),
FlatVariable::new(26936).into()
)
);
let f = crate::ir::Function::from(compiled);
let prog = crate::ir::Prog {
main: f,
private: vec![true; 768],
};
let input = (0..512).map(|_| 0).chain((0..256).map(|_| 1)).collect();
prog.execute(&input).unwrap();
}
}
}

View file

@ -11,59 +11,32 @@ pub mod flat_variable;
pub use self::flat_parameter::FlatParameter;
pub use self::flat_variable::FlatVariable;
use crate::helpers::{DirectiveStatement, Executable};
use crate::helpers::DirectiveStatement;
use crate::types::Signature;
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::fmt;
use zokrates_field::field::Field;
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct FlatProg<T: Field> {
/// FlatFunctions of the program
pub functions: Vec<FlatFunction<T>>,
}
impl<T: Field> FlatProg<T> {
// only main flattened function is relevant here, as all other functions are unrolled into it
#[allow(dead_code)] // I don't want to remove this
pub fn get_witness(&self, inputs: Vec<T>) -> Result<BTreeMap<FlatVariable, T>, Error> {
let main = self.functions.iter().find(|x| x.id == "main").unwrap();
main.get_witness(inputs)
}
pub main: FlatFunction<T>,
}
impl<T: Field> fmt::Display for FlatProg<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.functions
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join("\n")
)
write!(f, "{}", self.main)
}
}
impl<T: Field> fmt::Debug for FlatProg<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"flat_program(functions: {}\t)",
self.functions
.iter()
.map(|x| format!("\t{:?}", x))
.collect::<Vec<_>>()
.join("\n")
)
write!(f, "flat_program(main: {}\t)", self.main)
}
}
#[derive(Clone, PartialEq)]
pub struct FlatFunction<T: Field> {
/// Name of the program
pub id: String,
/// Arguments of the function
pub arguments: Vec<FlatParameter>,
/// Vector of statements that are executed when running the function
@ -72,62 +45,11 @@ pub struct FlatFunction<T: Field> {
pub signature: Signature,
}
impl<T: Field> FlatFunction<T> {
pub fn get_witness(&self, inputs: Vec<T>) -> Result<BTreeMap<FlatVariable, T>, Error> {
assert!(self.arguments.len() == inputs.len());
assert!(self.id == "main");
let mut witness = BTreeMap::new();
witness.insert(FlatVariable::one(), T::one());
for (i, arg) in self.arguments.iter().enumerate() {
witness.insert(arg.id, inputs[i].clone());
}
for statement in &self.statements {
match *statement {
FlatStatement::Return(ref list) => {
for (i, val) in list.expressions.iter().enumerate() {
let s = val.solve(&mut witness);
witness.insert(FlatVariable::public(i), s);
}
}
FlatStatement::Definition(ref id, ref expr) => {
let s = expr.solve(&mut witness);
witness.insert(id.clone(), s);
}
FlatStatement::Condition(ref lhs, ref rhs) => {
if lhs.solve(&mut witness) != rhs.solve(&mut witness) {
return Err(Error {
message: format!(
"Condition not satisfied: {} should equal {}",
lhs, rhs
),
});
}
}
FlatStatement::Directive(ref d) => {
let input_values: Vec<T> =
d.inputs.iter().map(|i| i.solve(&mut witness)).collect();
match d.helper.execute(&input_values) {
Ok(res) => {
for (i, o) in d.outputs.iter().enumerate() {
witness.insert(o.clone(), res[i].clone());
}
continue;
}
Err(message) => return Err(Error { message: message }),
};
}
}
}
Ok(witness)
}
}
impl<T: Field> fmt::Display for FlatFunction<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"def {}({}):\n{}",
self.id,
"def main({}):\n{}",
self.arguments
.iter()
.map(|x| format!("{}", x))
@ -146,8 +68,7 @@ impl<T: Field> fmt::Debug for FlatFunction<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"FlatFunction(id: {:?}, arguments: {:?}, signature: {:?}):\n{}",
self.id,
"FlatFunction(arguments: {:?}, signature: {:?}):\n{}",
self.arguments,
self.signature,
self.statements
@ -272,19 +193,6 @@ impl<T: Field> FlatExpression<T> {
}
}
fn solve(&self, inputs: &mut BTreeMap<FlatVariable, T>) -> T {
match *self {
FlatExpression::Number(ref x) => x.clone(),
FlatExpression::Identifier(ref var) => match inputs.get(var) {
Some(v) => v.clone(),
None => panic!("Variable {:?} is undeclared in witness: {:?}", var, inputs),
},
FlatExpression::Add(ref x, ref y) => x.solve(inputs) + y.solve(inputs),
FlatExpression::Sub(ref x, ref y) => x.solve(inputs) - y.solve(inputs),
FlatExpression::Mult(ref x, ref y) => x.solve(inputs) * y.solve(inputs),
}
}
pub fn is_linear(&self) -> bool {
match *self {
FlatExpression::Number(_) | FlatExpression::Identifier(_) => true,

File diff suppressed because it is too large Load diff

View file

@ -5,33 +5,17 @@
//! @date 2018
use crate::absy::*;
use crate::compile::compile_aux;
use crate::compile::{CompileErrorInner, CompileErrors};
use crate::flat_absy::*;
use crate::compile::compile_module;
use crate::compile::{CompileErrorInner, CompileErrors, Resolve};
use crate::embed::FlatEmbed;
use crate::parser::Position;
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::io::BufRead;
use typed_arena::Arena;
use zokrates_field::field::Field;
pub struct CompiledImport<T: Field> {
pub flat_func: FlatFunction<T>,
}
impl<T: Field> CompiledImport<T> {
fn new(prog: FlatProg<T>, alias: String) -> CompiledImport<T> {
match prog.functions.iter().find(|fun| fun.id == "main") {
Some(fun) => CompiledImport {
flat_func: FlatFunction {
id: alias,
..fun.clone()
},
},
None => panic!("no main"),
}
}
}
#[derive(PartialEq, Debug)]
pub struct Error {
pos: Option<(Position, Position)>,
@ -70,44 +54,44 @@ impl From<io::Error> for Error {
}
}
#[derive(PartialEq, Clone, Serialize, Deserialize)]
pub struct Import {
source: String,
alias: Option<String>,
#[derive(PartialEq, Clone)]
pub struct Import<'ast> {
source: Identifier<'ast>,
alias: Option<Identifier<'ast>>,
}
pub type ImportNode = Node<Import>;
pub type ImportNode<'ast> = Node<Import<'ast>>;
impl Import {
pub fn new(source: String) -> Import {
impl<'ast> Import<'ast> {
pub fn new(source: Identifier<'ast>) -> Import<'ast> {
Import {
source: source,
alias: None,
}
}
pub fn get_alias(&self) -> &Option<String> {
pub fn get_alias(&self) -> &Option<Identifier<'ast>> {
&self.alias
}
pub fn new_with_alias(source: String, alias: &String) -> Import {
pub fn new_with_alias(source: Identifier<'ast>, alias: Identifier<'ast>) -> Import<'ast> {
Import {
source: source,
alias: Some(alias.clone()),
alias: Some(alias),
}
}
pub fn alias(mut self, alias: Option<String>) -> Self {
pub fn alias(mut self, alias: Option<Identifier<'ast>>) -> Self {
self.alias = alias;
self
}
pub fn get_source(&self) -> &String {
pub fn get_source(&self) -> &Identifier<'ast> {
&self.source
}
}
impl fmt::Display for Import {
impl<'ast> fmt::Display for Import<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.alias {
Some(ref alias) => write!(f, "import {} as {}", self.source, alias),
@ -116,7 +100,7 @@ impl fmt::Display for Import {
}
}
impl fmt::Debug for Import {
impl<'ast> fmt::Debug for Import<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.alias {
Some(ref alias) => write!(f, "import(source: {}, alias: {})", self.source, alias),
@ -132,64 +116,48 @@ impl Importer {
Importer {}
}
// Inject dependencies declared for `destination`
// The lifetime of the Program before injection outlives the lifetime after
pub fn apply_imports<'before, 'after, T: Field, S: BufRead, E: Into<Error>>(
pub fn apply_imports<'ast, T: Field, S: BufRead, E: Into<Error>>(
&self,
destination: Prog<'before, T>,
destination: Module<'ast, T>,
location: Option<String>,
resolve_option: Option<fn(&Option<String>, &String) -> Result<(S, String, String), E>>,
) -> Result<Prog<'after, T>, CompileErrors>
where
'before: 'after,
{
let mut origins: Vec<CompiledImport<T>> = vec![];
resolve_option: Option<Resolve<S, E>>,
modules: &mut HashMap<ModuleId, Module<'ast, T>>,
arena: &'ast Arena<String>,
) -> Result<Module<'ast, T>, CompileErrors> {
let mut functions: Vec<_> = vec![];
for import in destination.imports.iter() {
for import in destination.imports {
let pos = import.pos();
let import = &import.value;
let import = import.value;
let alias = import.alias;
// handle the case of special bellman and packing imports
if import.source.starts_with("BELLMAN") {
if import.source.starts_with("EMBED") {
match import.source.as_ref() {
"BELLMAN/sha256round" => {
use crate::standard::sha_round;
"EMBED/sha256round" => {
let alias = alias.unwrap_or("sha256round");
let compiled = FlatProg {
functions: vec![sha_round()],
};
functions.push(
FunctionDeclaration {
id: &alias,
symbol: FunctionSymbol::Flat(FlatEmbed::Sha256Round),
}
.start_end(pos.0, pos.1),
);
}
"EMBED/unpack" => {
let alias = alias.unwrap_or("unpack");
let alias = match import.alias {
Some(ref alias) => alias.clone(),
None => String::from("sha256round"),
};
origins.push(CompiledImport::new(compiled, alias));
functions.push(
FunctionDeclaration {
id: &alias,
symbol: FunctionSymbol::Flat(FlatEmbed::Unpack),
}
.start_end(pos.0, pos.1),
);
}
s => {
return Err(CompileErrorInner::ImportError(
Error::new(format!("Gadget {} not found", s)).with_pos(Some(pos)),
)
.with_context(&location)
.into());
}
}
} else if import.source.starts_with("PACKING") {
use crate::types::conversions::split;
match import.source.as_ref() {
"PACKING/split" => {
let compiled = split();
let alias = match import.alias {
Some(ref alias) => alias.clone(),
None => String::from("split"),
};
origins.push(CompiledImport::new(compiled, alias));
}
s => {
return Err(CompileErrorInner::ImportError(
Error::new(format!("Packing helper {} not found", s))
.with_pos(Some(pos)),
Error::new(format!("Embed {} not found. Options are \"EMBED/sha256round\", \"EMBED/unpack\"", s)).with_pos(Some(pos)),
)
.with_context(&location)
.into());
@ -198,15 +166,38 @@ impl Importer {
} else {
// to resolve imports, we need a resolver
match resolve_option {
Some(resolve) => match resolve(&location, &import.source) {
Ok((mut reader, location, auto_alias)) => {
let compiled = compile_aux(&mut reader, Some(location), resolve_option)
.map_err(|e| e.with_context(Some(import.source.clone())))?;
let alias = match import.alias {
Some(ref alias) => alias.clone(),
None => auto_alias,
};
origins.push(CompiledImport::new(compiled, alias));
Some(resolve) => match resolve(location.clone(), &import.source) {
Ok((mut reader, location, alias)) => {
let mut source = String::new();
reader.read_to_string(&mut source).unwrap();
let source = arena.alloc(source);
let compiled = compile_module(
source,
Some(location),
resolve_option,
modules,
&arena,
)
.map_err(|e| e.with_context(Some(import.source.to_string())))?;
let alias = import.alias.clone().unwrap_or(alias);
modules.insert(import.source.to_string(), compiled);
functions.push(
FunctionDeclaration {
id: &alias,
symbol: FunctionSymbol::There(
FunctionImport::with_id_in_module(
"main",
import.source.clone(),
)
.start_end(pos.0, pos.1),
),
}
.start_end(pos.0, pos.1),
);
}
Err(err) => {
return Err(CompileErrorInner::ImportError(
@ -227,10 +218,11 @@ impl Importer {
}
}
Ok(Prog {
functions.extend(destination.functions);
Ok(Module {
imports: vec![],
functions: destination.clone().functions,
imported_functions: origins.into_iter().map(|o| o.flat_func).collect(),
functions: functions,
})
}
}
@ -243,9 +235,9 @@ mod tests {
#[test]
fn create_with_no_alias() {
assert_eq!(
Import::new("./foo/bar/baz.code".to_string()),
Import::new("./foo/bar/baz.code"),
Import {
source: String::from("./foo/bar/baz.code"),
source: "./foo/bar/baz.code",
alias: None,
}
);
@ -254,10 +246,10 @@ mod tests {
#[test]
fn create_with_alias() {
assert_eq!(
Import::new_with_alias("./foo/bar/baz.code".to_string(), &"myalias".to_string()),
Import::new_with_alias("./foo/bar/baz.code", &"myalias"),
Import {
source: String::from("./foo/bar/baz.code"),
alias: Some("myalias".to_string()),
source: "./foo/bar/baz.code",
alias: Some("myalias"),
}
);
}

View file

@ -5,8 +5,8 @@ use crate::ir::*;
use zokrates_field::field::Field;
pub trait Folder<T: Field>: Sized {
fn fold_program(&mut self, p: Prog<T>) -> Prog<T> {
fold_program(self, p)
fn fold_module(&mut self, p: Prog<T>) -> Prog<T> {
fold_module(self, p)
}
fn fold_function(&mut self, f: Function<T>) -> Function<T> {
@ -38,7 +38,7 @@ pub trait Folder<T: Field>: Sized {
}
}
pub fn fold_program<T: Field, F: Folder<T>>(f: &mut F, p: Prog<T>) -> Prog<T> {
pub fn fold_module<T: Field, F: Folder<T>>(f: &mut F, p: Prog<T>) -> Prog<T> {
Prog {
main: f.fold_function(p.main),
private: p.private,

View file

@ -16,7 +16,7 @@ impl<T: Field> From<FlatFunction<T>> for Function<T> {
.next()
.unwrap();
Function {
id: flat_function.id,
id: String::from("main"),
arguments: flat_function.arguments.into_iter().map(|p| p.id).collect(),
returns: return_expressions
.iter()
@ -63,12 +63,8 @@ impl<T: Field> QuadComb<T> {
impl<T: Field> From<FlatProg<T>> for Prog<T> {
fn from(flat_prog: FlatProg<T>) -> Prog<T> {
// get the main function as all calls have been resolved
let main = flat_prog
.functions
.into_iter()
.find(|f| f.id == "main")
.unwrap();
// get the main function
let main = flat_prog.main;
// get the interface of the program, ie which inputs are private and public
let private = main.arguments.iter().map(|p| p.private).collect();

View file

@ -5,6 +5,7 @@ extern crate num_bigint;
extern crate reduce; // better reduce function than Iter.fold
extern crate serde; // serialization deserialization
extern crate serde_json;
extern crate typed_arena;
#[macro_use]
extern crate serde_derive;
extern crate bellman_ce as bellman;
@ -25,13 +26,13 @@ extern crate zokrates_embed;
extern crate zokrates_field;
extern crate zokrates_pest_ast;
mod embed;
mod flatten;
mod helpers;
mod imports;
mod optimizer;
mod parser;
mod semantics;
mod standard;
mod static_analysis;
mod typed_absy;
mod types;

View file

@ -30,7 +30,7 @@ impl<T: Field> RedefinitionOptimizer<T> {
}
pub fn optimize(p: Prog<T>) -> Prog<T> {
RedefinitionOptimizer::new().fold_program(p)
RedefinitionOptimizer::new().fold_module(p)
}
}

View file

@ -18,7 +18,7 @@ impl TautologyOptimizer {
}
pub fn optimize<T: Field>(p: Prog<T>) -> Prog<T> {
TautologyOptimizer::new().fold_program(p)
TautologyOptimizer::new().fold_module(p)
}
}

View file

@ -14,6 +14,11 @@ impl Position {
col: (self.col as isize + delta) as usize,
}
}
#[cfg(test)]
pub fn mock() -> Self {
Position { line: 42, col: 42 }
}
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View file

@ -1,5 +1,3 @@
#![allow(deprecated)] // TODO remove when lazy_static is warning-free
extern crate rand;
use crate::ir::{CanonicalLinComb, Prog, Statement, Witness};

File diff suppressed because it is too large Load diff

View file

@ -1,232 +0,0 @@
use crate::flat_absy::{FlatExpression, FlatExpressionList, FlatFunction, FlatStatement};
use crate::flat_absy::{FlatParameter, FlatVariable};
use crate::helpers::{DirectiveStatement, Helper, RustHelper};
use crate::types::{Signature, Type};
use bellman::pairing::ff::ScalarEngine;
use reduce::Reduce;
use zokrates_embed::{generate_sha256_round_constraints, BellmanConstraint};
use zokrates_field::field::Field;
// util to convert a vector of `(variable_id, coefficient)` to a flat_expression
fn flat_expression_from_vec<T: Field>(
v: Vec<(usize, <<T as Field>::BellmanEngine as ScalarEngine>::Fr)>,
) -> FlatExpression<T> {
match v
.into_iter()
.map(|(key, val)| {
FlatExpression::Mult(
box FlatExpression::Number(T::from_bellman(val)),
box FlatExpression::Identifier(FlatVariable::new(key)),
)
})
.reduce(|acc, e| FlatExpression::Add(box acc, box e))
{
Some(e @ FlatExpression::Mult(..)) => {
FlatExpression::Add(box FlatExpression::Number(T::zero()), box e)
} // the R1CS serializer only recognizes Add
Some(e) => e,
None => FlatExpression::Number(T::zero()),
}
}
impl<T: Field> From<BellmanConstraint<T::BellmanEngine>> for FlatStatement<T> {
fn from(c: zokrates_embed::BellmanConstraint<T::BellmanEngine>) -> FlatStatement<T> {
let rhs_a = flat_expression_from_vec(c.a);
let rhs_b = flat_expression_from_vec(c.b);
let lhs = flat_expression_from_vec(c.c);
FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b))
}
}
/// Returns a flat function which computes a sha256 round
///
/// # Remarks
///
/// The variables inside the function are set in this order:
/// - constraint system variables
/// - arguments
pub fn sha_round<T: Field>() -> FlatFunction<T> {
// Define iterators for all indices at hand
let (r1cs, input_indices, current_hash_indices, output_indices) =
generate_sha256_round_constraints::<T::BellmanEngine>();
// indices of the input
let input_indices = input_indices.into_iter();
// indices of the current hash
let current_hash_indices = current_hash_indices.into_iter();
// indices of the output
let output_indices = output_indices.into_iter();
let variable_count = r1cs.aux_count + 1; // auxiliary and ONE
// indices of the sha256round constraint system variables
let cs_indices = (0..variable_count).into_iter();
// indices of the arguments to the function
// apply an offset of `variable_count` to get the indice of our dummy `input` argument
let input_argument_indices = input_indices
.clone()
.into_iter()
.map(|i| i + variable_count);
// apply an offset of `variable_count` to get the indice of our dummy `current_hash` argument
let current_hash_argument_indices = current_hash_indices
.clone()
.into_iter()
.map(|i| i + variable_count);
// define the signature of the resulting function
let signature = Signature {
inputs: vec![
Type::FieldElementArray(input_indices.len()),
Type::FieldElementArray(current_hash_indices.len()),
],
outputs: vec![Type::FieldElementArray(output_indices.len())],
};
// define parameters to the function based on the variables
let arguments = input_argument_indices
.clone()
.chain(current_hash_argument_indices.clone())
.map(|i| FlatParameter {
id: FlatVariable::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(),
FlatExpression::Number(T::from(1)),
);
let input_binding_statements =
// bind input and current_hash to inputs
input_indices.clone().chain(current_hash_indices).zip(input_argument_indices.clone().chain(current_hash_argument_indices.clone())).map(|(cs_index, argument_index)| {
FlatStatement::Condition(
FlatVariable::new(cs_index).into(),
FlatVariable::new(argument_index).into(),
)
});
// insert flattened statements to represent constraints
let constraint_statements = r1cs.constraints.into_iter().map(|c| c.into());
// define which subset of the witness is returned
let outputs: Vec<FlatExpression<T>> = output_indices
.map(|o| FlatExpression::Identifier(FlatVariable::new(o)))
.collect();
// insert a directive to set the witness based on the bellman gadget and inputs
let directive_statement = FlatStatement::Directive(DirectiveStatement {
outputs: cs_indices.map(|i| FlatVariable::new(i)).collect(),
inputs: input_argument_indices
.chain(current_hash_argument_indices)
.map(|i| FlatVariable::new(i).into())
.collect(),
helper: Helper::Rust(RustHelper::Sha256Round),
});
// insert a statement to return the subset of the witness
let return_statement = FlatStatement::Return(FlatExpressionList {
expressions: outputs,
});
let statements = std::iter::once(directive_statement)
.chain(std::iter::once(one_binding_statement))
.chain(input_binding_statements)
.chain(constraint_statements)
.chain(std::iter::once(return_statement))
.collect();
FlatFunction {
id: "main".to_owned(),
arguments,
statements,
signature,
}
}
#[cfg(test)]
mod tests {
use super::*;
use zokrates_field::field::FieldPrime;
#[test]
fn generate_sha256_constraints() {
let compiled = sha_round();
// function should have a signature of 768 inputs and 256 outputs
assert_eq!(
compiled.signature,
Signature::new()
.inputs(vec![
Type::FieldElementArray(512),
Type::FieldElementArray(256)
])
.outputs(vec![Type::FieldElementArray(256)])
);
// function should have 768 inputs
assert_eq!(compiled.arguments.len(), 768,);
// function should return 256 values
assert_eq!(
compiled
.statements
.iter()
.filter_map(|s| match s {
FlatStatement::Return(v) => Some(v),
_ => None,
})
.next()
.unwrap()
.expressions
.len(),
256,
);
// directive should take 768 inputs and return n_var outputs
let directive = compiled
.statements
.iter()
.filter_map(|s| match s {
FlatStatement::Directive(d) => Some(d.clone()),
_ => None,
})
.next()
.unwrap();
assert_eq!(directive.inputs.len(), 768);
assert_eq!(directive.outputs.len(), 26935);
// function input should be offset by variable_count
assert_eq!(
compiled.arguments[0].id,
FlatVariable::new(directive.outputs.len() + 1)
);
// bellman variable #0: index 0 should equal 1
assert_eq!(
compiled.statements[1],
FlatStatement::Condition(
FlatVariable::new(0).into(),
FlatExpression::Number(FieldPrime::from(1))
)
);
// bellman input #0: index 1 should equal zokrates input #0: index v_count
assert_eq!(
compiled.statements[2],
FlatStatement::Condition(FlatVariable::new(1).into(), FlatVariable::new(26936).into())
);
let f = crate::ir::Function::from(compiled);
let prog = crate::ir::Prog {
main: f,
private: vec![true; 768],
};
let input = (0..512).map(|_| 0).chain((0..256).map(|_| 1)).collect();
prog.execute(&input).unwrap();
}
}

View file

@ -1,101 +0,0 @@
use crate::typed_absy::folder::*;
use crate::typed_absy::Folder;
use crate::typed_absy::*;
use crate::types::{Signature, Type};
use std::collections::HashSet;
use zokrates_field::field::Field;
pub struct DeadCode {
called: HashSet<String>,
}
impl DeadCode {
fn new() -> Self {
DeadCode {
called: HashSet::new(),
}
}
pub fn clean<T: Field>(p: TypedProg<T>) -> TypedProg<T> {
DeadCode::new().fold_program(p)
}
}
impl<'ast, T: Field> Folder<'ast, T> for DeadCode {
fn fold_program(&mut self, p: TypedProg<'ast, T>) -> TypedProg<'ast, T> {
let p = fold_program(self, p);
// only keep functions which are being called, or `main`
TypedProg {
functions: p
.functions
.into_iter()
.filter(|f| f.id == "main" || self.called.contains(&f.to_slug()))
.collect(),
..p
}
}
// add extra statements before the modified statement
fn fold_statement(&mut self, s: TypedStatement<'ast, T>) -> Vec<TypedStatement<'ast, T>> {
match s {
TypedStatement::MultipleDefinition(variables, elist) => match elist {
TypedExpressionList::FunctionCall(id, exps, types) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(types.clone());
self.called
.insert(format!("{}_{}", id, signature.to_slug()));
vec![TypedStatement::MultipleDefinition(
variables,
TypedExpressionList::FunctionCall(id, exps, types),
)]
}
},
s => fold_statement(self, s),
}
}
fn fold_field_expression(
&mut self,
e: FieldElementExpression<'ast, T>,
) -> FieldElementExpression<'ast, T> {
match e {
FieldElementExpression::FunctionCall(id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElement]);
self.called
.insert(format!("{}_{}", id, signature.to_slug()));
FieldElementExpression::FunctionCall(id, exps)
}
e => fold_field_expression(self, e),
}
}
fn fold_field_array_expression(
&mut self,
e: FieldElementArrayExpression<'ast, T>,
) -> FieldElementArrayExpression<'ast, T> {
match e {
FieldElementArrayExpression::FunctionCall(size, id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElementArray(size)]);
self.called
.insert(format!("{}_{}", id, signature.to_slug()));
FieldElementArrayExpression::FunctionCall(size, id, exps)
}
e => fold_field_array_expression(self, e),
}
}
}

View file

@ -103,14 +103,9 @@ impl<T: Field> Propagate<T> for FlatFunction<T> {
impl<T: Field> FlatProg<T> {
pub fn propagate(self) -> FlatProg<T> {
let mut functions = vec![];
let main = self.main.propagate();
for f in self.functions {
let fun = f.propagate();
functions.push(fun);
}
FlatProg { functions, ..self }
FlatProg { main }
}
}

File diff suppressed because it is too large Load diff

View file

@ -4,36 +4,30 @@
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
//! @date 2018
mod dead_code;
mod flat_propagation;
mod inline;
mod propagation;
mod unroll;
use self::dead_code::DeadCode;
use self::inline::Inliner;
use self::propagation::Propagator;
use self::unroll::Unroller;
use crate::flat_absy::FlatProg;
use crate::typed_absy::TypedProg;
use crate::typed_absy::TypedProgram;
use zokrates_field::field::Field;
pub trait Analyse {
fn analyse(self) -> Self;
}
impl<'ast, T: Field> Analyse for TypedProg<'ast, T> {
impl<'ast, T: Field> Analyse for TypedProgram<'ast, T> {
fn analyse(self) -> Self {
// unroll
let r = Unroller::unroll(self);
//propagate a first time for constants to reach function calls
let r = Propagator::propagate(r);
// apply inlining strategy
// inline
let r = Inliner::inline(r);
// Propagate again
// propagate
let r = Propagator::propagate(r);
// remove unused functions
let r = DeadCode::clean(r);
r
}
}

View file

@ -20,7 +20,7 @@ impl<'ast, T: Field> Propagator<'ast, T> {
}
}
pub fn propagate(p: TypedProg<'ast, T>) -> TypedProg<'ast, T> {
pub fn propagate(p: TypedProgram<'ast, T>) -> TypedProgram<'ast, T> {
Propagator::new().fold_program(p)
}
}

View file

@ -40,7 +40,7 @@ impl<'ast> Unroller<'ast> {
res
}
pub fn unroll<T: Field>(p: TypedProg<T>) -> TypedProg<T> {
pub fn unroll<T: Field>(p: TypedProgram<T>) -> TypedProgram<T> {
Unroller::new().fold_program(p)
}
}
@ -182,6 +182,7 @@ mod tests {
#[cfg(test)]
mod statement {
use super::*;
use crate::types::{FunctionKey, Signature};
#[test]
fn for_loop() {
@ -395,7 +396,11 @@ mod tests {
let s: TypedStatement<FieldPrime> = TypedStatement::MultipleDefinition(
vec![Variable::field_element("a".into())],
TypedExpressionList::FunctionCall(
String::from("foo"),
FunctionKey::with_id("foo").signature(
Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
),
vec![FieldElementExpression::Identifier("a".into()).into()],
vec![Type::FieldElement],
),
@ -405,7 +410,11 @@ mod tests {
vec![TypedStatement::MultipleDefinition(
vec![Variable::field_element(Identifier::from("a").version(1))],
TypedExpressionList::FunctionCall(
String::from("foo"),
FunctionKey::with_id("foo").signature(
Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement])
),
vec![
FieldElementExpression::Identifier(Identifier::from("a").version(0))
.into()

View file

@ -4,10 +4,21 @@ use crate::typed_absy::*;
use zokrates_field::field::Field;
pub trait Folder<'ast, T: Field>: Sized {
fn fold_program(&mut self, p: TypedProg<'ast, T>) -> TypedProg<'ast, T> {
fn fold_program(&mut self, p: TypedProgram<'ast, T>) -> TypedProgram<'ast, T> {
fold_program(self, p)
}
fn fold_module(&mut self, p: TypedModule<'ast, T>) -> TypedModule<'ast, T> {
fold_module(self, p)
}
fn fold_function_symbol(
&mut self,
s: TypedFunctionSymbol<'ast, T>,
) -> TypedFunctionSymbol<'ast, T> {
fold_function_symbol(self, s)
}
fn fold_function(&mut self, f: TypedFunction<'ast, T>) -> TypedFunction<'ast, T> {
fold_function(self, f)
}
@ -90,15 +101,15 @@ pub trait Folder<'ast, T: Field>: Sized {
}
}
pub fn fold_program<'ast, T: Field, F: Folder<'ast, T>>(
pub fn fold_module<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
p: TypedProg<'ast, T>,
) -> TypedProg<'ast, T> {
TypedProg {
p: TypedModule<'ast, T>,
) -> TypedModule<'ast, T> {
TypedModule {
functions: p
.functions
.into_iter()
.map(|fun| f.fold_function(fun))
.map(|(key, fun)| (key, f.fold_function_symbol(fun)))
.collect(),
..p
}
@ -208,9 +219,9 @@ pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>(
let alt = f.fold_field_expression(alt);
FieldElementExpression::IfElse(box cond, box cons, box alt)
}
FieldElementExpression::FunctionCall(id, exps) => {
FieldElementExpression::FunctionCall(key, exps) => {
let exps = exps.into_iter().map(|e| f.fold_expression(e)).collect();
FieldElementExpression::FunctionCall(id, exps)
FieldElementExpression::FunctionCall(key, exps)
}
FieldElementExpression::Select(box array, box index) => {
let array = f.fold_field_array_expression(array);
@ -287,3 +298,27 @@ pub fn fold_function<'ast, T: Field, F: Folder<'ast, T>>(
..fun
}
}
pub fn fold_function_symbol<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
s: TypedFunctionSymbol<'ast, T>,
) -> TypedFunctionSymbol<'ast, T> {
match s {
TypedFunctionSymbol::Here(fun) => TypedFunctionSymbol::Here(f.fold_function(fun)),
there => there, // by default, do not fold modules recursively
}
}
pub fn fold_program<'ast, T: Field, F: Folder<'ast, T>>(
f: &mut F,
p: TypedProgram<'ast, T>,
) -> TypedProgram<'ast, T> {
TypedProgram {
modules: p
.modules
.into_iter()
.map(|(module_id, module)| (module_id, f.fold_module(module)))
.collect(),
main: p.main,
}
}

View file

@ -11,28 +11,86 @@ mod variable;
pub use crate::typed_absy::parameter::Parameter;
pub use crate::typed_absy::variable::Variable;
use crate::types::Signature;
use crate::flat_absy::*;
use crate::imports::Import;
use crate::types::Type;
use crate::types::{FunctionKey, Signature, Type};
use embed::FlatEmbed;
use std::collections::HashMap;
use std::fmt;
use zokrates_field::field::Field;
pub use self::folder::Folder;
/// A identifier for a variable
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub struct Identifier<'ast> {
/// the id of the variable
pub id: &'ast str,
/// the version of the variable, used after SSA transformation
pub version: usize,
pub stack: Vec<(&'ast str, Signature, usize)>,
/// the call stack of the variable, used when inlining
pub stack: Vec<(TypedModuleId, FunctionKey<'ast>, usize)>,
}
pub type FunctionIdentifier<'ast> = &'ast str;
/// An identifier for a `TypedModule`. Typically a path or uri.
pub type TypedModuleId = String;
/// A collection of `TypedModule`s
pub type TypedModules<'ast, T> = HashMap<TypedModuleId, TypedModule<'ast, T>>;
/// A collection of `TypedFunctionSymbol`s
/// # Remarks
/// * It is the role of the semantic checker to make sure there are no duplicates for a given `FunctionKey`
/// in a given `TypedModule`, hence the use of a HashMap
pub type TypedFunctionSymbols<'ast, T> = HashMap<FunctionKey<'ast>, TypedFunctionSymbol<'ast, T>>;
/// A typed program as a collection of modules, one of them being the main
#[derive(PartialEq, Debug)]
pub struct TypedProgram<'ast, T: Field> {
pub modules: TypedModules<'ast, T>,
pub main: TypedModuleId,
}
impl<'ast, T: Field> fmt::Display for TypedProgram<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (module_id, module) in &self.modules {
writeln!(
f,
"| {}: |{}",
module_id,
if *module_id == self.main {
"<---- main"
} else {
""
}
)?;
writeln!(f, "{}", "-".repeat(100))?;
writeln!(f, "{}", module)?;
writeln!(f, "{}", "-".repeat(100))?;
writeln!(f, "")?;
}
write!(f, "")
}
}
/// A
#[derive(PartialEq, Clone)]
pub struct TypedModule<'ast, T: Field> {
/// Functions of the program
pub functions: TypedFunctionSymbols<'ast, T>,
}
impl<'ast> fmt::Display for Identifier<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.id,)
write!(
f,
"{}_{}_{}",
self.stack
.iter()
.map(|(name, sig, count)| format!("{}_{}_{}", name, sig.to_slug(), count))
.collect::<Vec<_>>()
.join("_"),
self.id,
self.version
)
}
}
@ -52,56 +110,62 @@ impl<'ast> Identifier<'ast> {
self.version = version;
self
}
pub fn stack(mut self, stack: Vec<(TypedModuleId, FunctionKey<'ast>, usize)>) -> Self {
self.stack = stack;
self
}
}
#[derive(Clone, PartialEq)]
pub struct TypedProg<'ast, T: Field> {
/// Functions of the program
pub functions: Vec<TypedFunction<'ast, T>>,
pub imports: Vec<Import>,
pub imported_functions: Vec<FlatFunction<T>>,
#[derive(Debug, Clone, PartialEq)]
pub enum TypedFunctionSymbol<'ast, T: Field> {
Here(TypedFunction<'ast, T>),
There(FunctionKey<'ast>, TypedModuleId),
Flat(FlatEmbed),
}
impl<'ast, T: Field> fmt::Display for TypedProg<'ast, T> {
impl<'ast, T: Field> TypedFunctionSymbol<'ast, T> {
pub fn signature<'a>(&'a self, modules: &'a TypedModules<T>) -> Signature {
match self {
TypedFunctionSymbol::Here(f) => f.signature.clone(),
TypedFunctionSymbol::There(key, module_id) => modules
.get(module_id)
.unwrap()
.functions
.get(key)
.unwrap()
.signature(&modules)
.clone(),
TypedFunctionSymbol::Flat(flat_fun) => flat_fun.signature::<T>(),
}
}
}
impl<'ast, T: Field> fmt::Display for TypedModule<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut res = vec![];
res.extend(
self.imports
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>(),
);
res.extend(
self.imported_functions
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>(),
);
res.extend(
self.functions
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>(),
);
let res = self
.functions
.iter()
.map(|(key, symbol)| match symbol {
TypedFunctionSymbol::Here(ref function) => format!("def {}{}", key.id, function),
TypedFunctionSymbol::There(ref fun_key, ref module_id) => format!(
"import {} from \"{}\" as {} // with signature {}",
fun_key.id, module_id, key.id, key.signature
),
TypedFunctionSymbol::Flat(ref flat_fun) => {
format!("def {}{}:\n\t// hidden", key.id, flat_fun.signature::<T>())
}
})
.collect::<Vec<_>>();
write!(f, "{}", res.join("\n"))
}
}
impl<'ast, T: Field> fmt::Debug for TypedProg<'ast, T> {
impl<'ast, T: Field> fmt::Debug for TypedModule<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"program(\n\timports:\n\t\t{}\n\tfunctions:\n\t\t{}{}\n)",
self.imports
.iter()
.map(|x| format!("{:?}", x))
.collect::<Vec<_>>()
.join("\n\t\t"),
self.imported_functions
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join("\n\t\t"),
"module(\n\tfunctions:\n\t\t{}\n)",
self.functions
.iter()
.map(|x| format!("{:?}", x))
@ -111,10 +175,9 @@ impl<'ast, T: Field> fmt::Debug for TypedProg<'ast, T> {
}
}
/// A typed function
#[derive(Clone, PartialEq)]
pub struct TypedFunction<'ast, T: Field> {
/// Name of the program
pub id: FunctionIdentifier<'ast>,
/// Arguments of the function
pub arguments: Vec<Parameter<'ast>>,
/// Vector of statements that are executed when running the function
@ -127,8 +190,7 @@ impl<'ast, T: Field> fmt::Display for TypedFunction<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"def {}({}) -> ({}):\n{}",
self.id,
"({}) -> ({}):\n{}",
self.arguments
.iter()
.map(|x| format!("{}", x))
@ -153,8 +215,7 @@ impl<'ast, T: Field> fmt::Debug for TypedFunction<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"TypedFunction(id: {:?}, arguments: {:?}, ...):\n{}",
self.id,
"TypedFunction(arguments: {:?}, ...):\n{}",
self.arguments,
self.statements
.iter()
@ -165,6 +226,7 @@ impl<'ast, T: Field> fmt::Debug for TypedFunction<'ast, T> {
}
}
/// Something we can assign to.
#[derive(Clone, PartialEq, Hash, Eq)]
pub enum TypedAssignee<'ast, T: Field> {
Identifier(Variable<'ast>),
@ -204,6 +266,7 @@ impl<'ast, T: Field> fmt::Display for TypedAssignee<'ast, T> {
}
}
/// A statement in a `TypedFunction`
#[derive(Clone, PartialEq)]
pub enum TypedStatement<'ast, T: Field> {
Return(Vec<TypedExpression<'ast, T>>),
@ -288,6 +351,7 @@ pub trait Typed {
fn get_type(&self) -> Type;
}
/// A typed expression
#[derive(Clone, PartialEq, Hash, Eq)]
pub enum TypedExpression<'ast, T: Field> {
Boolean(BooleanExpression<'ast, T>),
@ -360,7 +424,7 @@ pub trait MultiTyped {
#[derive(Clone, PartialEq)]
pub enum TypedExpressionList<'ast, T: Field> {
FunctionCall(String, Vec<TypedExpression<'ast, T>>, Vec<Type>),
FunctionCall(FunctionKey<'ast>, Vec<TypedExpression<'ast, T>>, Vec<Type>),
}
impl<'ast, T: Field> MultiTyped for TypedExpressionList<'ast, T> {
@ -371,6 +435,7 @@ impl<'ast, T: Field> MultiTyped for TypedExpressionList<'ast, T> {
}
}
/// An expression of type `field`
#[derive(Clone, PartialEq, Hash, Eq)]
pub enum FieldElementExpression<'ast, T: Field> {
Number(T),
@ -400,13 +465,14 @@ pub enum FieldElementExpression<'ast, T: Field> {
Box<FieldElementExpression<'ast, T>>,
Box<FieldElementExpression<'ast, T>>,
),
FunctionCall(String, Vec<TypedExpression<'ast, T>>),
FunctionCall(FunctionKey<'ast>, Vec<TypedExpression<'ast, T>>),
Select(
Box<FieldElementArrayExpression<'ast, T>>,
Box<FieldElementExpression<'ast, T>>,
),
}
/// An expression of type `bool`
#[derive(Clone, PartialEq, Hash, Eq)]
pub enum BooleanExpression<'ast, T: Field> {
Identifier(Identifier<'ast>),
@ -442,12 +508,14 @@ pub enum BooleanExpression<'ast, T: Field> {
Not(Box<BooleanExpression<'ast, T>>),
}
// for now we store the array size in the variants
/// An expression of type `field[n]
/// # Remarks
/// * for now we store the array size in the variants
#[derive(Clone, PartialEq, Hash, Eq)]
pub enum FieldElementArrayExpression<'ast, T: Field> {
Identifier(usize, Identifier<'ast>),
Value(usize, Vec<FieldElementExpression<'ast, T>>),
FunctionCall(usize, String, Vec<TypedExpression<'ast, T>>),
FunctionCall(usize, FunctionKey<'ast>, Vec<TypedExpression<'ast, T>>),
IfElse(
Box<BooleanExpression<'ast, T>>,
Box<FieldElementArrayExpression<'ast, T>>,
@ -483,8 +551,8 @@ impl<'ast, T: Field> fmt::Display for FieldElementExpression<'ast, T> {
condition, consequent, alternative
)
}
FieldElementExpression::FunctionCall(ref i, ref p) => {
write!(f, "{}(", i,)?;
FieldElementExpression::FunctionCall(ref k, ref p) => {
write!(f, "{}(", k.id,)?;
for (i, param) in p.iter().enumerate() {
write!(f, "{}", param)?;
if i < p.len() - 1 {
@ -528,8 +596,8 @@ impl<'ast, T: Field> fmt::Display for FieldElementArrayExpression<'ast, T> {
.collect::<Vec<String>>()
.join(", ")
),
FieldElementArrayExpression::FunctionCall(_, ref i, ref p) => {
write!(f, "{}(", i,)?;
FieldElementArrayExpression::FunctionCall(_, ref key, ref p) => {
write!(f, "{}(", key.id,)?;
for (i, param) in p.iter().enumerate() {
write!(f, "{}", param)?;
if i < p.len() - 1 {
@ -610,8 +678,8 @@ impl<'ast, T: Field> fmt::Debug for FieldElementArrayExpression<'ast, T> {
impl<'ast, T: Field> fmt::Display for TypedExpressionList<'ast, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
TypedExpressionList::FunctionCall(ref i, ref p, _) => {
write!(f, "{}(", i,)?;
TypedExpressionList::FunctionCall(ref key, ref p, _) => {
write!(f, "{}(", key.id,)?;
for (i, param) in p.iter().enumerate() {
write!(f, "{}", param)?;
if i < p.len() - 1 {
@ -635,9 +703,3 @@ impl<'ast, T: Field> fmt::Debug for TypedExpressionList<'ast, T> {
}
}
}
impl<'ast, T: Field> TypedFunction<'ast, T> {
pub fn to_slug(&self) -> String {
format!("{}_{}", self.id, self.signature.to_slug())
}
}

View file

@ -1,237 +0,0 @@
use crate::flat_absy::flat_parameter::FlatParameter;
use crate::flat_absy::flat_variable::FlatVariable;
use crate::flat_absy::*;
use crate::helpers::{DirectiveStatement, Helper};
use crate::types::signature::Signature;
use crate::types::Type;
use std::collections::HashMap;
use zokrates_field::field::Field;
fn use_variable(
layout: &mut HashMap<String, FlatVariable>,
name: String,
index: &mut usize,
) -> FlatVariable {
let var = FlatVariable::new(*index);
layout.insert(name, var);
*index = *index + 1;
var
}
pub fn split<T: Field>() -> FlatProg<T> {
let nbits = T::get_required_bits();
let mut counter = 0;
let mut layout = HashMap::new();
let arguments = vec![FlatParameter {
id: FlatVariable::new(0),
private: true,
}];
// o0, ..., o253 = ToBits(i0)
let directive_inputs = vec![FlatExpression::Identifier(use_variable(
&mut layout,
format!("i0"),
&mut counter,
))];
let directive_outputs: Vec<FlatVariable> = (0..T::get_required_bits())
.map(|index| use_variable(&mut layout, format!("o{}", index), &mut counter))
.collect();
let helper = Helper::bits();
let signature = Signature {
inputs: vec![Type::FieldElement],
outputs: vec![Type::FieldElementArray(nbits)],
};
let outputs = directive_outputs
.iter()
.enumerate()
.filter(|(index, _)| *index >= T::get_required_bits() - nbits)
.map(|(_, o)| FlatExpression::Identifier(o.clone()))
.collect();
// o253, o252, ... o{253 - (nbits - 1)} are bits
let mut statements: Vec<FlatStatement<T>> = (0..nbits)
.map(|index| {
let bit = FlatExpression::Identifier(FlatVariable::new(T::get_required_bits() - index));
FlatStatement::Condition(
bit.clone(),
FlatExpression::Mult(box bit.clone(), box bit.clone()),
)
})
.collect();
// sum check: o253 + o252 * 2 + ... + o{253 - (nbits - 1)} * 2**(nbits - 1)
let mut lhs_sum = FlatExpression::Number(T::from(0));
for i in 0..nbits {
lhs_sum = FlatExpression::Add(
box lhs_sum,
box FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(T::get_required_bits() - i)),
box FlatExpression::Number(T::from(2).pow(i)),
),
);
}
statements.push(FlatStatement::Condition(
lhs_sum,
FlatExpression::Mult(
box FlatExpression::Identifier(FlatVariable::new(0)),
box FlatExpression::Number(T::from(1)),
),
));
statements.insert(
0,
FlatStatement::Directive(DirectiveStatement {
inputs: directive_inputs,
outputs: directive_outputs,
helper: helper,
}),
);
statements.push(FlatStatement::Return(FlatExpressionList {
expressions: outputs,
}));
FlatProg {
functions: vec![FlatFunction {
id: String::from("main"),
arguments,
statements,
signature,
}],
}
}
pub fn cast<T: Field>(from: &Type, to: &Type) -> FlatFunction<T> {
let mut counter = 0;
let mut layout = HashMap::new();
let arguments = (0..from.get_primitive_count())
.enumerate()
.map(|(index, _)| FlatParameter {
id: FlatVariable::new(index),
private: true,
})
.collect();
let binding_inputs: Vec<_> = (0..from.get_primitive_count())
.map(|index| use_variable(&mut layout, format!("i{}", index), &mut counter))
.collect();
let binding_outputs: Vec<FlatVariable> = (0..to.get_primitive_count())
.map(|index| use_variable(&mut layout, format!("o{}", index), &mut counter))
.collect();
let outputs = binding_outputs
.iter()
.map(|o| FlatExpression::Identifier(o.clone()))
.collect();
let bindings: Vec<_> = match (from, to) {
(Type::Boolean, Type::FieldElement) => binding_outputs
.into_iter()
.zip(binding_inputs.into_iter())
.map(|(o, i)| FlatStatement::Definition(o, i.into()))
.collect(),
_ => panic!(format!("can't cast {} to {}", from, to)),
};
let signature = Signature {
inputs: vec![from.clone()],
outputs: vec![to.clone()],
};
let statements = bindings
.into_iter()
.chain(std::iter::once(FlatStatement::Return(FlatExpressionList {
expressions: outputs,
})))
.collect();
FlatFunction {
id: format!("_{}_to_{}", from, to),
arguments,
statements,
signature,
}
}
#[cfg(test)]
mod tests {
use super::*;
use zokrates_field::field::FieldPrime;
#[cfg(test)]
mod cast {
use super::*;
#[test]
fn bool_to_field() {
let b2f: FlatFunction<FieldPrime> = cast(&Type::Boolean, &Type::FieldElement);
assert_eq!(b2f.id, String::from("_bool_to_field"));
assert_eq!(
b2f.arguments,
vec![FlatParameter::private(FlatVariable::new(0))]
);
assert_eq!(b2f.statements.len(), 2); // 1 definition, 1 return
assert_eq!(
b2f.statements[0],
FlatStatement::Definition(FlatVariable::new(1), FlatVariable::new(0).into())
);
assert_eq!(
b2f.statements[1],
FlatStatement::Return(FlatExpressionList {
expressions: vec![FlatExpression::Identifier(FlatVariable::new(1))]
})
);
assert_eq!(b2f.signature.outputs.len(), 1);
}
}
#[cfg(test)]
mod split {
use super::*;
#[test]
fn split254() {
let unpack: FlatProg<FieldPrime> = split();
let unpack = &unpack.functions[0];
assert_eq!(unpack.id, String::from("main"));
assert_eq!(
unpack.arguments,
vec![FlatParameter::private(FlatVariable::new(0))]
);
assert_eq!(
unpack.statements.len(),
FieldPrime::get_required_bits() + 1 + 1 + 1
); // 128 bit checks, 1 directive, 1 sum check, 1 return
assert_eq!(
unpack.statements[0],
FlatStatement::Directive(DirectiveStatement::new(
(0..FieldPrime::get_required_bits())
.map(|i| FlatVariable::new(i + 1))
.collect(),
Helper::bits(),
vec![FlatVariable::new(0)]
))
);
assert_eq!(
*unpack.statements.last().unwrap(),
FlatStatement::Return(FlatExpressionList {
expressions: (0..FieldPrime::get_required_bits())
.map(|i| FlatExpression::Identifier(FlatVariable::new(i + 1)))
.collect()
})
);
}
}
}

View file

@ -1,7 +1,8 @@
pub use crate::types::signature::Signature;
use std::fmt;
pub mod conversions;
pub type Identifier<'ast> = &'ast str;
mod signature;
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
@ -32,6 +33,14 @@ impl fmt::Debug for Type {
}
impl Type {
fn to_slug(&self) -> String {
match *self {
Type::FieldElement => String::from("f"),
Type::Boolean => String::from("b"),
Type::FieldElementArray(size) => format!("{}[{}]", Type::FieldElement.to_slug(), size),
}
}
// the number of field elements the type maps to
pub fn get_primitive_count(&self) -> usize {
match self {
@ -40,14 +49,50 @@ impl Type {
Type::FieldElementArray(size) => size * Type::FieldElement.get_primitive_count(),
}
}
}
fn to_slug(&self) -> String {
match *self {
Type::FieldElement => String::from("f"),
Type::Boolean => String::from("b"),
Type::FieldElementArray(size) => format!("{}[{}]", Type::FieldElement.to_slug(), size), // TODO differentiate types?
#[derive(Clone, PartialEq, Hash, Eq)]
pub struct Variable<'ast> {
pub id: Identifier<'ast>,
pub _type: Type,
}
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: {:?})", self._type, self.id,)
}
}
pub type FunctionIdentifier<'ast> = &'ast str;
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct FunctionKey<'ast> {
pub id: FunctionIdentifier<'ast>,
pub signature: Signature,
}
impl<'ast> FunctionKey<'ast> {
pub fn with_id<S: Into<Identifier<'ast>>>(id: S) -> Self {
FunctionKey {
id: id.into(),
signature: Signature::new(),
}
}
pub fn signature(mut self, signature: Signature) -> Self {
self.signature = signature;
self
}
pub fn to_slug(&self) -> String {
format!("{}_{}", self.id, self.signature.to_slug())
}
}
#[cfg(test)]
@ -58,6 +103,5 @@ mod tests {
fn array() {
let t = Type::FieldElementArray(42);
assert_eq!(t.get_primitive_count(), 42);
assert_eq!(t.to_slug(), "f[42]");
}
}

View file

@ -111,50 +111,4 @@ mod tests {
assert_eq!(s.to_string(), String::from("(field, bool) -> (bool)"));
}
#[test]
fn slug_0() {
let s = Signature::new().inputs(vec![]).outputs(vec![]);
assert_eq!(s.to_slug(), String::from("io"));
}
#[test]
fn slug_1() {
let s = Signature::new()
.inputs(vec![Type::FieldElement, Type::Boolean])
.outputs(vec![
Type::FieldElement,
Type::FieldElement,
Type::Boolean,
Type::FieldElement,
]);
assert_eq!(s.to_slug(), String::from("ifbo2fbf"));
}
#[test]
fn slug_2() {
let s = Signature::new()
.inputs(vec![
Type::FieldElement,
Type::FieldElement,
Type::FieldElement,
])
.outputs(vec![Type::FieldElement, Type::Boolean, Type::FieldElement]);
assert_eq!(s.to_slug(), String::from("i3fofbf"));
}
#[test]
fn array_slug() {
let s = Signature::new()
.inputs(vec![
Type::FieldElementArray(42),
Type::FieldElementArray(21),
])
.outputs(vec![]);
assert_eq!(s.to_slug(), String::from("if[42]f[21]o"));
}
}

View file

@ -1,7 +1,7 @@
import "PACKING/split"
import "EMBED/unpack"
def main(field a) -> (field[254]):
field[254] b = split(a)
field[254] b = unpack(a)
return b

View file

@ -7,7 +7,7 @@ edition = "2018"
[dependencies]
serde = "1.0"
serde_derive = "1.0"
lazy_static = "0.1.*"
lazy_static = "1.4"
bincode = "0.8.0"
serde_json = "1.0"
num-traits = "0.2"

View file

@ -1,5 +1,3 @@
#![allow(deprecated)] // TODO remove when lazy_static is warning-free
//
// @file field.rs
// @author Dennis Kuhnert <dennis.kuhnert@campus.tu-berlin.de>

View file

@ -1,14 +1,15 @@
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::path::Path;
use std::path::{Component, PathBuf};
const ZOKRATES_HOME: &str = &"ZOKRATES_HOME";
pub fn resolve(
location: &Option<String>,
source: &String,
) -> Result<(BufReader<File>, String, String), io::Error> {
pub fn resolve<'a>(
location: Option<String>,
source: &'a str,
) -> Result<(BufReader<File>, String, &'a str), io::Error> {
// the fs resolver has to be provided a location, as it supports relative paths
match location {
Some(location) => resolve_with_location(location, source),
@ -16,11 +17,11 @@ pub fn resolve(
}
}
fn resolve_with_location(
location: &String,
source: &String,
) -> Result<(BufReader<File>, String, String), io::Error> {
let source = PathBuf::from(source);
fn resolve_with_location<'a>(
location: String,
source: &'a str,
) -> Result<(BufReader<File>, String, &'a str), io::Error> {
let source = Path::new(source);
// paths starting with `./` or `../` are interpreted relative to the current file
// other paths `abc/def.code` are interpreted relative to $ZOKRATES_HOME
@ -31,25 +32,26 @@ fn resolve_with_location(
),
};
let path = base.join(PathBuf::from(source));
let path_owned = base.join(PathBuf::from(source));
if path.is_dir() {
if path_owned.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Not a file"));
}
let (next_location, alias) = generate_next_parameters(&path)?;
let alias = generate_alias(source);
let next_location = generate_next_location(&path_owned)?;
File::open(path).and_then(|f| Ok((BufReader::new(f), next_location, alias)))
File::open(path_owned).and_then(|f| Ok((BufReader::new(f), next_location, alias)))
}
fn generate_next_parameters(path: &PathBuf) -> Result<(String, String), io::Error> {
match (path.parent(), path.file_stem()) {
(Some(parent), Some(stem)) => Ok((
parent.to_path_buf().into_os_string().into_string().unwrap(),
stem.to_os_string().to_string_lossy().to_string(),
)),
_ => Err(io::Error::new(io::ErrorKind::Other, "Invalid path")),
}
fn generate_next_location<'a>(path: &'a PathBuf) -> Result<String, io::Error> {
path.parent()
.ok_or(io::Error::new(io::ErrorKind::Other, "Invalid path"))
.map(|v| v.to_path_buf().into_os_string().into_string().unwrap())
}
fn generate_alias<'a>(path: &'a Path) -> &'a str {
path.file_stem().unwrap().to_str().unwrap()
}
#[cfg(test)]
@ -58,42 +60,38 @@ mod tests {
#[test]
fn valid_path_with_location() {
let (_, next_location, alias) =
resolve(&Some(String::from("./src")), &String::from("./lib.rs")).unwrap();
let (_, next_location, alias) = resolve(Some(String::from("./src")), &"./lib.rs").unwrap();
assert_eq!(next_location, String::from("./src"));
assert_eq!(alias, String::from("lib"));
}
#[test]
fn valid_path_without_location() {
let res = resolve(&None, &String::from("./src/lib.rs"));
let res = resolve(None, &"./src/lib.rs");
assert!(res.is_err());
}
#[test]
fn non_existing_file() {
let res = resolve(&Some(String::from("./src")), &String::from("./rubbish.rs"));
let res = resolve(Some(String::from("./src")), &"./rubbish.rs");
assert!(res.is_err());
}
#[test]
fn invalid_location() {
let res = resolve(
&Some(String::from(",8!-$2abc")),
&String::from("./foo.code"),
);
let res = resolve(Some(String::from(",8!-$2abc")), &"./foo.code");
assert!(res.is_err());
}
#[test]
fn not_a_file() {
let res = resolve(&Some(String::from(".")), &String::from("./src/"));
let res = resolve(Some(String::from(".")), &"./src/");
assert!(res.is_err());
}
#[test]
fn no_parent() {
let res = resolve(&Some(String::from(".")), &String::from("."));
let res = resolve(Some(String::from(".")), &".");
assert!(res.is_err());
}
@ -102,7 +100,7 @@ mod tests {
fn no_file_name_without_stdlib() {
// an empty string is interpreted relative to the HOME folder. If there's none, panic
std::env::remove_var(ZOKRATES_HOME);
let _res = resolve(&Some(String::from(".")), &String::from(""));
let _res = resolve(Some(String::from(".")), &"");
}
#[test]
@ -118,7 +116,7 @@ mod tests {
// assign HOME folder to ZOKRATES_HOME
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let res = resolve(&Some(String::from(".")), &String::from(""));
let res = resolve(Some(String::from(".")), &"");
assert!(res.is_err());
}
@ -143,14 +141,14 @@ mod tests {
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let result = resolve(
&Some(
Some(
source_folder
.path()
.to_path_buf()
.to_string_lossy()
.to_string(),
),
&"./bar.code".to_string(),
&"./bar.code",
);
assert!(result.is_ok());
let mut code = String::new();
@ -180,14 +178,14 @@ mod tests {
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let result = resolve(
&Some(
Some(
source_folder
.path()
.to_path_buf()
.to_string_lossy()
.to_string(),
),
&"bar.code".to_string(),
&"bar.code",
);
assert!(result.is_ok());
let mut code = String::new();
@ -209,14 +207,14 @@ mod tests {
writeln!(file, "<user code>").unwrap();
let result = resolve(
&Some(
Some(
source_subfolder
.path()
.to_path_buf()
.to_string_lossy()
.to_string(),
),
&"../bar.code".to_string(),
&"../bar.code",
);
assert!(result.is_ok());
let mut code = String::new();
@ -238,20 +236,14 @@ mod tests {
// assign HOME folder to ZOKRATES_HOME
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let result = resolve(
&Some("/path/to/user/folder".to_string()),
&"./bar.code".to_string(),
);
let result = resolve(Some("/path/to/user/folder".to_string()), &"./bar.code");
assert!(result.is_err());
}
#[test]
fn fail_if_not_found_in_std() {
std::env::set_var(ZOKRATES_HOME, "");
let result = resolve(
&Some("/path/to/source".to_string()),
&"bar.code".to_string(),
);
let result = resolve(Some("/path/to/source".to_string()), &"bar.code");
assert!(result.is_err());
}
@ -259,9 +251,6 @@ mod tests {
#[should_panic]
fn panic_if_home_not_set() {
std::env::remove_var(ZOKRATES_HOME);
let _ = resolve(
&Some("/path/to/source".to_string()),
&"bar.code".to_string(),
);
let _ = resolve(Some("/path/to/source".to_string()), &"bar.code");
}
}

View file

@ -20,7 +20,7 @@
use reqwest;
use std::fs::File;
use std::io::{self, copy, BufReader};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use tempfile::NamedTempFile;
#[cfg(test)]
@ -36,30 +36,26 @@ const GITHUB_URL_BASE: &str = "https://raw.githubusercontent.com";
/// Resolves import from the Github.
/// This importer needs to be provided with location since relative paths could be used inside the
/// files that are imported from github.
pub fn resolve(
location: &Option<String>,
path: &String,
) -> Result<(BufReader<File>, String, String), io::Error> {
pub fn resolve<'a>(
location: Option<String>,
path: &'a str,
) -> Result<(BufReader<File>, String, &'a str), io::Error> {
if let Some(location) = location {
let (root, repo, branch, path) = parse_input_path(&path)?;
let path = Path::new(path);
let (root, repo, branch, file_path) = parse_input_path(&path)?;
#[cfg(not(test))]
let url = GITHUB_URL_BASE;
#[cfg(test)]
let url = mockito::server_url();
let pb = download_from_github(&url, &root, &repo, &branch, &path)?;
let pb = download_from_github(&url, &root, &repo, &branch, &file_path)?;
let file = File::open(&pb)?;
let br = BufReader::new(file);
let alias = PathBuf::from(path.clone())
.as_path()
.file_stem()
.unwrap()
.to_string_lossy()
.to_string();
let alias = path.file_stem().unwrap().to_str().unwrap();
Ok((br, location.to_owned(), alias))
Ok((br, location.to_owned(), &alias))
} else {
Err(io::Error::new(io::ErrorKind::Other, "No location provided"))
}
@ -76,19 +72,19 @@ pub fn is_github_import(source: &str) -> bool {
/// - <repo> is the repository name
/// - <branch> is the branch/snapshot name, e.g. `master`
/// - <path/to/file> is the absolute path to file in the specified branch of the repository
fn parse_input_path(path: &str) -> Result<(String, String, String, String), io::Error> {
let path = path.replacen(GITHUB_IMPORT_PREFIX, "", 1);
if path.contains("..") {
fn parse_input_path<'a>(path: &'a Path) -> Result<(&'a str, &'a str, &'a str, &'a str), io::Error> {
//let path_owned = path.replacen(GITHUB_IMPORT_PREFIX, "", 1);
if path.to_str().unwrap().contains("..") {
return Err(io::Error::new(
io::ErrorKind::Other,
"Invalid github import syntax. It must not contain '..'",
));
}
let components = path.split("/").collect::<Vec<_>>();
let mut components = path.components();
// Check that root, repo, branch & path are specified
if components.len() < 4 {
if components.clone().count() < 5 {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
@ -98,17 +94,13 @@ fn parse_input_path(path: &str) -> Result<(String, String, String, String), io::
));
}
let root = components[0];
let repo = components[1];
let branch = components[2];
let path = components[3..].join("/"); // Collect the rest of the import path into single string
let _ = components.next().unwrap().as_os_str().to_str().unwrap();
let root = components.next().unwrap().as_os_str().to_str().unwrap();
let repo = components.next().unwrap().as_os_str().to_str().unwrap();
let branch = components.next().unwrap().as_os_str().to_str().unwrap();
let path = components.as_path().to_str().unwrap(); // Collect the rest of the import path into single string
Ok((
root.to_owned(),
repo.to_owned(),
branch.to_owned(),
path.to_owned(),
))
Ok((root, repo, branch, path))
}
/// Downloads the file from github by specific root (user/org), repository, branch and path.
@ -179,9 +171,9 @@ mod tests {
#[test]
pub fn import_simple() {
let res = parse_input_path(
let res = parse_input_path(Path::new(
"github.com/Zokrates/ZoKrates/master/zokrates_cli/examples/imports/import.code",
)
))
.unwrap();
let (root, repo, branch, path) = res;
@ -196,24 +188,25 @@ mod tests {
pub fn import_no_branch() {
// Correct syntax should be: github.com/Zokrates/ZoKrates/master/zokrates_cli/examples/imports/import.code
// but branch name is not specified
parse_input_path("github.com/Zokrates/ZoKrates/test.code").unwrap();
parse_input_path(Path::new("github.com/Zokrates/ZoKrates/test.code")).unwrap();
}
#[test]
#[should_panic]
pub fn import_relative_paths() {
// Relative paths should not be allowed
parse_input_path("github.com/Zokrates/ZoKrates/master/examples/../imports.code").unwrap();
parse_input_path(Path::new(
"github.com/Zokrates/ZoKrates/master/examples/../imports.code",
))
.unwrap();
}
#[test]
pub fn resolve_ok() {
let (_m0, _m1) = init_github_mock();
let res = resolve(
&Some("".to_string()),
&String::from(
"github.com/Zokrates/ZoKrates/master/zokrates_cli/examples/imports/foo.code",
),
Some("".to_string()),
&"github.com/Zokrates/ZoKrates/master/zokrates_cli/examples/imports/foo.code",
);
assert!(res.is_ok());
}
@ -222,10 +215,8 @@ mod tests {
pub fn resolve_err() {
let (_m0, _m1) = init_github_mock();
assert!(resolve(
&Some("".to_string()),
&String::from(
"github.com/Zokrates/ZoKrates/master/zokrates_cli/examples/imports/notfound.code"
)
Some("".to_string()),
&"github.com/Zokrates/ZoKrates/master/zokrates_cli/examples/imports/notfound.code"
)
.is_err());
}

View file

@ -1,7 +1,6 @@
import "BELLMAN/sha256round" as sha256
import "EMBED/sha256round" as sha256round
// a and b is NOT checked to be 0 or 1
// the return value is checked to be 0 or 1
// IV vector is checked to be of type bool
def main(field[256] a, field[256] b, field[256] IV) -> (field[256]):
return sha256([...a, ...b], IV)
return sha256round([...a, ...b], IV)

View file

@ -2,10 +2,10 @@
// Note that this does not strongly enforce that the commitment is
// in the field.
import "PACKING/split" as split
import "EMBED/unpack" as unpack
def main(field i) -> (field[256]):
field[254] b = split(i)
field[254] b = unpack(i)
return [0, 0, ...b]

View file

@ -1,8 +1,8 @@
import "PACKING/split" as split
import "EMBED/unpack" as unpack
def main(field i) -> (field[128]):
field[254] b = split(i)
field[254] b = unpack(i)
b[0..126] == [0; 126]