Merge branch 'develop' of github.com:Zokrates/ZoKrates into zokrates-test-crate
This commit is contained in:
commit
5d40fa7083
43 changed files with 3604 additions and 3103 deletions
51
Cargo.lock
generated
51
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import "BELLMAN/sha256round" as sha256
|
||||
import "EMBED/sha256round" as sha256
|
||||
|
||||
def main(private field[256] expected) -> (field):
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>>,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::absy::Node;
|
||||
use crate::types::Type;
|
||||
use std::fmt;
|
||||
use types::Type;
|
||||
|
||||
use crate::absy::Identifier;
|
||||
|
||||
|
|
|
@ -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
426
zokrates_core/src/embed.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
Loading…
Reference in a new issue