Merge pull request #6 from AleoHQ/development

Development
This commit is contained in:
Howard Wu 2020-05-08 19:02:05 -07:00 committed by GitHub
commit bd553714e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 5432 additions and 2133 deletions

5
.gitignore vendored
View File

@ -1,2 +1,7 @@
/target
**.idea/
Leo.toml
src/
inputs/
outputs/
*.DS_Store

453
Cargo.lock generated
View File

@ -29,12 +29,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
[[package]]
name = "autocfg"
version = "1.0.0"
@ -63,27 +57,6 @@ dependencies = [
"libc",
]
[[package]]
name = "base58"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
[[package]]
name = "base58-monero"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c25c7705c81e36f14c293e67846819b1fa3ca7c5e9888ebf149c2bd59d06aa"
dependencies = [
"keccak-hash",
]
[[package]]
name = "bech32"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58946044516aa9dc922182e0d6e9d124a31aafe6b421614654eb27cf90cec09c"
[[package]]
name = "bincode"
version = "1.2.1"
@ -131,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b77e29dbd0115e43938be2d5128ecf81c0353e00acaa65339a1242586951d9"
dependencies = [
"byte-tools 0.2.0",
"crypto-mac 0.5.2",
"crypto-mac",
"digest 0.7.6",
]
@ -200,9 +173,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clang-sys"
version = "0.29.2"
version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92986241798376849e1a007827041fed9bb36195822c2049d18e174420e0534"
checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a"
dependencies = [
"glob",
"libc",
@ -224,15 +197,6 @@ dependencies = [
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "colored"
version = "1.9.3"
@ -267,7 +231,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg 1.0.0",
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
@ -292,23 +256,11 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 1.0.0",
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "crunchy"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-mac"
version = "0.5.2"
@ -319,16 +271,6 @@ dependencies = [
"generic-array 0.9.0",
]
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
"generic-array 0.12.3",
"subtle",
]
[[package]]
name = "derivative"
version = "1.0.4"
@ -377,42 +319,6 @@ dependencies = [
"termcolor",
]
[[package]]
name = "ethbloom"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6294da962646baa738414e8e718d1a1f0360a51d92de89ccbf91870418f5360"
dependencies = [
"crunchy 0.1.6",
"ethereum-types-serialize",
"fixed-hash",
"serde",
"tiny-keccak",
]
[[package]]
name = "ethereum-types"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e742184dc63a01c8ea0637369f8faa27c40f537949908a237f95c05e68d2c96"
dependencies = [
"crunchy 0.1.6",
"ethbloom",
"ethereum-types-serialize",
"fixed-hash",
"serde",
"uint",
]
[[package]]
name = "ethereum-types-serialize"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d77b32bc1891a79dad925f2acbc318ee942b38b9110f9dbc5fbeffcea350"
dependencies = [
"serde",
]
[[package]]
name = "failure"
version = "0.1.7"
@ -441,17 +347,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fixed-hash"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7afe6ce860afb14422711595a7b26ada9ed7de2f43c0b2ab79d09ee196287273"
dependencies = [
"heapsize",
"rand 0.4.6",
"rustc-hex",
]
[[package]]
name = "from-pest"
version = "0.3.1"
@ -462,12 +357,6 @@ dependencies = [
"void",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.1.29"
@ -509,15 +398,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "heapsize"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461"
dependencies = [
"winapi",
]
[[package]]
name = "heck"
version = "0.3.1"
@ -536,12 +416,6 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
[[package]]
name = "hex"
version = "0.4.2"
@ -574,9 +448,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]]
name = "jsonrpc-core"
version = "14.0.5"
version = "14.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe3b688648f1ef5d5072229e2d672ecb92cbff7d1c79bcf3fd5898f3f3df0970"
checksum = "25525f6002338fb4debb5167a89a0b47f727a5a48418417545ad3429758b7fec"
dependencies = [
"futures",
"log",
@ -585,16 +459,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "keccak-hash"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "253bbe643c32c816bf58fa5a88248fafedeebb139705ad17a62add3517854a86"
dependencies = [
"ethereum-types",
"tiny-keccak",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -618,8 +482,8 @@ dependencies = [
"from-pest",
"leo-compiler",
"log",
"rand 0.7.3",
"rand_core 0.5.1",
"rand",
"rand_core",
"serde",
"serde_json",
"snarkos-algorithms",
@ -627,6 +491,7 @@ dependencies = [
"snarkos-errors",
"snarkos-gadgets",
"snarkos-models",
"snarkos-utilities",
"structopt",
"toml",
]
@ -637,7 +502,7 @@ version = "0.1.0"
dependencies = [
"from-pest",
"leo-compiler",
"rand 0.7.3",
"rand",
"snarkos-algorithms",
"snarkos-curves",
"snarkos-errors",
@ -650,17 +515,20 @@ name = "leo-compiler"
version = "0.1.0"
dependencies = [
"from-pest",
"hex",
"lazy_static",
"log",
"pest",
"pest-ast",
"pest_derive",
"rand 0.7.3",
"rand",
"sha2",
"snarkos-algorithms",
"snarkos-curves",
"snarkos-errors",
"snarkos-gadgets",
"snarkos-models",
"thiserror",
]
[[package]]
@ -681,9 +549,9 @@ dependencies = [
[[package]]
name = "librocksdb-sys"
version = "6.6.4"
version = "6.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3b727e2dd20ec2fb7ed93f23d9fd5328a0871185485ebdaff007b47d3e27e4"
checksum = "883213ae3d09bfc3d104aefe94b25ebb183b6f4d3a515b23b14817e1f4854005"
dependencies = [
"bindgen",
"cc",
@ -720,11 +588,11 @@ checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "memoffset"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
dependencies = [
"rustc_version",
"autocfg",
]
[[package]]
@ -739,9 +607,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
@ -889,38 +757,6 @@ dependencies = [
"proc-macro2 1.0.9",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
"autocfg 0.1.7",
"libc",
"rand_chacha 0.1.1",
"rand_core 0.4.2",
"rand_hc 0.1.0",
"rand_isaac",
"rand_jitter",
"rand_os",
"rand_pcg",
"rand_xorshift 0.1.1",
"winapi",
]
[[package]]
name = "rand"
version = "0.7.3"
@ -929,19 +765,9 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
"autocfg 0.1.7",
"rand_core 0.3.1",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
@ -951,24 +777,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
@ -978,75 +789,13 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
"autocfg 0.1.7",
"rand_core 0.4.2",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
"rand_core 0.3.1",
"rand_core",
]
[[package]]
@ -1055,7 +804,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
dependencies = [
"rand_core 0.5.1",
"rand_core",
]
[[package]]
@ -1082,15 +831,6 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "regex"
version = "1.3.4"
@ -1109,17 +849,6 @@ version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1"
[[package]]
name = "ripemd160"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
dependencies = [
"block-buffer",
"digest 0.8.1",
"opaque-debug",
]
[[package]]
name = "rocksdb"
version = "0.13.0"
@ -1142,21 +871,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hex"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.2"
@ -1169,31 +883,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "secp256k1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d311229f403d64002e9eed9964dfa5a0a0c1ac443344f7546bf48e916c6053a"
dependencies = [
"cc",
"rand 0.6.5",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.104"
@ -1266,9 +955,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.2.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "snarkos-algorithms"
@ -1277,7 +966,7 @@ dependencies = [
"blake2",
"derivative",
"digest 0.7.6",
"rand 0.7.3",
"rand",
"rayon",
"sha2",
"smallvec",
@ -1292,7 +981,7 @@ name = "snarkos-curves"
version = "0.8.0"
dependencies = [
"derivative",
"rand 0.7.3",
"rand",
"snarkos-errors",
"snarkos-models",
"snarkos-utilities",
@ -1302,14 +991,11 @@ dependencies = [
name = "snarkos-errors"
version = "0.8.0"
dependencies = [
"base58",
"bincode",
"failure",
"hex 0.4.2",
"hex",
"jsonrpc-core",
"rocksdb",
"secp256k1",
"wagyu-model",
]
[[package]]
@ -1331,8 +1017,8 @@ version = "0.8.0"
dependencies = [
"derivative",
"failure",
"rand 0.7.3",
"rand_xorshift 0.2.0",
"rand",
"rand_xorshift",
"smallvec",
"snarkos-errors",
"snarkos-utilities",
@ -1346,7 +1032,7 @@ version = "0.8.0"
name = "snarkos-utilities"
version = "0.8.0"
dependencies = [
"rand 0.7.3",
"rand",
]
[[package]]
@ -1379,12 +1065,6 @@ dependencies = [
"syn 1.0.16",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "syn"
version = "0.15.44"
@ -1448,6 +1128,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269"
dependencies = [
"proc-macro2 1.0.9",
"quote 1.0.3",
"syn 1.0.16",
]
[[package]]
name = "thread_local"
version = "1.0.1"
@ -1457,15 +1157,6 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "tiny-keccak"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2"
dependencies = [
"crunchy 0.2.2",
]
[[package]]
name = "toml"
version = "0.5.6"
@ -1487,18 +1178,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2"
[[package]]
name = "uint"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646"
dependencies = [
"byteorder",
"crunchy 0.1.6",
"heapsize",
"rustc-hex",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
@ -1541,26 +1220,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "wagyu-model"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d64afd740a18fd8404dcebc8b15b2f7b63b0886f2c5552b28c8bf7f32ab462a"
dependencies = [
"base58",
"base58-monero",
"bech32",
"byteorder",
"crypto-mac 0.7.0",
"failure",
"hex 0.3.2",
"rand 0.7.3",
"rand_core 0.5.1",
"ripemd160",
"secp256k1",
"sha2",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"

View File

@ -23,6 +23,7 @@ snarkos-curves = { path = "../snarkOS/curves", version = "0.8.0" }
snarkos-errors = { path = "../snarkOS/errors", version = "0.8.0" }
snarkos-gadgets = { path = "../snarkOS/gadgets", version = "0.8.0" }
snarkos-models = { path = "../snarkOS/models", version = "0.8.0" }
snarkos-utilities = { path = "../snarkOS/utilities", version = "0.8.0" }
clap = { version = "2.33.0" }
colored = { version = "1.9" }

256
README.md
View File

@ -1,20 +1,20 @@
# The Leo Language
* All code examples can be copied and pasted into simple.program directly and executed with cargo run
* All code examples can be copied and pasted into `main.leo` directly and executed with cargo run
* Programs should be formatted:
1. Import definitions
2. Stuct definitions
2. Struct definitions
3. Function definitions
### Integers:
Currently, all integers are parsed as u32.
You can choose to explicitly add the type or let the compiler interpret implicitly.
```rust
function main() -> (u32) {
a = 1u32 + 1u32
b = 1 - 1
c = 2 * 2
d = 4 / 2
e = 2 ** 3
function main() -> u32 {
let a: u32 = 1u32 + 1u32; // explicit type
let b = 1 - 1; // implicit type
let c = 2 * 2;
let d = 4 / 2;
let e = 2 ** 3;
return a
}
```
@ -22,23 +22,37 @@ function main() -> (u32) {
### Field Elements:
Field elements must have the type added explicitly.
```rust
function main() -> (fe) {
f = 21888242871839275222246405745257275088548364400416034343698204186575808495617fe
a = 1fe + 1fe
b = 1fe - 1fe
c = 2fe * 2fe
d = 4fe / 2fe
function main() -> fe {
let f: fe = 21888242871839275222246405745257275088548364400416034343698204186575808495617fe;
let a = 1fe + 1fe;
let b = 1fe - 1fe;
let c = 2fe * 2fe;
let d = 4fe / 2fe;
return a
}
```
### Operator Assignment Statements:
```rust
function main() -> u32 {
let a = 10;
a += 5;
a -= 10;
a *= 5;
a /= 5;
a **= 2;
return a
}
```
### Booleans:
```rust
function main() -> (bool) {
a = true || false
b = false && false
c = 1 == 1
function main() -> bool {
let a: bool = true || false;
let b = false && false;
let c = 1 == 1;
return a
}
```
@ -47,27 +61,27 @@ function main() -> (bool) {
Leo supports static arrays with fixed length.
Array type must be explicitly stated
```rust
function main() -> (u32[2]) {
function main() -> u32[2] {
// initialize an integer array with integer values
u32[3] a = [1, 2, 3]
let a: u32[3] = [1, 2, 3];
// set a member to a value
a[2] = 4
a[2] = 4;
// initialize an array of 4 values all equal to 42
u32[4] b = [42; 4]
let b = [42; 4];
// initialize an array of 5 values copying all elements of b using a spread
u32[5] c = [1, ...b]
let c = [1, ...b];
// initialize an array copying a slice from `c`
d = c[1..3]
let d = c[1..3];
// initialize a field array
fe[2] e = [5fe; 2]
let e = [5fe; 2];
// initialize a boolean array
bool[3] f = [true, false || true, true]
let f = [true, false || true, true];
// return an array
return d
@ -76,57 +90,87 @@ function main() -> (u32[2]) {
### Structs:
```rust
struct Point {
u32 x
u32 y
x: u32
y: u32
}
function main() -> (u32) {
function main() -> u32 {
Point p = Point {x: 1, y: 0}
return p.x
}
```
```rust
struct Foo {
bool x
x: bool
}
function main() -> (Foo) {
Foo f = Foo {x: true}
f.x = false
function main() -> Foo {
let f = Foo {x: true};
f.x = false;
return f
}
```
### Conditionals:
### Assert Equals:
This will enforce that the two values are equal in the constraint system.
```rust
function main() -> (u32) {
y = if 3==3 then 1 else 5 fi
function main() {
assert_eq(45, 45);
assert_eq(2fe, 2fe);
assert_eq(true, true);
}
```
### Conditionals:
#### If Else Ternary Expression
```rust
function main() -> u32 {
let y = if 3==3 ? 1 : 5;
return y
}
```
#### If Else Conditional Statement
```rust
function main() -> (fe) {
a = 1fe
for i in 0..4 do
a = a + 1fe
endfor
function main(a: private bool, b: private bool) -> u32 {
let res = 0;
if (a) {
res = 1;
} else if (b) {
res = 2;
} else {
res = 3;
}
return res
}
```
#### For loop
```rust
function main() -> fe {
let a = 1fe;
for i in 0..4 {
a = a + 1fe;
}
return a
}
```
### Functions:
```rust
function test1(a : u32) -> (u32) {
return a + 1
function test1(a : u32) -> u32 {
return a + 1
}
function test2(b: fe) -> (fe) {
return b * 2fe
function test2(b: fe) -> fe {
return b * 2fe
}
function test3(c: bool) -> (bool) {
function test3(c: bool) -> bool {
return c && true
}
function main() -> (u32) {
function main() -> u32 {
return test1(5)
}
```
@ -134,62 +178,134 @@ function main() -> (u32) {
#### Function Scope:
```rust
function foo() -> (field) {
// return myGlobal <- not allowed
return 42fe
function foo() -> field {
// return myGlobal <- not allowed
return 42fe
}
function main() -> (field) {
myGlobal = 42fe
return foo()
function main() -> field {
let myGlobal = 42fe;
return foo()
}
```
### Parameters:
Main function arguments are allocated as public or private variables in the program's constaint system.
#### Multiple returns:
Functions can return tuples whose types are specified in the function signature.
```rust
function main(a: private fe) -> (fe) {
function test() -> (u32, u32[2]) {
return 1, [2, 3]
}
function main() -> u32[3] {
let (a, b) = test();
// (a, u32[2] b) = test() <- explicit type also works
return [a, ...b]
}
```
#### Main function inputs:
Main function inputs are allocated as public or private variables in the program's constaint system.
```rust
function main(a: private fe) -> fe {
return a
}
```
```rust
function main(a: public fe) -> (fe) {
function main(a: public fe) -> fe {
return a
}
```
Function inputs are passed by value.
```rust
function test(a: u32) {
a = 0;
}
function main() -> u32 {
let a = 1;
test(a);
return a // <- returns 1
}
```
### Imports:
Note that there can only be one main function across all imported files.
Both struct and function imports are supported.
import all: `*`
import alias: `symbol as alias`
/simple_import.leo
```rust
struct Point {
u32 x
u32 y
x: u32
y: u32
}
function test() -> (u32, u32[2]) {
return 1, [2, 3]
}
```
/simple.leo
```rust
from "./simple_import" import Point
from "./simple_import" import {
Point as Foo,
test
};
function main() -> (Point) {
Point p = Point { x: 1, y: 2}
return p
// from "./simple_import" import *
function main() -> (u32[3]) {
let p = Foo { x: 1, y: 2};
let (a, b) = test();
return [a, ...b]
}
```
Default exports are not currently supported.
There should only be one main function across all files.
# Leo CLI
## Develop
### `leo new`
To setup a new package, run:
```
leo new {$NAME}
```
This will create a new directory with a given package name. The new package will have a directory structure as follows:
```
- inputs # Your program inputs
- outputs # Your program outputs
- src
- lib.leo # Your program library
- main.leo # Your program
- tests
- tests.leo # Your program tests
- Leo.toml # Your program manifest
```
### `leo init`
To initialize an existing directory, run:
```
leo init
```
This will initialize the current directory with the same package directory setup.
### `leo build`
To compile your program and verify that it builds properly, run:
```
leo build
```
### `leo test`
To execute unit tests on your program, run:
```
leo test
@ -197,6 +313,8 @@ leo test
## Run
### `leo setup`
To perform the program setup, producing a proving key and verification key, run:
```
leo setup
@ -208,6 +326,8 @@ Leo uses cryptographic randomness from your machine to perform the setup. The pr
{$LIBRARY}/target/{$PROGRAM}.leo.vk
```
### `leo prove`
To execute the program and produce an execution proof, run:
```
leo prove
@ -220,6 +340,8 @@ Once again, Leo uses cryptographic randomness from your machine to produce the p
{$LIBRARY}/target/{$PROGRAM}.leo.proof
```
### `leo verify`
To verify the program proof, run:
```
leo verify
@ -252,7 +374,7 @@ If your gadget name has already been taken, `leo publish` will fail.
## Deploy
To deploy your program to the blockchain, run:
To deploy your program to Aleo, run:
```
leo deploy
```

View File

@ -1,4 +1,17 @@
function main(a: private u32) -> (u32) {
b = a + 5;
return a
struct Foo {
b: u32
}
function main() {
let a = 1u8 + 1u8;
let c = 1u16 + 1u16;
let b = 1u32 + 1;
let d = 1u64 + 1u64;
let e = 1u128 + 1u128;
let f = true && false;
let g = 5fe + 10fe;
let h: u32[2] = [1, 2];
let i = Foo { b: 5 };
let j = [1, 4, 5];
}

View File

@ -1,5 +1,7 @@
use leo_program::{self, ast};
use leo_compiler::{self, ast, errors::CompilerError, InputValue, Program};
use from_pest::FromPest;
use rand::thread_rng;
use snarkos_algorithms::snark::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
};
@ -9,32 +11,29 @@ use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem},
};
use from_pest::FromPest;
use rand::thread_rng;
use std::{
fs,
marker::PhantomData,
time::{Duration, Instant},
};
#[derive(Clone)]
pub struct Benchmark<F: Field + PrimeField> {
program: Program<F>,
parameters: Vec<Option<InputValue<F>>>,
_engine: PhantomData<F>,
}
impl<F: Field + PrimeField> Benchmark<F> {
pub fn new() -> Self {
Self {
program: Program::new(),
parameters: vec![],
_engine: PhantomData,
}
}
}
impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
pub fn evaluate_program(&mut self) -> Result<(), CompilerError> {
// Read in file as string
let unparsed_file = fs::read_to_string("simple.leo").expect("cannot read file");
@ -45,11 +44,26 @@ impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
// println!("{:#?}", syntax_tree);
let program = leo_program::Program::<'_, F>::from(syntax_tree);
println!(" compiled: {:#?}", program);
// Build a leo program from the syntax tree
self.program = Program::<F>::from(syntax_tree, "simple".into());
self.parameters = vec![None; self.program.num_parameters];
let program = program.name("simple".into());
leo_program::ResolvedProgram::generate_constraints(cs, program);
println!(" compiled: {:#?}\n", self.program);
Ok(())
}
}
impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
let _res =
leo_compiler::generate_constraints(cs, self.program, self.parameters).unwrap();
println!(" Result: {}", _res);
// Write results to file or something
Ok(())
}
@ -64,27 +78,33 @@ fn main() {
let start = Instant::now();
let params = {
let circuit = Benchmark::<Fr>::new();
generate_random_parameters::<Bls12_377, _, _>(circuit, rng).unwrap()
};
// Load and compile program
let mut program = Benchmark::<Fr>::new();
program.evaluate_program().unwrap();
// Generate proof parameters
let params = { generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap() };
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&params.vk);
setup += start.elapsed();
let start = Instant::now();
let proof = {
let c = Benchmark::new();
create_random_proof(c, &params, rng).unwrap()
};
// Set main function arguments in compiled program
// let argument = Some(ParameterValue::Field(Fr::one()));
// program.parameters = vec![argument];
// Generate proof
let proof = create_random_proof(program, &params, rng).unwrap();
proving += start.elapsed();
// let _inputs: Vec<_> = [1u32; 1].to_vec();
let start = Instant::now();
// let public_input = Fr::one();
// Verify proof
let is_success = verify_proof(&prepared_verifying_key, &proof, &[]).unwrap();
verifying += start.elapsed();

View File

@ -11,10 +11,13 @@ snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0" }
snarkos-gadgets = { path = "../../snarkOS/gadgets", version = "0.8.0" }
snarkos-models = { path = "../../snarkOS/models", version = "0.8.0" }
thiserror = { version = "1.0" }
from-pest = { version = "0.3.1" }
hex = { version = "0.4.2" }
lazy_static = { version = "1.3.0" }
log = { version = "0.4" }
pest = { version = "2.0" }
pest-ast = { version = "0.3.3" }
pest_derive = { version = "2.0" }
rand = { version = "0.7" }
sha2 = { version = "0.8" }

View File

@ -1,8 +1,4 @@
//! Abstract syntax tree (ast) representation from leo.pest.
//!
//! @file zokrates_program.rs
//! @author Howard Wu <howard@aleo.org>
//! @date 2020
use from_pest::{ConversionError, FromPest, Void};
use pest::{
@ -89,31 +85,89 @@ pub enum BinaryOperator {
Pow,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::assign))]
pub struct Assign {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_add_assign))]
pub struct AddAssign {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_sub_assign))]
pub struct SubAssign {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_mul_assign))]
pub struct MulAssign {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_div_assign))]
pub struct DivAssign {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_pow_assign))]
pub struct PowAssign {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_assign))]
pub enum OperationAssign {
Assign(Assign),
AddAssign(AddAssign),
SubAssign(SubAssign),
MulAssign(MulAssign),
DivAssign(DivAssign),
PowAssign(PowAssign),
}
// Types
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_u32))]
pub struct U32Type<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
#[pest_ast(rule(Rule::type_u8))]
pub struct U8Type {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::type_u16))]
pub struct U16Type {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::type_u32))]
pub struct U32Type {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::type_u64))]
pub struct U64Type {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::type_u128))]
pub struct U128Type {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::type_integer))]
pub enum IntegerType {
U8Type(U8Type),
U16Type(U16Type),
U32Type(U32Type),
U64Type(U64Type),
U128Type(U128Type),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_field))]
#[pest_ast(rule(Rule::type_field))]
pub struct FieldType<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_bool))]
#[pest_ast(rule(Rule::type_bool))]
pub struct BooleanType<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_struct))]
#[pest_ast(rule(Rule::type_struct))]
pub struct StructType<'ast> {
pub variable: Variable<'ast>,
#[pest_ast(outer())]
@ -121,31 +175,31 @@ pub struct StructType<'ast> {
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_basic))]
#[pest_ast(rule(Rule::type_basic))]
pub enum BasicType<'ast> {
U32(U32Type<'ast>),
Integer(IntegerType),
Field(FieldType<'ast>),
Boolean(BooleanType<'ast>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_basic_or_struct))]
#[pest_ast(rule(Rule::type_basic_or_struct))]
pub enum BasicOrStructType<'ast> {
Struct(StructType<'ast>),
Basic(BasicType<'ast>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_array))]
#[pest_ast(rule(Rule::type_array))]
pub struct ArrayType<'ast> {
pub ty: BasicType<'ast>,
pub _type: BasicType<'ast>,
pub count: Value<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty))]
#[pest_ast(rule(Rule::_type))]
pub enum Type<'ast> {
Basic(BasicType<'ast>),
Array(ArrayType<'ast>),
@ -155,9 +209,9 @@ pub enum Type<'ast> {
impl<'ast> fmt::Display for Type<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Basic(ref _ty) => write!(f, "basic"),
Type::Array(ref _ty) => write!(f, "array"),
Type::Struct(ref _ty) => write!(f, "struct"),
Type::Basic(ref _type) => write!(f, "basic"),
Type::Array(ref _type) => write!(f, "array"),
Type::Struct(ref _type) => write!(f, "struct"),
}
}
}
@ -179,15 +233,15 @@ impl<'ast> fmt::Display for Number<'ast> {
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_u32))]
pub struct U32<'ast> {
#[pest_ast(rule(Rule::value_integer))]
pub struct Integer<'ast> {
pub number: Number<'ast>,
pub ty: Option<U32Type<'ast>>,
pub _type: Option<IntegerType>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for U32<'ast> {
impl<'ast> fmt::Display for Integer<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.number)
}
@ -197,7 +251,7 @@ impl<'ast> fmt::Display for U32<'ast> {
#[pest_ast(rule(Rule::value_field))]
pub struct Field<'ast> {
pub number: Number<'ast>,
pub ty: FieldType<'ast>,
pub _type: FieldType<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
@ -226,15 +280,15 @@ impl<'ast> fmt::Display for Boolean<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value))]
pub enum Value<'ast> {
Integer(Integer<'ast>),
Field(Field<'ast>),
Boolean(Boolean<'ast>),
U32(U32<'ast>),
}
impl<'ast> Value<'ast> {
pub fn span(&self) -> &Span<'ast> {
match self {
Value::U32(value) => &value.span,
Value::Integer(value) => &value.span,
Value::Field(value) => &value.span,
Value::Boolean(value) => &value.span,
}
@ -244,7 +298,7 @@ impl<'ast> Value<'ast> {
impl<'ast> fmt::Display for Value<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Value::U32(ref value) => write!(f, "{}", value),
Value::Integer(ref value) => write!(f, "{}", value),
Value::Field(ref value) => write!(f, "{}", value),
Value::Boolean(ref value) => write!(f, "{}", value),
}
@ -271,7 +325,7 @@ impl<'ast> fmt::Display for Variable<'ast> {
#[derive(Debug, FromPest, PartialEq, Clone)]
#[pest_ast(rule(Rule::optionally_typed_variable))]
pub struct OptionallyTypedVariable<'ast> {
pub ty: Option<Type<'ast>>,
pub _type: Option<Type<'ast>>,
pub id: Variable<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -464,8 +518,8 @@ pub struct ArrayInitializerExpression<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::struct_field))]
pub struct StructField<'ast> {
pub ty: Type<'ast>,
pub variable: Variable<'ast>,
pub _type: Type<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
@ -512,7 +566,6 @@ pub struct NotExpression<'ast> {
// #[pest_ast(rule(Rule::expression_increment))]
// pub struct IncrementExpression<'ast> {
// pub expression: Box<Expression<'ast>>,
// pub operation: Increment<'ast>,
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
@ -521,7 +574,6 @@ pub struct NotExpression<'ast> {
// #[pest_ast(rule(Rule::expression_decrement))]
// pub struct DecrementExpression<'ast> {
// pub expression: Box<Expression<'ast>>,
// pub operation: Decrement<'ast>,
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
@ -618,7 +670,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
}
Expression::Ternary(ref expression) => write!(
f,
"if {} then {} else {} fi",
"if {} ? {} : {}",
expression.first, expression.second, expression.third
),
Expression::ArrayInline(ref expression) => {
@ -700,25 +752,25 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
},
// Rule::expression_increment => {
// println!("expression increment");
// // let span = next.as_span();
// // let mut inner = next.into_inner();
// // let expression = parse_term(inner.next().unwrap());
// let span = next.as_span();
// let mut inner = next.into_inner();
// let expression = parse_term(inner.next().unwrap());
// // let operation = match inner.next().unwrap().as_rule() {
// // Rule::operation_post_increment => Increment::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
// // rule => unreachable!("`expression_increment` should yield `operation_post_increment`, found {:#?}", rule)
// // };
// // Expression::Increment(IncrementExpression { operation, expression, span })
// Expression::Increment(IncrementExpression { expression, span })
// },
// Rule::expression_decrement => {
// println!("expression decrement");
// // let span = next.as_span();
// // let mut inner = next.into_inner();
// // let expression = parse_term(inner.next().unwrap());
// let span = next.as_span();
// let mut inner = next.into_inner();
// let expression = parse_term(inner.next().unwrap());
// // let operation = match inner.next().unwrap().as_rule() {
// // Rule::operation_post_decrement => Decrement::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
// // rule => unreachable!("`expression_decrement` should yield `operation_post_decrement`, found {:#?}", rule)
// // };
// // Expression::Decrement(DecrementExpression { operation, expression, span })
// Expression::Decrement(DecrementExpression { expression, span })
// },
Rule::expression_postfix => {
Expression::Postfix(
@ -805,6 +857,23 @@ pub struct ReturnStatement<'ast> {
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::conditional_nested_or_end))]
pub enum ConditionalNestedOrEnd<'ast> {
Nested(Box<ConditionalStatement<'ast>>),
End(Vec<Statement<'ast>>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_conditional))]
pub struct ConditionalStatement<'ast> {
pub condition: Expression<'ast>,
pub statements: Vec<Statement<'ast>>,
pub next: Option<ConditionalNestedOrEnd<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_for))]
pub struct ForStatement<'ast> {
@ -829,8 +898,8 @@ pub struct MultipleAssignmentStatement<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_definition))]
pub struct DefinitionStatement<'ast> {
pub ty: Type<'ast>,
pub variable: Variable<'ast>,
pub _type: Option<Type<'ast>>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -840,6 +909,30 @@ pub struct DefinitionStatement<'ast> {
#[pest_ast(rule(Rule::statement_assign))]
pub struct AssignStatement<'ast> {
pub assignee: Assignee<'ast>,
pub assign: OperationAssign,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::assert_eq))]
pub struct AssertEq<'ast> {
pub left: Expression<'ast>,
pub right: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_assert))]
pub enum AssertStatement<'ast> {
AssertEq(AssertEq<'ast>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_expression))]
pub struct ExpressionStatement<'ast> {
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -849,10 +942,13 @@ pub struct AssignStatement<'ast> {
#[pest_ast(rule(Rule::statement))]
pub enum Statement<'ast> {
Return(ReturnStatement<'ast>),
Iteration(ForStatement<'ast>),
MultipleAssignment(MultipleAssignmentStatement<'ast>),
Definition(DefinitionStatement<'ast>),
Assign(AssignStatement<'ast>),
MultipleAssignment(MultipleAssignmentStatement<'ast>),
Conditional(ConditionalStatement<'ast>),
Iteration(ForStatement<'ast>),
Assert(AssertStatement<'ast>),
Expression(ExpressionStatement<'ast>),
}
impl<'ast> fmt::Display for ReturnStatement<'ast> {
@ -867,11 +963,33 @@ impl<'ast> fmt::Display for ReturnStatement<'ast> {
}
}
impl<'ast> fmt::Display for ConditionalNestedOrEnd<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConditionalNestedOrEnd::Nested(ref nested) => write!(f, "else {}", nested),
ConditionalNestedOrEnd::End(ref statements) => {
write!(f, "else {{\n \t{:#?}\n }}", statements)
}
}
}
}
impl<'ast> fmt::Display for ConditionalStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "if ({}) {{\n", self.condition)?;
write!(f, "\t{:#?}\n", self.statements)?;
self.next
.as_ref()
.map(|n_or_e| write!(f, "}} {}", n_or_e))
.unwrap_or(write!(f, "}}"))
}
}
impl<'ast> fmt::Display for ForStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"for {} in {}..{} do {:#?} endfor",
"for {} in {}..{} {{ {:#?} }}",
self.index, self.start, self.stop, self.statements
)
}
@ -891,13 +1009,30 @@ impl<'ast> fmt::Display for MultipleAssignmentStatement<'ast> {
impl<'ast> fmt::Display for DefinitionStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} = {}", self.ty, self.variable, self.expression)
match self._type {
Some(ref _type) => write!(
f,
"let {} : {} = {};",
self.variable, _type, self.expression
),
None => write!(f, "let {} = {}", self.variable, self.expression),
}
}
}
impl<'ast> fmt::Display for AssignStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.assignee, self.expression)
write!(f, "{} = {};", self.assignee, self.expression)
}
}
impl<'ast> fmt::Display for AssertStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
AssertStatement::AssertEq(ref assert) => {
write!(f, "assert_eq({}, {});", assert.left, assert.right)
}
}
}
}
@ -905,10 +1040,13 @@ impl<'ast> fmt::Display for Statement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Return(ref statement) => write!(f, "{}", statement),
Statement::Iteration(ref statement) => write!(f, "{}", statement),
Statement::MultipleAssignment(ref statement) => write!(f, "{}", statement),
Statement::Assign(ref statement) => write!(f, "{}", statement),
Statement::Definition(ref statement) => write!(f, "{}", statement),
Statement::Assign(ref statement) => write!(f, "{}", statement),
Statement::MultipleAssignment(ref statement) => write!(f, "{}", statement),
Statement::Conditional(ref statement) => write!(f, "{}", statement),
Statement::Iteration(ref statement) => write!(f, "{}", statement),
Statement::Assert(ref statement) => write!(f, "{}", statement),
Statement::Expression(ref statement) => write!(f, "{}", statement.expression),
}
}
}
@ -920,7 +1058,7 @@ impl<'ast> fmt::Display for Statement<'ast> {
pub struct Parameter<'ast> {
pub variable: Variable<'ast>,
pub visibility: Option<Visibility>,
pub ty: Type<'ast>,
pub _type: Type<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}

View File

@ -1,4 +1,11 @@
use crate::{ast, Program, ResolvedProgram};
//! Compiles a Leo program from a file path.
use crate::{
ast,
constraints::{generate_constraints, ConstrainedValue},
errors::CompilerError,
InputValue, Program,
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
@ -7,21 +14,80 @@ use snarkos_models::{
};
use from_pest::FromPest;
use std::{
fs,
marker::PhantomData,
path::PathBuf,
};
use sha2::{Digest, Sha256};
use std::{fs, marker::PhantomData, path::PathBuf};
#[derive(Clone)]
pub struct Compiler<F: Field + PrimeField> {
package_name: String,
main_file_path: PathBuf,
program: Program<F>,
program_inputs: Vec<Option<InputValue<F>>>,
output: Option<ConstrainedValue<F>>,
_engine: PhantomData<F>,
}
impl<F: Field + PrimeField> Compiler<F> {
pub fn init(main_file_path: PathBuf) -> Self {
Self { main_file_path, _engine: PhantomData }
pub fn init(package_name: String, main_file_path: PathBuf) -> Self {
Self {
package_name,
main_file_path,
program: Program::new(),
program_inputs: vec![],
output: None,
_engine: PhantomData,
}
}
pub fn checksum(&self) -> Result<String, CompilerError> {
// Read in the main file as string
let unparsed_file = fs::read_to_string(&self.main_file_path)
.map_err(|_| CompilerError::FileReadError(self.main_file_path.clone()))?;
// Hash the file contents
let mut hasher = Sha256::new();
hasher.input(unparsed_file.as_bytes());
let hash = hasher.result();
Ok(hex::encode(hash))
}
// pub fn compile(&self) -> Result<ast::File, CompilerError> {
// // Read in the main file as string
// let unparsed_file = fs::read_to_string(&self.main_file_path).map_err(|_| CompilerError::FileReadError(self.main_file_path.clone()))?;
//
// // Parse the file using leo.pest
// let mut file = ast::parse(&unparsed_file).map_err(|_| CompilerError::FileParsingError)?;
//
// // Build the abstract syntax tree
// let syntax_tree = ast::File::from_pest(&mut file).map_err(|_| CompilerError::SyntaxTreeError)?;
// log::debug!("{:#?}", syntax_tree);
//
// Ok(syntax_tree)
// }
pub fn evaluate_program<CS: ConstraintSystem<F>>(&mut self) -> Result<(), CompilerError> {
// Read in the main file as string
let unparsed_file = fs::read_to_string(&self.main_file_path)
.map_err(|_| CompilerError::FileReadError(self.main_file_path.clone()))?;
// Parse the file using leo.pest
let mut file = ast::parse(&unparsed_file).map_err(|_| CompilerError::FileParsingError)?;
// Build the abstract syntax tree
let syntax_tree =
ast::File::from_pest(&mut file).map_err(|_| CompilerError::SyntaxTreeError)?;
log::debug!("{:#?}", syntax_tree);
// Build program from abstract syntax tree
let package_name = self.package_name.clone();
self.program = Program::<F>::from(syntax_tree, package_name);
self.program_inputs = vec![None; self.program.num_parameters];
log::debug!("Compilation complete\n{:#?}", self.program);
Ok(())
}
}
@ -30,22 +96,10 @@ impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Compiler<F> {
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
// Read in the main file as string
let unparsed_file = fs::read_to_string(&self.main_file_path).expect("cannot read file");
let _res = generate_constraints(cs, self.program, self.program_inputs).unwrap();
// Parse the file using leo.pest
let mut file = ast::parse(&unparsed_file).expect("unsuccessful parse");
// Build the abstract syntax tree
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
log::debug!("{:#?}", syntax_tree);
let program = Program::<'_, F>::from(syntax_tree);
log::info!(" compiled: {:#?}", program);
let program = program.name("simple".into());
ResolvedProgram::generate_constraints(cs, program);
// Write results to file or something
Ok(())
}
}
}

View File

@ -1,78 +1,77 @@
//! Methods to enforce constraints on booleans in a resolved aleo program.
//! Methods to enforce constraints on booleans in a resolved Leo program.
use crate::constraints::{ResolvedProgram, ResolvedValue};
use crate::{new_variable_from_variable, Parameter, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, boolean::Boolean, eq::EqGadget},
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::BooleanError,
types::{InputModel, InputValue, Variable},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, boolean::Boolean, eq::EqGadget},
},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn bool_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<bool>()
.expect(&format!(
"expected main function parameter {} at index {}",
parameter, index
));
// Check visibility of parameter
let name = parameter.variable.name.clone();
let number = if parameter.private {
Boolean::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
} else {
Boolean::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
parameter_model: InputModel<F>,
parameter_value: Option<InputValue<F>>,
) -> Result<Variable<F>, BooleanError> {
// Check that the parameter value is the correct type
let bool_value = match parameter_value {
Some(parameter) => {
if let InputValue::Boolean(bool) = parameter {
Some(bool)
} else {
return Err(BooleanError::InvalidBoolean(parameter.to_string()));
}
}
None => None,
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let number = if parameter_model.private {
Boolean::alloc(cs.ns(|| name), || {
bool_value.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
Boolean::alloc_input(cs.ns(|| name), || {
bool_value.ok_or(SynthesisError::AssignmentMissing)
})?
};
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(parameter_variable.clone(), ResolvedValue::Boolean(number));
self.store_variable(
parameter_variable.clone(),
ConstrainedValue::Boolean(number),
);
parameter_variable
Ok(parameter_variable)
}
pub(crate) fn boolean_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
) -> Variable<F> {
_parameter_model: InputModel<F>,
_parameter_value: Option<InputValue<F>>,
) -> Result<Variable<F>, BooleanError> {
unimplemented!("Cannot enforce boolean array as parameter")
// // Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<bool>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// let number = if parameter.private {
// Boolean::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
// Boolean::alloc(cs.ns(|| name), ||bool_value.ok_or(SynthesisError::AssignmentMissing).unwrap()
// } else {
// Boolean::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
// };
@ -89,62 +88,63 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
// parameter_variable
}
pub(crate) fn get_boolean_constant(bool: bool) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(bool))
pub(crate) fn get_boolean_constant(bool: Boolean) -> ConstrainedValue<F> {
ConstrainedValue::Boolean(bool)
}
pub(crate) fn enforce_not(value: ResolvedValue<F>) -> ResolvedValue<F> {
pub(crate) fn evaluate_not(
value: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, BooleanError> {
match value {
ResolvedValue::Boolean(boolean) => ResolvedValue::Boolean(boolean.not()),
value => unimplemented!("cannot enforce not on non-boolean value {}", value),
ConstrainedValue::Boolean(boolean) => Ok(ConstrainedValue::Boolean(boolean.not())),
value => Err(BooleanError::CannotEvaluate(format!("!{}", value))),
}
}
pub(crate) fn enforce_or(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, BooleanError> {
match (left, right) {
(ResolvedValue::Boolean(left_bool), ResolvedValue::Boolean(right_bool)) => {
ResolvedValue::Boolean(Boolean::or(cs, &left_bool, &right_bool).unwrap())
}
(left_value, right_value) => unimplemented!(
"cannot enforce or on non-boolean values {} || {}",
left_value,
right_value
(ConstrainedValue::Boolean(left_bool), ConstrainedValue::Boolean(right_bool)) => Ok(
ConstrainedValue::Boolean(Boolean::or(cs, &left_bool, &right_bool)?),
),
(left_value, right_value) => Err(BooleanError::CannotEnforce(format!(
"{} || {}",
left_value, right_value
))),
}
}
pub(crate) fn enforce_and(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, BooleanError> {
match (left, right) {
(ResolvedValue::Boolean(left_bool), ResolvedValue::Boolean(right_bool)) => {
ResolvedValue::Boolean(Boolean::and(cs, &left_bool, &right_bool).unwrap())
}
(left_value, right_value) => unimplemented!(
"cannot enforce and on non-boolean values {} && {}",
left_value,
right_value
(ConstrainedValue::Boolean(left_bool), ConstrainedValue::Boolean(right_bool)) => Ok(
ConstrainedValue::Boolean(Boolean::and(cs, &left_bool, &right_bool)?),
),
(left_value, right_value) => Err(BooleanError::CannotEnforce(format!(
"{} && {}",
left_value, right_value
))),
}
}
pub(crate) fn boolean_eq(left: Boolean, right: Boolean) -> ConstrainedValue<F> {
ConstrainedValue::Boolean(Boolean::Constant(left.eq(&right)))
}
pub(crate) fn enforce_boolean_eq(
&mut self,
cs: &mut CS,
left: Boolean,
right: Boolean,
) -> ResolvedValue<F> {
left.enforce_equal(cs.ns(|| format!("enforce bool equal")), &right)
.unwrap();
ResolvedValue::Boolean(Boolean::Constant(true))
) -> Result<(), BooleanError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce bool equal")), &right)?)
}
}

View File

@ -1,358 +0,0 @@
//! Methods to enforce constraints and construct a resolved aleo program.
use crate::ast;
use crate::constraints::{
new_scope, new_scope_from_variable, new_variable_from_variables, ResolvedProgram, ResolvedValue,
};
use crate::{Expression, Function, Import, Program, Statement, Type};
use from_pest::FromPest;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use std::fs;
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn enforce_function(
&mut self,
cs: &mut CS,
scope: String,
function: Function<F>,
arguments: Vec<Expression<F>>,
) -> ResolvedValue<F> {
let function_name = new_scope(scope.clone(), function.get_name());
// Make sure we are given the correct number of arguments
if function.parameters.len() != arguments.len() {
unimplemented!(
"function expected {} arguments, got {}",
function.parameters.len(),
arguments.len()
)
}
// Store arguments as variables in resolved program
function
.parameters
.clone()
.iter()
.zip(arguments.clone().into_iter())
.for_each(|(parameter, argument)| {
// Check that argument is correct type
match parameter.ty.clone() {
Type::U32 => {
match self.enforce_expression(
cs,
scope.clone(),
function_name.clone(),
argument,
) {
ResolvedValue::U32(number) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::U32(number));
}
argument => {
unimplemented!("expected integer argument got {}", argument)
}
}
}
Type::FieldElement => {
match self.enforce_expression(
cs,
scope.clone(),
function_name.clone(),
argument,
) {
ResolvedValue::FieldElement(field) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::FieldElement(field));
}
argument => unimplemented!("expected field argument got {}", argument),
}
}
Type::Boolean => {
match self.enforce_expression(
cs,
scope.clone(),
function_name.clone(),
argument,
) {
ResolvedValue::Boolean(bool) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::Boolean(bool));
}
argument => {
unimplemented!("expected boolean argument got {}", argument)
}
}
}
ty => unimplemented!("parameter type {} not matched yet", ty),
}
});
// Evaluate function statements
let mut return_values = ResolvedValue::Return(vec![]);
function
.statements
.clone()
.into_iter()
.for_each(|statement| match statement {
Statement::Return(expressions) => {
return_values = self.enforce_return_statement(
cs,
scope.clone(),
function_name.clone(),
expressions,
function.returns.to_owned(),
);
}
Statement::Assign(variable, expression) => {
self.enforce_assign_statement(
cs,
scope.clone(),
function_name.clone(),
variable,
expression,
);
}
Statement::Definition(ty, assignee, expression) => {
self.enforce_definition_statement(
cs,
scope.clone(),
function_name.clone(),
ty,
assignee,
expression,
);
}
Statement::MultipleDefinition(assignees, function_call) => {
self.enforce_multiple_definition_statement(
cs,
scope.clone(),
function_name.clone(),
assignees,
function_call,
);
}
Statement::For(index, start, stop, statements) => {
self.enforce_for_statement(
cs,
scope.clone(),
function_name.clone(),
index,
start,
stop,
statements,
);
}
});
return_values
}
fn enforce_main_function(
&mut self,
cs: &mut CS,
scope: String,
function: Function<F>,
) -> ResolvedValue<F> {
let function_name = new_scope(scope.clone(), function.get_name());
let mut arguments = vec![];
// Iterate over main function parameters
function
.parameters
.clone()
.into_iter()
.enumerate()
.for_each(|(i, parameter)| {
// append each variable to arguments vector
arguments.push(Expression::Variable(match parameter.ty {
Type::U32 => {
self.u32_from_parameter(cs, function_name.clone(), i + 1, parameter)
}
Type::FieldElement => self.field_element_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
),
Type::Boolean => {
self.bool_from_parameter(cs, function_name.clone(), i + 1, parameter)
}
Type::Array(ref ty, _length) => match *ty.clone() {
Type::U32 => self.u32_array_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
),
Type::FieldElement => self.field_element_array_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
),
Type::Boolean => self.boolean_array_from_parameter(
cs,
function_name.clone(),
i + 1,
parameter,
),
ty => unimplemented!("parameter type not implemented {}", ty),
},
ty => unimplemented!("parameter type not implemented {}", ty),
}))
});
self.enforce_function(cs, scope, function, arguments)
}
fn enforce_import(&mut self, cs: &mut CS, scope: String, import: Import<F>) {
// Resolve program file path
let unparsed_file = fs::read_to_string(import.get_file())
.expect(&format!("cannot parse import {}", import.get_file()));
let mut file = ast::parse(&unparsed_file)
.expect(&format!("cannot parse import {}", import.get_file()));
// generate ast from file
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
// generate aleo program from file
let mut program = Program::from(syntax_tree);
// Use same namespace as calling function for imported symbols
program = program.name(scope);
// * -> import all imports, structs, functions in the current scope
if import.is_star() {
// recursively evaluate program statements
self.resolve_definitions(cs, program);
} else {
let program_name = program.name.clone();
// match each import symbol to a symbol in the imported file
import.symbols.into_iter().for_each(|symbol| {
// see if the imported symbol is a struct
let matched_struct = program
.structs
.clone()
.into_iter()
.find(|(struct_name, _struct_def)| symbol.symbol == *struct_name);
match matched_struct {
Some((_struct_name, struct_def)) => {
// take the alias if it is present
let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
let resolved_struct_name =
new_variable_from_variables(&program_name.clone(), &resolved_name);
// store imported struct under resolved name
self.store_variable(
resolved_struct_name,
ResolvedValue::StructDefinition(struct_def),
);
}
None => {
// see if the imported symbol is a function
let matched_function = program.functions.clone().into_iter().find(
|(function_name, _function)| symbol.symbol.name == *function_name.0,
);
match matched_function {
Some((_function_name, function)) => {
// take the alias if it is present
let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
let resolved_function_name = new_variable_from_variables(
&program_name.clone(),
&resolved_name,
);
// store imported function under resolved name
self.store_variable(
resolved_function_name,
ResolvedValue::Function(function),
)
}
None => unimplemented!(
"cannot find imported symbol {} in imported file {}",
symbol,
program_name.clone()
),
}
}
}
});
// evaluate all import statements in imported file
program.imports.into_iter().for_each(|nested_import| {
self.enforce_import(cs, program_name.name.clone(), nested_import)
});
}
}
pub fn resolve_definitions(&mut self, cs: &mut CS, program: Program<F>) {
let program_name = program.name.clone();
// evaluate and store all imports
program
.imports
.into_iter()
.for_each(|import| self.enforce_import(cs, program_name.name.clone(), import));
// evaluate and store all struct definitions
program
.structs
.into_iter()
.for_each(|(variable, struct_def)| {
let resolved_struct_name =
new_variable_from_variables(&program_name.clone(), &variable);
self.store_variable(
resolved_struct_name,
ResolvedValue::StructDefinition(struct_def),
);
});
// evaluate and store all function definitions
program
.functions
.into_iter()
.for_each(|(function_name, function)| {
let resolved_function_name = new_scope(program_name.name.clone(), function_name.0);
self.store(resolved_function_name, ResolvedValue::Function(function));
});
}
pub fn generate_constraints(cs: &mut CS, program: Program<F>) {
let mut resolved_program = ResolvedProgram::new();
let program_name = program.get_name();
let main_function_name = new_scope(program_name.clone(), "main".into());
resolved_program.resolve_definitions(cs, program);
let main = resolved_program
.get(&main_function_name)
.expect("main function not defined");
let result = match main.clone() {
ResolvedValue::Function(function) => {
resolved_program.enforce_main_function(cs, program_name, function)
}
_ => unimplemented!("main must be a function"),
};
println!("\n {}", result);
}
}

View File

@ -1,33 +1,39 @@
//! Methods to enforce constraints on expressions in a resolved aleo program.
//! Methods to enforce constraints on expressions in a resolved Leo program.
use crate::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
use crate::{
new_variable_from_variable, Expression, RangeOrExpression, ResolvedStructMember,
SpreadOrExpression, StructMember, Variable,
constraints::{
new_scope_from_variable, new_variable_from_variable, ConstrainedProgram,
ConstrainedStructMember, ConstrainedValue,
},
errors::ExpressionError,
types::{Expression, RangeOrExpression, SpreadOrExpression, StructMember, Variable},
};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use snarkos_models::gadgets::utilities::boolean::Boolean;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
/// Enforce a variable expression by getting the resolved value
fn enforce_variable(
pub(crate) fn enforce_variable(
&mut self,
scope: String,
unresolved_variable: Variable<F>,
) -> ResolvedValue<F> {
) -> Result<ConstrainedValue<F>, ExpressionError> {
// Evaluate the variable name in the current function scope
let variable_name = new_scope_from_variable(scope, &unresolved_variable);
if self.contains_name(&variable_name) {
// Reassigning variable to another variable
self.get_mut(&variable_name).unwrap().clone()
Ok(self.get_mut(&variable_name).unwrap().clone())
} else if self.contains_variable(&unresolved_variable) {
// Check global scope (function and struct names)
self.get_mut_variable(&unresolved_variable).unwrap().clone()
Ok(self.get_mut_variable(&unresolved_variable).unwrap().clone())
} else {
unimplemented!("variable declaration {} not found", variable_name)
Err(ExpressionError::UndefinedVariable(
unresolved_variable.to_string(),
))
}
}
@ -35,105 +41,201 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
fn enforce_add_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_add(cs, num1, num2)
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
Ok(match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_add(cs, num_1, num_2)?
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_add(fe1, fe2)
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_add(cs, fe_1, fe_2)?
}
(val1, val2) => unimplemented!("cannot add {} + {}", val1, val2),
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} + {}",
val_1, val_2,
)))
}
})
}
fn enforce_sub_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_sub(cs, num1, num2)
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
Ok(match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_sub(cs, num_1, num_2)?
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_sub(fe1, fe2)
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_sub(cs, fe_1, fe_2)?
}
(val1, val2) => unimplemented!("cannot subtract {} - {}", val1, val2),
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} - {}",
val_1, val_2,
)))
}
})
}
fn enforce_mul_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_mul(cs, num1, num2)
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
Ok(match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_mul(cs, num_1, num_2)?
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_mul(fe1, fe2)
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_mul(cs, fe_1, fe_2)?
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} * {}",
val_1, val_2,
)))
}
})
}
fn enforce_div_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_div(cs, num1, num2)
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
Ok(match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_div(cs, num_1, num_2)?
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_div(fe1, fe2)
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_div(cs, fe_1, fe_2)?
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} / {}",
val_1, val_2,
)))
}
})
}
fn enforce_pow_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
Ok(match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_pow(cs, num_1, num_2)?
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::Integer(num_2)) => {
self.enforce_field_pow(cs, fe_1, num_2)?
}
(_, ConstrainedValue::FieldElement(num_2)) => {
return Err(ExpressionError::InvalidExponent(num_2.to_string()))
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} * {}",
val_1, val_2,
)))
}
})
}
/// Evaluate Boolean operations
fn evaluate_eq_expression(
&mut self,
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
Ok(match (left, right) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
Self::boolean_eq(bool_1, bool_2)
}
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::evaluate_integer_eq(num_1, num_2)?
}
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_eq(fe_1, fe_2)
// }
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} == {}",
val_1, val_2,
)))
}
})
}
fn evaluate_geq_expression(
&mut self,
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_pow(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_pow(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_geq(fe_1, fe_2)
// }
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} >= {}, values must be fields",
val_1, val_2
))),
}
}
/// Enforce Boolean operations
fn enforce_eq_expression(
fn evaluate_gt_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
match (left, right) {
(ResolvedValue::Boolean(bool1), ResolvedValue::Boolean(bool2)) => {
self.enforce_boolean_eq(cs, bool1, bool2)
}
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_eq(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_eq(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot enforce equality between {} == {}", val1, val2),
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_gt(fe_1, fe_2)
// }
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} > {}, values must be fields",
val_1, val_2
))),
}
}
fn evaluate_leq_expression(
&mut self,
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
match (left, right) {
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_leq(fe_1, fe_2)
// }
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} <= {}, values must be fields",
val_1, val_2
))),
}
}
fn evaluate_lt_expression(
&mut self,
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<ConstrainedValue<F>, ExpressionError> {
match (left, right) {
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_lt(fe_1, fe_2)
// }
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} < {}, values must be fields",
val_1, val_2,
))),
}
}
@ -144,37 +246,36 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
file_scope: String,
function_scope: String,
array: Vec<Box<SpreadOrExpression<F>>>,
) -> ResolvedValue<F> {
) -> Result<ConstrainedValue<F>, ExpressionError> {
let mut result = vec![];
array.into_iter().for_each(|element| match *element {
SpreadOrExpression::Spread(spread) => match spread {
Expression::Variable(variable) => {
let array_name = new_scope_from_variable(function_scope.clone(), &variable);
match self.get(&array_name) {
Some(value) => match value {
ResolvedValue::Array(array) => result.extend(array.clone()),
value => {
unimplemented!("spreads only implemented for arrays, got {}", value)
}
},
None => unimplemented!(
"cannot copy elements from array that does not exist {}",
variable.name
),
for element in array.into_iter() {
match *element {
SpreadOrExpression::Spread(spread) => match spread {
Expression::Variable(variable) => {
let array_name = new_scope_from_variable(function_scope.clone(), &variable);
match self.get(&array_name) {
Some(value) => match value {
ConstrainedValue::Array(array) => result.extend(array.clone()),
value => {
return Err(ExpressionError::InvalidSpread(value.to_string()));
}
},
None => return Err(ExpressionError::UndefinedArray(variable.name)),
}
}
value => return Err(ExpressionError::InvalidSpread(value.to_string())),
},
SpreadOrExpression::Expression(expression) => {
result.push(self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?);
}
value => unimplemented!("spreads only implemented for arrays, got {}", value),
},
SpreadOrExpression::Expression(expression) => {
result.push(self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
));
}
});
ResolvedValue::Array(result)
}
Ok(ConstrainedValue::Array(result))
}
pub(crate) fn enforce_index(
@ -183,10 +284,10 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
file_scope: String,
function_scope: String,
index: Expression<F>,
) -> usize {
match self.enforce_expression(cs, file_scope, function_scope, index) {
ResolvedValue::U32(number) => number.value.unwrap() as usize,
value => unimplemented!("From index must resolve to an integer, got {}", value),
) -> Result<usize, ExpressionError> {
match self.enforce_expression(cs, file_scope, function_scope, index)? {
ConstrainedValue::Integer(number) => Ok(number.to_usize()),
value => Err(ExpressionError::InvalidIndex(value.to_string())),
}
}
@ -197,9 +298,9 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
function_scope: String,
array: Box<Expression<F>>,
index: RangeOrExpression<F>,
) -> ResolvedValue<F> {
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *array) {
ResolvedValue::Array(array) => {
) -> Result<ConstrainedValue<F>, ExpressionError> {
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *array)? {
ConstrainedValue::Array(array) => {
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
@ -210,16 +311,18 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
Some(to_index) => to_index.to_usize(),
None => array.len(), // Array slice ends at array length
};
ResolvedValue::Array(array[from_resolved..to_resolved].to_owned())
Ok(ConstrainedValue::Array(
array[from_resolved..to_resolved].to_owned(),
))
}
RangeOrExpression::Expression(index) => {
let index_resolved =
self.enforce_index(cs, file_scope, function_scope, index);
array[index_resolved].to_owned()
self.enforce_index(cs, file_scope, function_scope, index)?;
Ok(array[index_resolved].to_owned())
}
}
}
value => unimplemented!("Cannot access element of untyped array {}", value),
value => Err(ExpressionError::InvalidArrayAccess(value.to_string())),
}
}
@ -230,42 +333,42 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
function_scope: String,
variable: Variable<F>,
members: Vec<StructMember<F>>,
) -> ResolvedValue<F> {
) -> Result<ConstrainedValue<F>, ExpressionError> {
let struct_name = new_variable_from_variable(file_scope.clone(), &variable);
if let Some(resolved_value) = self.get_mut_variable(&struct_name) {
match resolved_value {
ResolvedValue::StructDefinition(struct_definition) => {
let resolved_members = struct_definition
.fields
.clone()
.iter()
.zip(members.clone().into_iter())
.map(|(field, member)| {
if field.variable != member.variable {
unimplemented!("struct field variables do not match")
}
// Resolve and enforce struct fields
let member_value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
member.expression,
);
ResolvedStructMember(member.variable, member_value)
})
.collect();
ResolvedValue::StructExpression(variable, resolved_members)
if let Some(ConstrainedValue::StructDefinition(struct_definition)) =
self.get_mut_variable(&struct_name)
{
let mut resolved_members = vec![];
for (field, member) in struct_definition
.fields
.clone()
.into_iter()
.zip(members.clone().into_iter())
{
if field.variable != member.variable {
return Err(ExpressionError::InvalidStructField(
field.variable.name,
member.variable.name,
));
}
_ => unimplemented!("Inline struct type is not defined as a struct"),
// Resolve and enforce struct fields
let member_value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
member.expression,
)?;
resolved_members.push(ConstrainedStructMember(member.variable, member_value))
}
Ok(ConstrainedValue::StructExpression(
variable,
resolved_members,
))
} else {
unimplemented!(
"Struct {} must be declared before it is used in an inline expression",
struct_name
)
Err(ExpressionError::UndefinedStruct(variable.to_string()))
}
}
@ -276,16 +379,18 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
function_scope: String,
struct_variable: Box<Expression<F>>,
struct_member: Variable<F>,
) -> ResolvedValue<F> {
match self.enforce_expression(cs, file_scope, function_scope, *struct_variable) {
ResolvedValue::StructExpression(_name, members) => {
) -> Result<ConstrainedValue<F>, ExpressionError> {
match self.enforce_expression(cs, file_scope, function_scope, *struct_variable)? {
ConstrainedValue::StructExpression(_name, members) => {
let matched_member = members.into_iter().find(|member| member.0 == struct_member);
match matched_member {
Some(member) => member.1,
None => unimplemented!("Cannot access struct member {}", struct_member.name),
Some(member) => Ok(member.1),
None => Err(ExpressionError::UndefinedStructField(
struct_member.to_string(),
)),
}
}
value => unimplemented!("Cannot access element of untyped struct {}", value),
value => Err(ExpressionError::InvalidStructAccess(value.to_string())),
}
}
@ -293,34 +398,26 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
function: Variable<F>,
arguments: Vec<Expression<F>>,
) -> ResolvedValue<F> {
) -> Result<ConstrainedValue<F>, ExpressionError> {
let function_name = new_variable_from_variable(file_scope.clone(), &function);
match self.get_mut_variable(&function_name) {
Some(value) => match value.clone() {
ResolvedValue::Function(function) => {
// this function call is inline so we unwrap the return value
match self.enforce_function(cs, file_scope, function, arguments) {
ResolvedValue::Return(return_values) => {
if return_values.len() == 0 {
ResolvedValue::Return(vec![])
} else if return_values.len() == 1 {
return_values[0].clone()
} else {
unimplemented!("function {} returns multiple values but is used in an expression that expects one", function_name)
}
}
value => unimplemented!(
"function {} has no return value {}",
function_name,
value
),
}
let function_call = match self.get(&function_name.to_string()) {
Some(ConstrainedValue::Function(function)) => function.clone(),
_ => return Err(ExpressionError::UndefinedFunction(function.to_string())),
};
match self.enforce_function(cs, file_scope, function_scope, function_call, arguments) {
Ok(ConstrainedValue::Return(return_values)) => {
if return_values.len() == 1 {
Ok(return_values[0].clone())
} else {
Ok(ConstrainedValue::Return(return_values))
}
value => unimplemented!("Cannot make function call to {}", value),
},
None => unimplemented!("Cannot call unknown function {}", function_name),
}
Ok(_) => Err(ExpressionError::FunctionDidNotReturn(function.to_string())),
Err(error) => Err(ExpressionError::from(Box::new(error))),
}
}
@ -330,7 +427,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
file_scope: String,
function_scope: String,
expression: Expression<F>,
) -> ResolvedValue<F> {
) -> Result<ConstrainedValue<F>, ExpressionError> {
match expression {
// Variables
Expression::Variable(unresolved_variable) => {
@ -338,94 +435,162 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
}
// Values
Expression::Integer(integer) => Self::get_integer_constant(integer),
Expression::FieldElement(fe) => ResolvedValue::FieldElement(fe),
Expression::Boolean(bool) => Self::get_boolean_constant(bool),
Expression::Integer(integer) => Ok(Self::get_integer_constant(integer)),
Expression::FieldElement(fe) => Ok(Self::get_field_element_constant(fe)),
Expression::Boolean(bool) => Ok(Self::get_boolean_constant(bool)),
// Binary operations
Expression::Add(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_add_expression(cs, resolved_left, resolved_right)
}
Expression::Sub(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_sub_expression(cs, resolved_left, resolved_right)
}
Expression::Mul(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_mul_expression(cs, resolved_left, resolved_right)
}
Expression::Div(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_div_expression(cs, resolved_left, resolved_right)
}
Expression::Pow(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_pow_expression(cs, resolved_left, resolved_right)
}
// Boolean operations
Expression::Not(expression) => Self::enforce_not(self.enforce_expression(
Expression::Not(expression) => Ok(Self::evaluate_not(self.enforce_expression(
cs,
file_scope,
function_scope,
*expression,
)),
)?)?),
Expression::Or(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_or(cs, resolved_left, resolved_right)
Ok(self.enforce_or(cs, resolved_left, resolved_right)?)
}
Expression::And(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_and(cs, resolved_left, resolved_right)
Ok(self.enforce_and(cs, resolved_left, resolved_right)?)
}
Expression::Eq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
self.enforce_eq_expression(cs, resolved_left, resolved_right)
Ok(self.evaluate_eq_expression(resolved_left, resolved_right)?)
}
Expression::Geq(left, right) => {
unimplemented!("expression {} >= {} unimplemented", left, right)
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
Ok(self.evaluate_geq_expression(resolved_left, resolved_right)?)
}
Expression::Gt(left, right) => {
unimplemented!("expression {} > {} unimplemented", left, right)
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
Ok(self.evaluate_gt_expression(resolved_left, resolved_right)?)
}
Expression::Leq(left, right) => {
unimplemented!("expression {} <= {} unimplemented", left, right)
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
Ok(self.evaluate_leq_expression(resolved_left, resolved_right)?)
}
Expression::Lt(left, right) => {
unimplemented!("expression {} < {} unimplemented", left, right)
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*right,
)?;
Ok(self.evaluate_lt_expression(resolved_left, resolved_right)?)
}
// Conditionals
@ -435,9 +600,9 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
file_scope.clone(),
function_scope.clone(),
*first,
) {
ResolvedValue::Boolean(resolved) => resolved,
_ => unimplemented!("if else conditional must resolve to boolean"),
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(ExpressionError::IfElseConditional(value.to_string())),
};
if resolved_first.eq(&Boolean::Constant(true)) {
@ -469,9 +634,13 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
),
// Functions
Expression::FunctionCall(function, arguments) => {
self.enforce_function_call_expression(cs, file_scope, function, arguments)
} // _ => unimplemented!(),
Expression::FunctionCall(function, arguments) => self.enforce_function_call_expression(
cs,
file_scope,
function_scope,
function,
arguments,
),
}
}
}

View File

@ -1,71 +1,70 @@
//! Methods to enforce constraints on field elements in a resolved aleo program.
//! Methods to enforce constraints on field elements in a resolved Leo program.
use crate::constraints::{ResolvedProgram, ResolvedValue};
use crate::{new_variable_from_variable, Parameter, Variable};
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::FieldElementError,
types::{FieldElement, InputModel, InputValue, Integer, Variable},
};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean};
// use std::ops::{Add, Div, Mul, Neg, Sub};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::{ConstraintSystem, LinearCombination, Variable as R1CSVariable},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn field_element_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument: F = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<F>()
.unwrap_or_default();
parameter_model: InputModel<F>,
parameter_value: Option<InputValue<F>>,
) -> Result<Variable<F>, FieldElementError> {
// Check that the parameter value is the correct type
let field_option = match parameter_value {
Some(parameter) => {
if let InputValue::Field(fe) = parameter {
Some(fe)
} else {
return Err(FieldElementError::InvalidField(parameter.to_string()));
}
}
None => None,
};
// Check visibility of parameter
let name = parameter.variable.name.clone();
if parameter.private {
cs.alloc(|| name, || Ok(argument.clone())).unwrap();
let name = parameter_model.variable.name.clone();
let field_value = if parameter_model.private {
cs.alloc(
|| name,
|| field_option.ok_or(SynthesisError::AssignmentMissing),
)?
} else {
cs.alloc_input(|| name, || Ok(argument.clone())).unwrap();
}
cs.alloc_input(
|| name,
|| field_option.ok_or(SynthesisError::AssignmentMissing),
)?
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
// Store parameter as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ResolvedValue::FieldElement(argument),
ConstrainedValue::FieldElement(FieldElement::Allocated(field_option, field_value)),
);
parameter_variable
Ok(parameter_variable)
}
pub(crate) fn field_element_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
) -> Variable<F> {
_parameter_model: InputModel<F>,
_parameter_value: Option<InputValue<F>>,
) -> Result<Variable<F>, FieldElementError> {
unimplemented!("Cannot enforce field element array as parameter")
// // Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<F>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
@ -86,29 +85,420 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
// parameter_variable
}
pub(crate) fn enforce_field_eq(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.eq(&fe2)))
pub(crate) fn get_field_element_constant(fe: FieldElement<F>) -> ConstrainedValue<F> {
ConstrainedValue::FieldElement(fe)
}
pub(crate) fn enforce_field_add(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.add(&fe2))
// pub(crate) fn field_eq(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.eq(&fe2)))
// }
//
// pub(crate) fn field_geq(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.ge(&fe2)))
// }
//
// pub(crate) fn field_gt(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.gt(&fe2)))
// }
//
// pub(crate) fn field_leq(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.le(&fe2)))
// }
//
// pub(crate) fn field_lt(fe1: F, fe2: F) -> ResolvedValue<F> {
// ResolvedValue::Boolean(Boolean::Constant(fe1.lt(&fe2)))
// }
pub(crate) fn enforce_field_eq(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) {
let mut lc = LinearCombination::zero();
match (fe_1, fe_2) {
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
// lc = lc + (fe_1_constant * 1) - (fe_2_constant * 1)
// lc = lc + fe_1 - fe_2
lc = lc + (fe_1_constant, CS::one()) - (fe_2_constant, CS::one());
}
// else, return an allocated result
(
FieldElement::Allocated(_fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
// lc = lc + fe_1 - (fe_2_constant * 1)
// lc = lc + fe_1 - fe_2
lc = lc + fe_1_variable - (fe_2_constant, CS::one())
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(_fe_2_value, fe_2_variable),
) => {
// lc = lc + (fe_1_constant * 1) - fe_2
// lc = lc + fe_1 - fe_2
lc = lc + (fe_1_constant, CS::one()) - fe_2_variable
}
(
FieldElement::Allocated(_fe_1_value, fe_1_variable),
FieldElement::Allocated(_fe_2_value, fe_2_variable),
) => {
// lc = lc + fe_1 - fe_2
lc = lc + fe_1_variable - fe_2_variable
}
}
// enforce that the linear combination is zero
cs.enforce(|| "field equality", |lc| lc, |lc| lc, |_| lc);
}
pub(crate) fn enforce_field_sub(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.sub(&fe2))
pub(crate) fn enforce_field_add(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> Result<ConstrainedValue<F>, FieldElementError> {
Ok(match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ConstrainedValue::FieldElement(FieldElement::Constant(
fe_1_constant.add(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let sum_value: Option<F> = fe_1_value.map(|v| v.add(&fe_2_constant));
let sum_variable: R1CSVariable = cs.alloc(
|| "field addition",
|| sum_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "sum = 1 * (fe_1 + fe2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable + (fe_2_constant, CS::one()),
|lc| lc + sum_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sum_value: Option<F> = fe_2_value.map(|v| fe_1_constant.add(&v));
let sum_variable: R1CSVariable = cs.alloc(
|| "field addition",
|| sum_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "sum = 1 * (fe_1 + fe_2)",
|lc| lc + CS::one(),
|lc| lc + (fe_1_constant, CS::one()) + fe_2_variable,
|lc| lc + sum_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sum_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.add(&fe_2_value)),
(_, _) => None,
};
let sum_variable: R1CSVariable = cs.alloc(
|| "field addition",
|| sum_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "sum = 1 * (fe_1 + fe_2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable + fe_2_variable,
|lc| lc + sum_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable))
}
})
}
pub(crate) fn enforce_field_mul(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.mul(&fe2))
pub(crate) fn enforce_field_sub(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> Result<ConstrainedValue<F>, FieldElementError> {
Ok(match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ConstrainedValue::FieldElement(FieldElement::Constant(
fe_1_constant.sub(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let sub_value: Option<F> = fe_1_value.map(|v| v.sub(&fe_2_constant));
let sub_variable: R1CSVariable = cs.alloc(
|| "field subtraction",
|| sub_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "sub = 1 * (fe_1 - fe2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable - (fe_2_constant, CS::one()),
|lc| lc + sub_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sub_value: Option<F> = fe_2_value.map(|v| fe_1_constant.sub(&v));
let sub_variable: R1CSVariable = cs.alloc(
|| "field subtraction",
|| sub_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "sub = 1 * (fe_1 - fe_2)",
|lc| lc + CS::one(),
|lc| lc + (fe_1_constant, CS::one()) - fe_2_variable,
|lc| lc + sub_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let sub_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.sub(&fe_2_value)),
(_, _) => None,
};
let sub_variable: R1CSVariable = cs.alloc(
|| "field subtraction",
|| sub_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "sub = 1 * (fe_1 - fe_2)",
|lc| lc + CS::one(),
|lc| lc + fe_1_variable - fe_2_variable,
|lc| lc + sub_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable))
}
})
}
pub(crate) fn enforce_field_div(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.div(&fe2))
pub(crate) fn enforce_field_mul(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> Result<ConstrainedValue<F>, FieldElementError> {
Ok(match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ConstrainedValue::FieldElement(FieldElement::Constant(
fe_1_constant.mul(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let mul_value: Option<F> = fe_1_value.map(|v| v.mul(&fe_2_constant));
let mul_variable: R1CSVariable = cs.alloc(
|| "field multiplication",
|| mul_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "mul = fe_1 * fe_2",
|lc| lc + fe_1_variable,
|lc| lc + (fe_2_constant, CS::one()),
|lc| lc + mul_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let mul_value: Option<F> = fe_2_value.map(|v| fe_1_constant.mul(&v));
let mul_variable: R1CSVariable = cs.alloc(
|| "field multiplication",
|| mul_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "mul = fe_1 * fe_2",
|lc| lc + (fe_1_constant, CS::one()),
|lc| lc + fe_2_variable,
|lc| lc + mul_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, fe_2_variable),
) => {
let mul_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.mul(&fe_2_value)),
(_, _) => None,
};
let mul_variable: R1CSVariable = cs.alloc(
|| "field multiplication",
|| mul_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "mul = fe_1 * fe_2",
|lc| lc + fe_1_variable,
|lc| lc + fe_2_variable,
|lc| lc + mul_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable))
}
})
}
pub(crate) fn enforce_field_pow(&mut self, _fe1: F, _fe2: F) -> ResolvedValue<F> {
unimplemented!("field element exponentiation not supported")
pub(crate) fn enforce_field_div(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
fe_2: FieldElement<F>,
) -> Result<ConstrainedValue<F>, FieldElementError> {
Ok(match (fe_1, fe_2) {
// if both constants, then return a constant result
(FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => {
ConstrainedValue::FieldElement(FieldElement::Constant(
fe_1_constant.div(&fe_2_constant),
))
}
// else, return an allocated result
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Constant(fe_2_constant),
) => {
let div_value: Option<F> = fe_1_value.map(|v| v.div(&fe_2_constant));
let div_variable: R1CSVariable = cs.alloc(
|| "field division",
|| div_value.ok_or(SynthesisError::AssignmentMissing),
)?;
let fe_2_inverse_value = fe_2_constant.inverse().unwrap();
// ResolvedValue::FieldElement(fe1.pow(&fe2))
cs.enforce(
|| "div = fe_1 * fe_2^-1",
|lc| lc + fe_1_variable,
|lc| lc + (fe_2_inverse_value, CS::one()),
|lc| lc + div_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(div_value, div_variable))
}
(
FieldElement::Constant(fe_1_constant),
FieldElement::Allocated(fe_2_value, _fe_2_variable),
) => {
let div_value: Option<F> = fe_2_value.map(|v| fe_1_constant.div(&v));
let div_variable: R1CSVariable = cs.alloc(
|| "field division",
|| div_value.ok_or(SynthesisError::AssignmentMissing),
)?;
let fe_2_inverse_value = fe_2_value.map(|v| v.inverse().unwrap());
let fe_2_inverse_variable = cs.alloc(
|| "field inverse",
|| fe_2_inverse_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "div = fe_1 * fe_2^-1",
|lc| lc + (fe_1_constant, CS::one()),
|lc| lc + fe_2_inverse_variable,
|lc| lc + div_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(div_value, div_variable))
}
(
FieldElement::Allocated(fe_1_value, fe_1_variable),
FieldElement::Allocated(fe_2_value, _fe_2_variable),
) => {
let div_value: Option<F> = match (fe_1_value, fe_2_value) {
(Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.div(&fe_2_value)),
(_, _) => None,
};
let div_variable: R1CSVariable = cs.alloc(
|| "field division",
|| div_value.ok_or(SynthesisError::AssignmentMissing),
)?;
let fe_2_inverse_value = fe_2_value.map(|v| v.inverse().unwrap());
let fe_2_inverse_variable = cs.alloc(
|| "field inverse",
|| fe_2_inverse_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "div = fe_1 * fe_2^-1",
|lc| lc + fe_1_variable,
|lc| lc + fe_2_inverse_variable,
|lc| lc + div_variable.clone(),
);
ConstrainedValue::FieldElement(FieldElement::Allocated(div_value, div_variable))
}
})
}
pub(crate) fn enforce_field_pow(
&mut self,
cs: &mut CS,
fe_1: FieldElement<F>,
num: Integer,
) -> Result<ConstrainedValue<F>, FieldElementError> {
Ok(match fe_1 {
// if both constants, then return a constant result
FieldElement::Constant(fe_1_constant) => ConstrainedValue::FieldElement(
FieldElement::Constant(fe_1_constant.pow(&[num.to_usize() as u64])),
),
// else, return an allocated result
FieldElement::Allocated(fe_1_value, _fe_1_variable) => {
let pow_value: Option<F> = fe_1_value.map(|v| v.pow(&[num.to_usize() as u64]));
let pow_variable: R1CSVariable = cs.alloc(
|| "field exponentiation",
|| pow_value.ok_or(SynthesisError::AssignmentMissing),
)?;
// cs.enforce( //todo: find a linear combination for this
// || "pow = 1 + fe_1^num",
// |lc| lc + fe_1_variable,
// |lc| lc + (fe_2_inverse_value, CS::one()),
// |lc| lc + pow_variable.clone());
ConstrainedValue::FieldElement(FieldElement::Allocated(pow_value, pow_variable))
}
})
}
}

View File

@ -0,0 +1,260 @@
//! Methods to enforce functions with arguments in
//! a resolved Leo program.
use crate::{
constraints::{
new_scope, new_scope_from_variable, new_variable_from_variables, ConstrainedProgram,
ConstrainedValue,
},
errors::{FunctionError, ImportError},
types::{Expression, Function, InputValue, Program, Type},
};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
fn check_inputs_length(expected: usize, actual: usize) -> Result<(), FunctionError> {
// Make sure we are given the correct number of arguments
if expected != actual {
Err(FunctionError::InputsLength(expected, actual))
} else {
Ok(())
}
}
fn enforce_input(
&mut self,
cs: &mut CS,
scope: String,
caller_scope: String,
function_name: String,
input: Expression<F>,
) -> Result<ConstrainedValue<F>, FunctionError> {
match input {
Expression::Variable(variable) => Ok(self.enforce_variable(caller_scope, variable)?),
expression => Ok(self.enforce_expression(cs, scope, function_name, expression)?),
}
}
pub(crate) fn enforce_function(
&mut self,
cs: &mut CS,
scope: String,
caller_scope: String,
function: Function<F>,
inputs: Vec<Expression<F>>,
) -> Result<ConstrainedValue<F>, FunctionError> {
let function_name = new_scope(scope.clone(), function.get_name());
// Make sure we are given the correct number of arguments
Self::check_inputs_length(function.inputs.len(), inputs.len())?;
// Store argument values as new variables in resolved program
for (input_model, input_expression) in
function.inputs.clone().iter().zip(inputs.into_iter())
{
// Check that argument is correct type
match input_model._type.clone() {
Type::IntegerType(integer_type) => {
match self.enforce_input(
cs,
scope.clone(),
caller_scope.clone(),
function_name.clone(),
input_expression,
)? {
ConstrainedValue::Integer(number) => {
number.expect_type(&integer_type)?;
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&input_model.variable,
);
self.store(variable_name, ConstrainedValue::Integer(number));
}
argument => {
return Err(FunctionError::InvalidInput(
integer_type.to_string(),
argument.to_string(),
))
}
}
}
Type::FieldElement => {
match self.enforce_input(
cs,
scope.clone(),
caller_scope.clone(),
function_name.clone(),
input_expression,
)? {
ConstrainedValue::FieldElement(fe) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&input_model.variable,
);
self.store(variable_name, ConstrainedValue::FieldElement(fe));
}
argument => {
return Err(FunctionError::InvalidInput(
Type::<F>::FieldElement.to_string(),
argument.to_string(),
))
}
}
}
Type::Boolean => {
match self.enforce_input(
cs,
scope.clone(),
caller_scope.clone(),
function_name.clone(),
input_expression,
)? {
ConstrainedValue::Boolean(bool) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function_name.clone(),
&input_model.variable,
);
self.store(variable_name, ConstrainedValue::Boolean(bool));
}
argument => {
return Err(FunctionError::InvalidInput(
Type::<F>::Boolean.to_string(),
argument.to_string(),
))
}
}
}
ty => return Err(FunctionError::UndefinedInput(ty.to_string())),
}
}
// Evaluate function statements
let mut return_values = ConstrainedValue::Return(vec![]);
for statement in function.statements.iter() {
if let Some(returned) = self.enforce_statement(
cs,
scope.clone(),
function_name.clone(),
statement.clone(),
function.returns.clone(),
)? {
return_values = returned;
break;
}
}
Ok(return_values)
}
pub(crate) fn enforce_main_function(
&mut self,
cs: &mut CS,
scope: String,
function: Function<F>,
inputs: Vec<Option<InputValue<F>>>,
) -> Result<ConstrainedValue<F>, FunctionError> {
let function_name = new_scope(scope.clone(), function.get_name());
// Make sure we are given the correct number of arguments
Self::check_inputs_length(function.inputs.len(), inputs.len())?;
// Iterate over main function inputs and allocate new passed-by variable values
let mut input_variables = vec![];
for (input_model, input_value) in
function.inputs.clone().into_iter().zip(inputs.into_iter())
{
// append each variable to inputs vector
let variable = match input_model._type {
Type::IntegerType(ref _integer_type) => self.integer_from_parameter(
cs,
function_name.clone(),
input_model,
input_value,
)?,
Type::FieldElement => self.field_element_from_parameter(
cs,
function_name.clone(),
input_model,
input_value,
)?,
Type::Boolean => {
self.bool_from_parameter(cs, function_name.clone(), input_model, input_value)?
}
Type::Array(ref ty, _length) => match *ty.clone() {
Type::IntegerType(_type) => self.integer_array_from_parameter(
cs,
function_name.clone(),
input_model,
input_value,
)?,
Type::FieldElement => self.field_element_array_from_parameter(
cs,
function_name.clone(),
input_model,
input_value,
)?,
Type::Boolean => self.boolean_array_from_parameter(
cs,
function_name.clone(),
input_model,
input_value,
)?,
_type => return Err(FunctionError::UndefinedInput(_type.to_string())),
},
_type => return Err(FunctionError::UndefinedInput(_type.to_string())),
};
input_variables.push(Expression::Variable(variable));
}
self.enforce_function(cs, scope, function_name, function, input_variables)
}
pub(crate) fn resolve_definitions(
&mut self,
cs: &mut CS,
program: Program<F>,
) -> Result<(), ImportError> {
let program_name = program.name.clone();
// evaluate and store all imports
program
.imports
.into_iter()
.map(|import| self.enforce_import(cs, program_name.name.clone(), import))
.collect::<Result<Vec<_>, ImportError>>()?;
// evaluate and store all struct definitions
program
.structs
.into_iter()
.for_each(|(variable, struct_def)| {
let resolved_struct_name =
new_variable_from_variables(&program_name.clone(), &variable);
self.store_variable(
resolved_struct_name,
ConstrainedValue::StructDefinition(struct_def),
);
});
// evaluate and store all function definitions
program
.functions
.into_iter()
.for_each(|(function_name, function)| {
let resolved_function_name = new_scope(program_name.name.clone(), function_name.0);
self.store(resolved_function_name, ConstrainedValue::Function(function));
});
Ok(())
}
}

View File

@ -0,0 +1,113 @@
use crate::{
ast,
constraints::{new_variable_from_variables, ConstrainedProgram, ConstrainedValue},
errors::constraints::ImportError,
types::Program,
Import,
};
use from_pest::FromPest;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
use std::fs;
use std::path::Path;
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub fn enforce_import(
&mut self,
cs: &mut CS,
scope: String,
import: Import<F>,
) -> Result<(), ImportError> {
// Resolve program file path
let unparsed_file = fs::read_to_string(Path::new(&import.path_string_full()))
.map_err(|_| ImportError::FileReadError(import.path_string_full()))?;
let mut file = ast::parse(&unparsed_file).map_err(|_| ImportError::FileParsingError)?;
// generate ast from file
let syntax_tree =
ast::File::from_pest(&mut file).map_err(|_| ImportError::SyntaxTreeError)?;
// generate aleo program from file
let mut program = Program::from(syntax_tree, import.path_string.clone());
// Use same namespace as calling function for imported symbols
program = program.name(scope);
// * -> import all imports, structs, functions in the current scope
if import.is_star() {
// recursively evaluate program statements
self.resolve_definitions(cs, program).unwrap();
Ok(())
} else {
let program_name = program.name.clone();
// match each import symbol to a symbol in the imported file
import.symbols.into_iter().for_each(|symbol| {
// see if the imported symbol is a struct
let matched_struct = program
.structs
.clone()
.into_iter()
.find(|(struct_name, _struct_def)| symbol.symbol == *struct_name);
match matched_struct {
Some((_struct_name, struct_def)) => {
// take the alias if it is present
let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
let resolved_struct_name =
new_variable_from_variables(&program_name.clone(), &resolved_name);
// store imported struct under resolved name
self.store_variable(
resolved_struct_name,
ConstrainedValue::StructDefinition(struct_def),
);
}
None => {
// see if the imported symbol is a function
let matched_function = program.functions.clone().into_iter().find(
|(function_name, _function)| symbol.symbol.name == *function_name.0,
);
match matched_function {
Some((_function_name, function)) => {
// take the alias if it is present
let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
let resolved_function_name = new_variable_from_variables(
&program_name.clone(),
&resolved_name,
);
// store imported function under resolved name
self.store_variable(
resolved_function_name,
ConstrainedValue::Function(function),
)
}
None => unimplemented!(
"cannot find imported symbol {} in imported file {}",
symbol,
program_name.clone()
),
}
}
}
});
// evaluate all import statements in imported file
program
.imports
.into_iter()
.map(|nested_import| {
self.enforce_import(cs, program_name.name.clone(), nested_import)
})
.collect::<Result<Vec<_>, ImportError>>()?;
Ok(())
}
}
}

View File

@ -1,162 +0,0 @@
//! Methods to enforce constraints on integers in a resolved aleo program.
use crate::constraints::{ResolvedProgram, ResolvedValue};
use crate::{new_variable_from_variable, Integer, Parameter, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, eq::ConditionalEqGadget, uint32::UInt32},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn u32_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<u32>()
.expect(&format!(
"expected main function parameter {} at index {}",
parameter, index
));
// Check visibility of parameter
let name = parameter.variable.name.clone();
let number = if parameter.private {
UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
} else {
UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// store each argument as variable in resolved program
self.store_variable(parameter_variable.clone(), ResolvedValue::U32(number));
parameter_variable
}
pub(crate) fn u32_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
) -> Variable<F> {
unimplemented!("Cannot enforce integer array as parameter")
// Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<u32>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// let number = if parameter.private {
// UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// } else {
// UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// };
//
// array_value.push(number);
// }
//
//
// let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
//
// // store array as variable in resolved program
// self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
//
// parameter_variable
}
pub(crate) fn get_integer_constant(integer: Integer) -> ResolvedValue<F> {
match integer {
Integer::U32(u32_value) => ResolvedValue::U32(UInt32::constant(u32_value)),
}
}
pub(crate) fn enforce_u32_eq(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
left.conditional_enforce_equal(
cs.ns(|| format!("enforce field equal")),
&right,
&Boolean::Constant(true),
)
.unwrap();
ResolvedValue::Boolean(Boolean::Constant(true))
}
pub(crate) fn enforce_u32_add(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
UInt32::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_sub(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_mul(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_div(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_pow(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)
.unwrap(),
)
}
}

View File

@ -0,0 +1,272 @@
//! Methods to enforce constraints on integers in a resolved Leo program.
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, InputValue, Integer, Type, Variable},
IntegerType,
};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn get_integer_constant(integer: Integer) -> ConstrainedValue<F> {
ConstrainedValue::Integer(integer)
}
pub(crate) fn evaluate_integer_eq(
left: Integer,
right: Integer,
) -> Result<ConstrainedValue<F>, IntegerError> {
Ok(ConstrainedValue::Boolean(Boolean::Constant(
match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => left_u8.eq(&right_u8),
(Integer::U16(left_u16), Integer::U16(right_u16)) => left_u16.eq(&right_u16),
(Integer::U32(left_u32), Integer::U32(right_u32)) => left_u32.eq(&right_u32),
(Integer::U64(left_u64), Integer::U64(right_u64)) => left_u64.eq(&right_u64),
(Integer::U128(left_u128), Integer::U128(right_u128)) => left_u128.eq(&right_u128),
(left, right) => {
return Err(IntegerError::CannotEvaluate(format!(
"{} == {}",
left, right
)))
}
},
)))
}
pub(crate) fn integer_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
parameter_model: InputModel<F>,
parameter_value: Option<InputValue<F>>,
) -> Result<Variable<F>, IntegerError> {
let integer_type = match &parameter_model._type {
Type::IntegerType(integer_type) => integer_type,
_type => return Err(IntegerError::InvalidType(_type.to_string())),
};
// Check that the parameter value is the correct type
let integer_option = match parameter_value {
Some(parameter) => {
if let InputValue::Integer(integer) = parameter {
Some(integer)
} else {
return Err(IntegerError::InvalidInteger(
parameter_model._type.to_string(),
parameter.to_string(),
));
}
}
None => None,
};
match integer_type {
IntegerType::U8 => self.u8_from_parameter(cs, scope, parameter_model, integer_option),
IntegerType::U16 => self.u16_from_parameter(cs, scope, parameter_model, integer_option),
IntegerType::U32 => self.u32_from_parameter(cs, scope, parameter_model, integer_option),
IntegerType::U64 => self.u64_from_parameter(cs, scope, parameter_model, integer_option),
IntegerType::U128 => {
self.u128_from_parameter(cs, scope, parameter_model, integer_option)
}
}
}
pub(crate) fn integer_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_parameter_model: InputModel<F>,
_parameter_value: Option<InputValue<F>>,
) -> Result<Variable<F>, IntegerError> {
unimplemented!("Cannot enforce integer array as parameter")
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// let number = if parameter.private {
// UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// } else {
// UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// };
//
// array_value.push(number);
// }
//
//
// let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
//
// // store array as variable in resolved program
// self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
//
// parameter_variable
}
pub(crate) fn enforce_integer_eq(
cs: &mut CS,
left: Integer,
right: Integer,
) -> Result<(), IntegerError> {
match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => {
Self::enforce_u8_eq(cs, left_u8, right_u8)
}
(Integer::U16(left_u16), Integer::U16(right_u16)) => {
Self::enforce_u16_eq(cs, left_u16, right_u16)
}
(Integer::U32(left_u32), Integer::U32(right_u32)) => {
Self::enforce_u32_eq(cs, left_u32, right_u32)
}
(Integer::U64(left_u64), Integer::U64(right_u64)) => {
Self::enforce_u64_eq(cs, left_u64, right_u64)
}
(Integer::U128(left_u128), Integer::U128(right_u128)) => {
Self::enforce_u128_eq(cs, left_u128, right_u128)
}
(left, right) => {
return Err(IntegerError::CannotEnforce(format!(
"{} == {}",
left, right
)))
}
}
}
pub(crate) fn enforce_integer_add(
cs: &mut CS,
left: Integer,
right: Integer,
) -> Result<ConstrainedValue<F>, IntegerError> {
Ok(ConstrainedValue::Integer(match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => {
Integer::U8(Self::enforce_u8_add(cs, left_u8, right_u8)?)
}
(Integer::U16(left_u16), Integer::U16(right_u16)) => {
Integer::U16(Self::enforce_u16_add(cs, left_u16, right_u16)?)
}
(Integer::U32(left_u32), Integer::U32(right_u32)) => {
Integer::U32(Self::enforce_u32_add(cs, left_u32, right_u32)?)
}
(Integer::U64(left_u64), Integer::U64(right_u64)) => {
Integer::U64(Self::enforce_u64_add(cs, left_u64, right_u64)?)
}
(Integer::U128(left_u128), Integer::U128(right_u128)) => {
Integer::U128(Self::enforce_u128_add(cs, left_u128, right_u128)?)
}
(left, right) => {
return Err(IntegerError::CannotEnforce(format!("{} + {}", left, right)))
}
}))
}
pub(crate) fn enforce_integer_sub(
cs: &mut CS,
left: Integer,
right: Integer,
) -> Result<ConstrainedValue<F>, IntegerError> {
Ok(ConstrainedValue::Integer(match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => {
Integer::U8(Self::enforce_u8_sub(cs, left_u8, right_u8)?)
}
(Integer::U16(left_u16), Integer::U16(right_u16)) => {
Integer::U16(Self::enforce_u16_sub(cs, left_u16, right_u16)?)
}
(Integer::U32(left_u32), Integer::U32(right_u32)) => {
Integer::U32(Self::enforce_u32_sub(cs, left_u32, right_u32)?)
}
(Integer::U64(left_u64), Integer::U64(right_u64)) => {
Integer::U64(Self::enforce_u64_sub(cs, left_u64, right_u64)?)
}
(Integer::U128(left_u128), Integer::U128(right_u128)) => {
Integer::U128(Self::enforce_u128_sub(cs, left_u128, right_u128)?)
}
(left, right) => {
return Err(IntegerError::CannotEnforce(format!("{} - {}", left, right)))
}
}))
}
pub(crate) fn enforce_integer_mul(
cs: &mut CS,
left: Integer,
right: Integer,
) -> Result<ConstrainedValue<F>, IntegerError> {
Ok(ConstrainedValue::Integer(match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => {
Integer::U8(Self::enforce_u8_mul(cs, left_u8, right_u8)?)
}
(Integer::U16(left_u16), Integer::U16(right_u16)) => {
Integer::U16(Self::enforce_u16_mul(cs, left_u16, right_u16)?)
}
(Integer::U32(left_u32), Integer::U32(right_u32)) => {
Integer::U32(Self::enforce_u32_mul(cs, left_u32, right_u32)?)
}
(Integer::U64(left_u64), Integer::U64(right_u64)) => {
Integer::U64(Self::enforce_u64_mul(cs, left_u64, right_u64)?)
}
(Integer::U128(left_u128), Integer::U128(right_u128)) => {
Integer::U128(Self::enforce_u128_mul(cs, left_u128, right_u128)?)
}
(left, right) => {
return Err(IntegerError::CannotEnforce(format!("{} * {}", left, right)))
}
}))
}
pub(crate) fn enforce_integer_div(
cs: &mut CS,
left: Integer,
right: Integer,
) -> Result<ConstrainedValue<F>, IntegerError> {
Ok(ConstrainedValue::Integer(match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => {
Integer::U8(Self::enforce_u8_div(cs, left_u8, right_u8)?)
}
(Integer::U16(left_u16), Integer::U16(right_u16)) => {
Integer::U16(Self::enforce_u16_div(cs, left_u16, right_u16)?)
}
(Integer::U32(left_u32), Integer::U32(right_u32)) => {
Integer::U32(Self::enforce_u32_div(cs, left_u32, right_u32)?)
}
(Integer::U64(left_u64), Integer::U64(right_u64)) => {
Integer::U64(Self::enforce_u64_div(cs, left_u64, right_u64)?)
}
(Integer::U128(left_u128), Integer::U128(right_u128)) => {
Integer::U128(Self::enforce_u128_div(cs, left_u128, right_u128)?)
}
(left, right) => {
return Err(IntegerError::CannotEnforce(format!("{} / {}", left, right)))
}
}))
}
pub(crate) fn enforce_integer_pow(
cs: &mut CS,
left: Integer,
right: Integer,
) -> Result<ConstrainedValue<F>, IntegerError> {
Ok(ConstrainedValue::Integer(match (left, right) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => {
Integer::U8(Self::enforce_u8_pow(cs, left_u8, right_u8)?)
}
(Integer::U16(left_u16), Integer::U16(right_u16)) => {
Integer::U16(Self::enforce_u16_pow(cs, left_u16, right_u16)?)
}
(Integer::U32(left_u32), Integer::U32(right_u32)) => {
Integer::U32(Self::enforce_u32_pow(cs, left_u32, right_u32)?)
}
(Integer::U64(left_u64), Integer::U64(right_u64)) => {
Integer::U64(Self::enforce_u64_pow(cs, left_u64, right_u64)?)
}
(Integer::U128(left_u128), Integer::U128(right_u128)) => {
Integer::U128(Self::enforce_u128_pow(cs, left_u128, right_u128)?)
}
(left, right) => {
return Err(IntegerError::CannotEnforce(format!(
"{} ** {}",
left, right
)))
}
}))
}
}

View File

@ -0,0 +1,19 @@
//! Module containing methods to enforce constraints on integers in a Leo program
pub mod integer;
pub use integer::*;
pub mod uint8;
pub use uint8::*;
pub mod uint16;
pub use uint16::*;
pub mod uint32;
pub use uint32::*;
pub mod uint64;
pub use uint64::*;
pub mod uint128;
pub use uint128::*;

View File

@ -0,0 +1,149 @@
//! Methods to enforce constraints on uint128s in a resolved Leo program.
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer, Variable},
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, eq::EqGadget, uint128::UInt128},
},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn u128_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
parameter_model: InputModel<F>,
integer_option: Option<usize>,
) -> Result<Variable<F>, IntegerError> {
// Type cast to u128 in rust.
// If this fails should we return our own error?
let u128_option = integer_option.map(|integer| integer as u128);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer = if parameter_model.private {
UInt128::alloc(cs.ns(|| name), || {
u128_option.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
UInt128::alloc_input(cs.ns(|| name), || {
u128_option.ok_or(SynthesisError::AssignmentMissing)
})?
};
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ConstrainedValue::Integer(Integer::U128(integer)),
);
Ok(parameter_variable)
}
// pub(crate) fn u128_array_from_parameter(
// &mut self,
// _cs: &mut CS,
// _scope: String,
// _parameter_model: ParameterModel<F>,
// _parameter_value: Option<ParameterValue<F>>,
// ) -> Result<Variable<F>, IntegerError> {
// unimplemented!("Cannot enforce integer array as parameter")
// // // Check visibility of parameter
// // let mut array_value = vec![];
// // let name = parameter.variable.name.clone();
// // for argument in argument_array {
// // let number = if parameter.private {
// // UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// // } else {
// // UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// // };
// //
// // array_value.push(number);
// // }
// //
// //
// // let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// //
// // // store array as variable in resolved program
// // self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
// //
// // parameter_variable
// }
pub(crate) fn enforce_u128_eq(
cs: &mut CS,
left: UInt128,
right: UInt128,
) -> Result<(), IntegerError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce u128 equal")), &right)?)
}
pub(crate) fn enforce_u128_add(
cs: &mut CS,
left: UInt128,
right: UInt128,
) -> Result<UInt128, IntegerError> {
Ok(UInt128::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)?)
}
pub(crate) fn enforce_u128_sub(
cs: &mut CS,
left: UInt128,
right: UInt128,
) -> Result<UInt128, IntegerError> {
Ok(left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u128_mul(
cs: &mut CS,
left: UInt128,
right: UInt128,
) -> Result<UInt128, IntegerError> {
Ok(left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u128_div(
cs: &mut CS,
left: UInt128,
right: UInt128,
) -> Result<UInt128, IntegerError> {
Ok(left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u128_pow(
cs: &mut CS,
left: UInt128,
right: UInt128,
) -> Result<UInt128, IntegerError> {
Ok(left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)?)
}
}

View File

@ -0,0 +1,149 @@
//! Methods to enforce constraints on uint16s in a resolved Leo program.
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer, Variable},
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, eq::EqGadget, uint16::UInt16},
},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn u16_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
parameter_model: InputModel<F>,
integer_option: Option<usize>,
) -> Result<Variable<F>, IntegerError> {
// Type cast to u16 in rust.
// If this fails should we return our own error?
let u16_option = integer_option.map(|integer| integer as u16);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer = if parameter_model.private {
UInt16::alloc(cs.ns(|| name), || {
u16_option.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
UInt16::alloc_input(cs.ns(|| name), || {
u16_option.ok_or(SynthesisError::AssignmentMissing)
})?
};
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ConstrainedValue::Integer(Integer::U16(integer)),
);
Ok(parameter_variable)
}
// pub(crate) fn u16_array_from_parameter(
// &mut self,
// _cs: &mut CS,
// _scope: String,
// _parameter_model: ParameterModel<F>,
// _parameter_value: Option<ParameterValue<F>>,
// ) -> Result<Variable<F>, IntegerError> {
// unimplemented!("Cannot enforce integer array as parameter")
// // // Check visibility of parameter
// // let mut array_value = vec![];
// // let name = parameter.variable.name.clone();
// // for argument in argument_array {
// // let number = if parameter.private {
// // UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// // } else {
// // UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// // };
// //
// // array_value.push(number);
// // }
// //
// //
// // let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// //
// // // store array as variable in resolved program
// // self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
// //
// // parameter_variable
// }
pub(crate) fn enforce_u16_eq(
cs: &mut CS,
left: UInt16,
right: UInt16,
) -> Result<(), IntegerError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce u16 equal")), &right)?)
}
pub(crate) fn enforce_u16_add(
cs: &mut CS,
left: UInt16,
right: UInt16,
) -> Result<UInt16, IntegerError> {
Ok(UInt16::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)?)
}
pub(crate) fn enforce_u16_sub(
cs: &mut CS,
left: UInt16,
right: UInt16,
) -> Result<UInt16, IntegerError> {
Ok(left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u16_mul(
cs: &mut CS,
left: UInt16,
right: UInt16,
) -> Result<UInt16, IntegerError> {
Ok(left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u16_div(
cs: &mut CS,
left: UInt16,
right: UInt16,
) -> Result<UInt16, IntegerError> {
Ok(left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u16_pow(
cs: &mut CS,
left: UInt16,
right: UInt16,
) -> Result<UInt16, IntegerError> {
Ok(left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)?)
}
}

View File

@ -0,0 +1,149 @@
//! Methods to enforce constraints on uint32s in a resolved Leo program.
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer, Variable},
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, eq::EqGadget, uint32::UInt32},
},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn u32_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
parameter_model: InputModel<F>,
integer_option: Option<usize>,
) -> Result<Variable<F>, IntegerError> {
// Type cast to u32 in rust.
// If this fails should we return our own error?
let u32_option = integer_option.map(|integer| integer as u32);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer = if parameter_model.private {
UInt32::alloc(cs.ns(|| name), || {
u32_option.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
UInt32::alloc_input(cs.ns(|| name), || {
u32_option.ok_or(SynthesisError::AssignmentMissing)
})?
};
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ConstrainedValue::Integer(Integer::U32(integer)),
);
Ok(parameter_variable)
}
// pub(crate) fn u32_array_from_parameter(
// &mut self,
// _cs: &mut CS,
// _scope: String,
// _parameter_model: ParameterModel<F>,
// _parameter_value: Option<ParameterValue<F>>,
// ) -> Result<Variable<F>, IntegerError> {
// unimplemented!("Cannot enforce integer array as parameter")
// // // Check visibility of parameter
// // let mut array_value = vec![];
// // let name = parameter.variable.name.clone();
// // for argument in argument_array {
// // let number = if parameter.private {
// // UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// // } else {
// // UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// // };
// //
// // array_value.push(number);
// // }
// //
// //
// // let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// //
// // // store array as variable in resolved program
// // self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
// //
// // parameter_variable
// }
pub(crate) fn enforce_u32_eq(
cs: &mut CS,
left: UInt32,
right: UInt32,
) -> Result<(), IntegerError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce u32 equal")), &right)?)
}
pub(crate) fn enforce_u32_add(
cs: &mut CS,
left: UInt32,
right: UInt32,
) -> Result<UInt32, IntegerError> {
Ok(UInt32::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)?)
}
pub(crate) fn enforce_u32_sub(
cs: &mut CS,
left: UInt32,
right: UInt32,
) -> Result<UInt32, IntegerError> {
Ok(left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u32_mul(
cs: &mut CS,
left: UInt32,
right: UInt32,
) -> Result<UInt32, IntegerError> {
Ok(left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u32_div(
cs: &mut CS,
left: UInt32,
right: UInt32,
) -> Result<UInt32, IntegerError> {
Ok(left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u32_pow(
cs: &mut CS,
left: UInt32,
right: UInt32,
) -> Result<UInt32, IntegerError> {
Ok(left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)?)
}
}

View File

@ -0,0 +1,149 @@
//! Methods to enforce constraints on uint64s in a resolved Leo program.
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer, Variable},
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, eq::EqGadget, uint64::UInt64},
},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn u64_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
parameter_model: InputModel<F>,
integer_option: Option<usize>,
) -> Result<Variable<F>, IntegerError> {
// Type cast to u64 in rust.
// If this fails should we return our own error?
let u64_option = integer_option.map(|integer| integer as u64);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer = if parameter_model.private {
UInt64::alloc(cs.ns(|| name), || {
u64_option.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
UInt64::alloc_input(cs.ns(|| name), || {
u64_option.ok_or(SynthesisError::AssignmentMissing)
})?
};
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ConstrainedValue::Integer(Integer::U64(integer)),
);
Ok(parameter_variable)
}
// pub(crate) fn u64_array_from_parameter(
// &mut self,
// _cs: &mut CS,
// _scope: String,
// _parameter_model: ParameterModel<F>,
// _parameter_value: Option<ParameterValue<F>>,
// ) -> Result<Variable<F>, IntegerError> {
// unimplemented!("Cannot enforce integer array as parameter")
// // // Check visibility of parameter
// // let mut array_value = vec![];
// // let name = parameter.variable.name.clone();
// // for argument in argument_array {
// // let number = if parameter.private {
// // UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// // } else {
// // UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// // };
// //
// // array_value.push(number);
// // }
// //
// //
// // let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// //
// // // store array as variable in resolved program
// // self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
// //
// // parameter_variable
// }
pub(crate) fn enforce_u64_eq(
cs: &mut CS,
left: UInt64,
right: UInt64,
) -> Result<(), IntegerError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce u64 equal")), &right)?)
}
pub(crate) fn enforce_u64_add(
cs: &mut CS,
left: UInt64,
right: UInt64,
) -> Result<UInt64, IntegerError> {
Ok(UInt64::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)?)
}
pub(crate) fn enforce_u64_sub(
cs: &mut CS,
left: UInt64,
right: UInt64,
) -> Result<UInt64, IntegerError> {
Ok(left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u64_mul(
cs: &mut CS,
left: UInt64,
right: UInt64,
) -> Result<UInt64, IntegerError> {
Ok(left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u64_div(
cs: &mut CS,
left: UInt64,
right: UInt64,
) -> Result<UInt64, IntegerError> {
Ok(left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u64_pow(
cs: &mut CS,
left: UInt64,
right: UInt64,
) -> Result<UInt64, IntegerError> {
Ok(left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)?)
}
}

View File

@ -0,0 +1,149 @@
//! Methods to enforce constraints on uint8s in a resolved Leo program.
use crate::{
constraints::{new_variable_from_variable, ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer, Variable},
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, eq::EqGadget, uint8::UInt8},
},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub(crate) fn u8_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
parameter_model: InputModel<F>,
integer_option: Option<usize>,
) -> Result<Variable<F>, IntegerError> {
// Type cast to u8 in rust.
// If this fails should we return our own error?
let u8_option = integer_option.map(|integer| integer as u8);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer_value = if parameter_model.private {
UInt8::alloc(cs.ns(|| name), || {
u8_option.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
UInt8::alloc_input(cs.ns(|| name), || {
u8_option.ok_or(SynthesisError::AssignmentMissing)
})?
};
let parameter_variable = new_variable_from_variable(scope, &parameter_model.variable);
// store each argument as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ConstrainedValue::Integer(Integer::U8(integer_value)),
);
Ok(parameter_variable)
}
// pub(crate) fn u8_array_from_parameter(
// &mut self,
// _cs: &mut CS,
// _scope: String,
// _parameter_model: ParameterModel<F>,
// _parameter_value: Option<ParameterValue<F>>,
// ) -> Result<Variable<F>, IntegerError> {
// unimplemented!("Cannot enforce integer array as parameter")
// // // Check visibility of parameter
// // let mut array_value = vec![];
// // let name = parameter.variable.name.clone();
// // for argument in argument_array {
// // let number = if parameter.private {
// // UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// // } else {
// // UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// // };
// //
// // array_value.push(number);
// // }
// //
// //
// // let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// //
// // // store array as variable in resolved program
// // self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
// //
// // parameter_variable
// }
pub(crate) fn enforce_u8_eq(
cs: &mut CS,
left: UInt8,
right: UInt8,
) -> Result<(), IntegerError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce u8 equal")), &right)?)
}
pub(crate) fn enforce_u8_add(
cs: &mut CS,
left: UInt8,
right: UInt8,
) -> Result<UInt8, IntegerError> {
Ok(UInt8::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)?)
}
pub(crate) fn enforce_u8_sub(
cs: &mut CS,
left: UInt8,
right: UInt8,
) -> Result<UInt8, IntegerError> {
Ok(left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u8_mul(
cs: &mut CS,
left: UInt8,
right: UInt8,
) -> Result<UInt8, IntegerError> {
Ok(left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u8_div(
cs: &mut CS,
left: UInt8,
right: UInt8,
) -> Result<UInt8, IntegerError> {
Ok(left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)?)
}
pub(crate) fn enforce_u8_pow(
cs: &mut CS,
left: UInt8,
right: UInt8,
) -> Result<UInt8, IntegerError> {
Ok(left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)?)
}
}

View File

@ -1,25 +1,64 @@
//! Module containing methods to enforce constraints in an aleo program
//! Module containing methods to enforce constraints in an Leo program
pub mod boolean;
pub use boolean::*;
pub mod constraints;
pub use constraints::*;
pub mod function;
pub use function::*;
pub mod expression;
pub use expression::*;
pub mod import;
pub use import::*;
pub mod integer;
pub use integer::*;
pub mod field_element;
pub use field_element::*;
pub mod resolved_program;
pub use resolved_program::*;
pub mod program;
pub use program::*;
pub mod resolved_value;
pub use resolved_value::*;
pub mod value;
pub use value::*;
pub mod statement;
pub use statement::*;
use crate::{
errors::CompilerError,
types::{InputValue, Program},
};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
pub fn generate_constraints<F: Field + PrimeField, CS: ConstraintSystem<F>>(
cs: &mut CS,
program: Program<F>,
parameters: Vec<Option<InputValue<F>>>,
) -> Result<ConstrainedValue<F>, CompilerError> {
let mut resolved_program = ConstrainedProgram::new();
let program_name = program.get_name();
let main_function_name = new_scope(program_name.clone(), "main".into());
resolved_program.resolve_definitions(cs, program)?;
let main = resolved_program
.get(&main_function_name)
.ok_or_else(|| CompilerError::NoMain)?;
match main.clone() {
ConstrainedValue::Function(function) => {
let result =
resolved_program.enforce_main_function(cs, program_name, function, parameters)?;
log::debug!("{}", result);
Ok(result)
}
_ => Err(CompilerError::NoMainFunction),
}
}

View File

@ -1,15 +1,15 @@
//! An in memory store to keep track of defined names when constraining an aleo program.
//! An in memory store to keep track of defined names when constraining a Leo program.
use crate::constraints::ResolvedValue;
use crate::types::Variable;
use crate::{constraints::ConstrainedValue, types::Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use std::collections::HashMap;
use std::marker::PhantomData;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
use std::{collections::HashMap, marker::PhantomData};
pub struct ResolvedProgram<F: Field + PrimeField, CS: ConstraintSystem<F>> {
pub resolved_names: HashMap<String, ResolvedValue<F>>,
pub struct ConstrainedProgram<F: Field + PrimeField, CS: ConstraintSystem<F>> {
pub resolved_names: HashMap<String, ConstrainedValue<F>>,
pub _cs: PhantomData<CS>,
}
@ -44,7 +44,7 @@ pub fn new_variable_from_variables<F: Field + PrimeField>(
}
}
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
pub fn new() -> Self {
Self {
resolved_names: HashMap::new(),
@ -52,11 +52,11 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
}
}
pub(crate) fn store(&mut self, name: String, value: ResolvedValue<F>) {
pub(crate) fn store(&mut self, name: String, value: ConstrainedValue<F>) {
self.resolved_names.insert(name, value);
}
pub(crate) fn store_variable(&mut self, variable: Variable<F>, value: ResolvedValue<F>) {
pub(crate) fn store_variable(&mut self, variable: Variable<F>, value: ConstrainedValue<F>) {
self.store(variable.name, value);
}
@ -68,18 +68,18 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
self.contains_name(&variable.name)
}
pub(crate) fn get(&self, name: &String) -> Option<&ResolvedValue<F>> {
pub(crate) fn get(&self, name: &String) -> Option<&ConstrainedValue<F>> {
self.resolved_names.get(name)
}
pub(crate) fn get_mut(&mut self, name: &String) -> Option<&mut ResolvedValue<F>> {
pub(crate) fn get_mut(&mut self, name: &String) -> Option<&mut ConstrainedValue<F>> {
self.resolved_names.get_mut(name)
}
pub(crate) fn get_mut_variable(
&mut self,
variable: &Variable<F>,
) -> Option<&mut ResolvedValue<F>> {
) -> Option<&mut ConstrainedValue<F>> {
self.get_mut(&variable.name)
}
}

View File

@ -1,99 +0,0 @@
//! The in memory stored value for a defined name in a resolved aleo program.
use crate::types::{Function, Struct, Type, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{utilities::boolean::Boolean, utilities::uint32::UInt32};
use std::fmt;
#[derive(Clone)]
pub enum ResolvedValue<F: Field + PrimeField> {
U32(UInt32),
FieldElement(F),
Boolean(Boolean),
Array(Vec<ResolvedValue<F>>),
StructDefinition(Struct<F>),
StructExpression(Variable<F>, Vec<ResolvedStructMember<F>>),
Function(Function<F>),
Return(Vec<ResolvedValue<F>>), // add Null for function returns
}
#[derive(Clone)]
pub struct ResolvedStructMember<F: Field + PrimeField>(pub Variable<F>, pub ResolvedValue<F>);
impl<F: Field + PrimeField> ResolvedValue<F> {
pub(crate) fn match_type(&self, ty: &Type<F>) -> bool {
match (self, ty) {
(ResolvedValue::U32(ref _a), Type::U32) => true,
(ResolvedValue::FieldElement(ref _a), Type::FieldElement) => true,
(ResolvedValue::Boolean(ref _a), Type::Boolean) => true,
(ResolvedValue::Array(ref arr), Type::Array(ref ty, ref len)) => {
// check array lengths are equal
let mut res = arr.len() == *len;
// check each value in array matches
for value in arr {
res &= value.match_type(ty)
}
res
}
(
ResolvedValue::StructExpression(ref actual_name, ref _members),
Type::Struct(ref expected_name),
) => actual_name == expected_name,
(ResolvedValue::Return(ref values), ty) => {
let mut res = true;
for value in values {
res &= value.match_type(ty)
}
res
}
(_, _) => false,
}
}
}
impl<F: Field + PrimeField> fmt::Display for ResolvedValue<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ResolvedValue::U32(ref value) => write!(f, "{}", value.value.unwrap()),
ResolvedValue::FieldElement(ref value) => write!(f, "{}", value),
ResolvedValue::Boolean(ref value) => write!(f, "{}", value.get_value().unwrap()),
ResolvedValue::Array(ref array) => {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
write!(f, "{}", e)?;
if i < array.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
ResolvedValue::StructExpression(ref variable, ref members) => {
write!(f, "{} {{", variable)?;
for (i, member) in members.iter().enumerate() {
write!(f, "{}: {}", member.0, member.1)?;
if i < members.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
ResolvedValue::Return(ref values) => {
write!(f, "Return values : [")?;
for (i, value) in values.iter().enumerate() {
write!(f, "{}", value)?;
if i < values.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
ResolvedValue::StructDefinition(ref _definition) => {
unimplemented!("cannot return struct definition in program")
}
ResolvedValue::Function(ref _function) => {
unimplemented!("cannot return function definition in program")
} // _ => unimplemented!("display not impl for value"),
}
}
}

View File

@ -1,12 +1,20 @@
//! Methods to enforce constraints on statements in a resolved aleo program.
//! Methods to enforce constraints on statements in a resolved Leo program.
use crate::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
use crate::{Assignee, Expression, Integer, RangeOrExpression, Statement, Type, Variable};
use crate::{
constraints::{new_scope_from_variable, ConstrainedProgram, ConstrainedValue},
errors::StatementError,
types::{
Assignee, ConditionalNestedOrEnd, ConditionalStatement, Expression, Integer,
RangeOrExpression, Statement, Type, Variable,
},
};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::uint32::UInt32};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean, utilities::uint32::UInt32},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ConstrainedProgram<F, CS> {
fn resolve_assignee(&mut self, scope: String, assignee: Assignee<F>) -> String {
match assignee {
Assignee::Variable(name) => new_scope_from_variable(scope, &name),
@ -23,8 +31,8 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
file_scope: String,
function_scope: String,
assignee: Assignee<F>,
return_value: &mut ResolvedValue<F>,
) {
return_value: &mut ConstrainedValue<F>,
) -> Result<(), StatementError> {
match assignee {
Assignee::Variable(name) => {
// Store the variable in the current scope
@ -44,22 +52,19 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
file_scope.clone(),
function_scope.clone(),
index,
);
)?;
// Modify the single value of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match value {
ResolvedValue::Array(old) => {
ConstrainedValue::Array(old) => {
old[index] = return_value.to_owned();
}
_ => {
unimplemented!("Cannot assign single index to array of values ")
}
_ => return Err(StatementError::ArrayAssignIndex),
},
None => unimplemented!(
"tried to assign to unknown array {}",
expected_array_name
),
None => {
return Err(StatementError::UndefinedArray(expected_array_name))
}
}
}
RangeOrExpression::Range(from, to) => {
@ -75,18 +80,15 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
// Modify the range of values of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match (value, return_value) {
(ResolvedValue::Array(old), ResolvedValue::Array(new)) => {
(ConstrainedValue::Array(old), ConstrainedValue::Array(new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
}
_ => unimplemented!(
"Cannot assign a range of array values to single value"
),
_ => return Err(StatementError::ArrayAssignRange),
},
None => unimplemented!(
"tried to assign to unknown array {}",
expected_array_name
),
None => {
return Err(StatementError::UndefinedArray(expected_array_name))
}
}
}
}
@ -97,192 +99,228 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
self.resolve_assignee(function_scope.clone(), *struct_variable);
match self.get_mut(&expected_struct_name) {
Some(value) => match value {
ResolvedValue::StructExpression(_variable, members) => {
// Modify the struct member in place
let matched_member =
members.into_iter().find(|member| member.0 == struct_member);
match matched_member {
Some(mut member) => member.1 = return_value.to_owned(),
None => unimplemented!(
"struct member {} does not exist in {}",
struct_member,
expected_struct_name
),
Some(ConstrainedValue::StructExpression(_variable, members)) => {
// Modify the struct member in place
let matched_member =
members.into_iter().find(|member| member.0 == struct_member);
match matched_member {
Some(mut member) => member.1 = return_value.to_owned(),
None => {
return Err(StatementError::UndefinedStructField(
struct_member.to_string(),
))
}
}
_ => unimplemented!(
"tried to assign to unknown struct {}",
expected_struct_name
),
},
None => {
unimplemented!("tried to assign to unknown struct {}", expected_struct_name)
}
_ => return Err(StatementError::UndefinedStruct(expected_struct_name)),
}
}
}
Ok(())
}
pub(crate) fn enforce_assign_statement(
fn enforce_assign_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
assignee: Assignee<F>,
expression: Expression<F>,
) {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
);
) -> Result<(), StatementError> {
// Check that assignee exists
let name = self.resolve_assignee(function_scope.clone(), assignee.clone());
self.store_assignment(cs, file_scope, function_scope, assignee, result_value);
}
match self.get(&name) {
Some(_assignee) => {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?;
pub(crate) fn enforce_definition_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
ty: Type<F>,
assignee: Assignee<F>,
expression: Expression<F>,
) {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
);
if result_value.match_type(&ty) {
self.store_assignment(cs, file_scope, function_scope, assignee, result_value);
} else {
unimplemented!("incompatible types {} = {}", assignee, result_value)
self.store_assignment(cs, file_scope, function_scope, assignee, result_value)
}
None => Err(StatementError::UndefinedVariable(assignee.to_string())),
}
}
pub(crate) fn enforce_multiple_definition_statement(
fn enforce_definition_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
assignee: Assignee<F>,
ty: Option<Type<F>>,
expression: Expression<F>,
) -> Result<(), StatementError> {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?;
match ty {
// Explicit type
Some(ty) => {
result_value.expect_type(&ty)?;
self.store_assignment(cs, file_scope, function_scope, assignee, result_value)
}
// Implicit type
None => self.store_assignment(cs, file_scope, function_scope, assignee, result_value),
}
}
fn enforce_multiple_definition_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
assignees: Vec<Assignee<F>>,
function: Expression<F>,
) {
) -> Result<(), StatementError> {
// Expect return values from function
let return_values =
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), function)
{
ResolvedValue::Return(values) => values,
value => unimplemented!(
"multiple assignment only implemented for functions, got {}",
value
),
};
let return_values = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
function,
)? {
ConstrainedValue::Return(values) => values,
value => unimplemented!(
"multiple assignment only implemented for functions, got {}",
value
),
};
assignees
.into_iter()
.zip(return_values.into_iter())
.for_each(|(assignee, mut return_value)| {
.map(|(assignee, mut return_value)| {
self.store_assignment(
cs,
file_scope.clone(),
function_scope.clone(),
assignee,
&mut return_value,
);
});
)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(())
}
pub(crate) fn enforce_return_statement(
fn enforce_return_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
statements: Vec<Expression<F>>,
expressions: Vec<Expression<F>>,
return_types: Vec<Type<F>>,
) -> ResolvedValue<F> {
ResolvedValue::Return(
statements
.into_iter()
.zip(return_types.into_iter())
.map(|(expression, ty)| {
let result = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
);
if !result.match_type(&ty) {
unimplemented!("expected return type {}, got {}", ty, result)
} else {
result
}
})
.collect::<Vec<ResolvedValue<F>>>(),
)
) -> Result<ConstrainedValue<F>, StatementError> {
// Make sure we return the correct number of values
if return_types.len() != expressions.len() {
return Err(StatementError::InvalidNumberOfReturns(
return_types.len(),
expressions.len(),
));
}
let mut returns = vec![];
for (expression, ty) in expressions.into_iter().zip(return_types.into_iter()) {
let result = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?;
result.expect_type(&ty)?;
returns.push(result);
}
Ok(ConstrainedValue::Return(returns))
}
fn enforce_statement(
fn iterate_or_early_return(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
statement: Statement<F>,
statements: Vec<Statement<F>>,
return_types: Vec<Type<F>>,
) {
match statement {
Statement::Return(statements) => {
// TODO: add support for early termination
let _res = self.enforce_return_statement(
cs,
file_scope,
function_scope,
statements,
return_types,
);
}
Statement::Assign(variable, expression) => {
self.enforce_assign_statement(cs, file_scope, function_scope, variable, expression);
}
Statement::Definition(ty, assignee, expression) => {
self.enforce_definition_statement(
cs,
file_scope,
function_scope,
ty,
assignee,
expression,
);
}
Statement::MultipleDefinition(assignees, function) => {
self.enforce_multiple_definition_statement(
cs,
file_scope,
function_scope,
assignees,
function,
);
}
Statement::For(index, start, stop, statements) => {
self.enforce_for_statement(
cs,
file_scope,
function_scope,
index,
start,
stop,
statements,
);
) -> Result<Option<ConstrainedValue<F>>, StatementError> {
let mut res = None;
// Evaluate statements and possibly return early
for statement in statements.iter() {
if let Some(early_return) = self.enforce_statement(
cs,
file_scope.clone(),
function_scope.clone(),
statement.clone(),
return_types.clone(),
)? {
res = Some(early_return);
break;
}
}
Ok(res)
}
fn enforce_conditional_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
statement: ConditionalStatement<F>,
return_types: Vec<Type<F>>,
) -> Result<Option<ConstrainedValue<F>>, StatementError> {
let condition = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
statement.condition.clone(),
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(StatementError::IfElseConditional(value.to_string())),
};
// use gadget impl
if condition.eq(&Boolean::Constant(true)) {
self.iterate_or_early_return(
cs,
file_scope,
function_scope,
statement.statements,
return_types,
)
} else {
match statement.next {
Some(next) => match next {
ConditionalNestedOrEnd::Nested(nested) => self.enforce_conditional_statement(
cs,
file_scope,
function_scope,
*nested,
return_types,
),
ConditionalNestedOrEnd::End(statements) => self.iterate_or_early_return(
cs,
file_scope,
function_scope,
statements,
return_types,
),
},
None => Ok(None),
}
}
}
pub(crate) fn enforce_for_statement(
fn enforce_for_statement(
&mut self,
cs: &mut CS,
file_scope: String,
@ -291,23 +329,152 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
start: Integer,
stop: Integer,
statements: Vec<Statement<F>>,
) {
return_types: Vec<Type<F>>,
) -> Result<Option<ConstrainedValue<F>>, StatementError> {
let mut res = None;
for i in start.to_usize()..stop.to_usize() {
// Store index in current function scope.
// For loop scope is not implemented.
let index_name = new_scope_from_variable(function_scope.clone(), &index);
self.store(index_name, ResolvedValue::U32(UInt32::constant(i as u32)));
self.store(
index_name,
ConstrainedValue::Integer(Integer::U32(UInt32::constant(i as u32))),
);
// Evaluate statements (for loop statements should not have a return type)
statements.clone().into_iter().for_each(|statement| {
self.enforce_statement(
cs,
file_scope.clone(),
function_scope.clone(),
statement,
vec![],
)
});
// Evaluate statements and possibly return early
if let Some(early_return) = self.iterate_or_early_return(
cs,
file_scope.clone(),
function_scope.clone(),
statements.clone(),
return_types.clone(),
)? {
res = Some(early_return);
break;
}
}
Ok(res)
}
fn enforce_assert_eq_statement(
&mut self,
cs: &mut CS,
left: ConstrainedValue<F>,
right: ConstrainedValue<F>,
) -> Result<(), StatementError> {
Ok(match (left, right) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
self.enforce_boolean_eq(cs, bool_1, bool_2)?
}
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_eq(cs, num_1, num_2)?
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_eq(cs, fe_1, fe_2)
}
(val_1, val_2) => {
return Err(StatementError::AssertEq(
val_1.to_string(),
val_2.to_string(),
))
}
})
}
pub(crate) fn enforce_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
statement: Statement<F>,
return_types: Vec<Type<F>>,
) -> Result<Option<ConstrainedValue<F>>, StatementError> {
let mut res = None;
match statement {
Statement::Return(expressions) => {
res = Some(self.enforce_return_statement(
cs,
file_scope,
function_scope,
expressions,
return_types,
)?);
}
Statement::Definition(assignee, ty, expression) => {
self.enforce_definition_statement(
cs,
file_scope,
function_scope,
assignee,
ty,
expression,
)?;
}
Statement::Assign(variable, expression) => {
self.enforce_assign_statement(
cs,
file_scope,
function_scope,
variable,
expression,
)?;
}
Statement::MultipleAssign(assignees, function) => {
self.enforce_multiple_definition_statement(
cs,
file_scope,
function_scope,
assignees,
function,
)?;
}
Statement::Conditional(statement) => {
if let Some(early_return) = self.enforce_conditional_statement(
cs,
file_scope,
function_scope,
statement,
return_types,
)? {
res = Some(early_return)
}
}
Statement::For(index, start, stop, statements) => {
if let Some(early_return) = self.enforce_for_statement(
cs,
file_scope,
function_scope,
index,
start,
stop,
statements,
return_types,
)? {
res = Some(early_return)
}
}
Statement::AssertEq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), left)?;
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), right)?;
self.enforce_assert_eq_statement(cs, resolved_left, resolved_right)?;
}
Statement::Expression(expression) => {
match self.enforce_expression(cs, file_scope, function_scope, expression.clone())? {
ConstrainedValue::Return(values) => {
if !values.is_empty() {
return Err(StatementError::Unassigned(expression.to_string()));
}
}
_ => return Err(StatementError::Unassigned(expression.to_string())),
}
}
};
Ok(res)
}
}

View File

@ -0,0 +1,127 @@
//! The in memory stored value for a defined name in a resolved Leo program.
use crate::{
errors::ValueError,
types::{FieldElement, Function, Struct, Type, Variable},
Integer,
};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::utilities::boolean::Boolean,
};
use std::fmt;
#[derive(Clone, PartialEq, Eq)]
pub struct ConstrainedStructMember<F: Field + PrimeField>(pub Variable<F>, pub ConstrainedValue<F>);
#[derive(Clone, PartialEq, Eq)]
pub enum ConstrainedValue<F: Field + PrimeField> {
Integer(Integer),
FieldElement(FieldElement<F>),
Boolean(Boolean),
Array(Vec<ConstrainedValue<F>>),
StructDefinition(Struct<F>),
StructExpression(Variable<F>, Vec<ConstrainedStructMember<F>>),
Function(Function<F>),
Return(Vec<ConstrainedValue<F>>), // add Null for function returns
}
impl<F: Field + PrimeField> ConstrainedValue<F> {
pub(crate) fn expect_type(&self, _type: &Type<F>) -> Result<(), ValueError> {
match (self, _type) {
(ConstrainedValue::Integer(ref integer), Type::IntegerType(ref _type)) => {
integer.expect_type(_type)?;
}
(ConstrainedValue::FieldElement(ref _f), Type::FieldElement) => {}
(ConstrainedValue::Boolean(ref _b), Type::Boolean) => {}
(ConstrainedValue::Array(ref arr), Type::Array(ref ty, ref len)) => {
// check array lengths are equal
if arr.len() != *len {
return Err(ValueError::ArrayLength(format!(
"Expected array {:?} to be length {}",
arr, len
)));
}
// check each value in array matches
for value in arr {
value.expect_type(ty)?;
}
}
(
ConstrainedValue::StructExpression(ref actual_name, ref _members),
Type::Struct(ref expected_name),
) => {
if expected_name != actual_name {
return Err(ValueError::StructName(format!(
"Expected struct name {} got {}",
expected_name, actual_name
)));
}
}
(ConstrainedValue::Return(ref values), ty) => {
for value in values {
value.expect_type(ty)?;
}
}
(value, _type) => {
return Err(ValueError::TypeError(format!(
"expected type {}, got {}",
_type, value
)))
}
}
Ok(())
}
}
impl<F: Field + PrimeField> fmt::Display for ConstrainedValue<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConstrainedValue::Integer(ref value) => write!(f, "{}", value),
ConstrainedValue::FieldElement(ref value) => write!(f, "{}", value),
ConstrainedValue::Boolean(ref value) => write!(f, "{}", value.get_value().unwrap()),
ConstrainedValue::Array(ref array) => {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
write!(f, "{}", e)?;
if i < array.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
ConstrainedValue::StructExpression(ref variable, ref members) => {
write!(f, "{} {{", variable)?;
for (i, member) in members.iter().enumerate() {
write!(f, "{}: {}", member.0, member.1)?;
if i < members.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
ConstrainedValue::Return(ref values) => {
write!(f, "Program output: [")?;
for (i, value) in values.iter().enumerate() {
write!(f, "{}", value)?;
if i < values.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
ConstrainedValue::StructDefinition(ref _definition) => {
unimplemented!("cannot return struct definition in program")
}
ConstrainedValue::Function(ref function) => write!(f, "{}();", function.function_name),
}
}
}
impl<F: Field + PrimeField> fmt::Debug for ConstrainedValue<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}

View File

@ -0,0 +1,64 @@
use crate::errors::{FunctionError, ImportError, IntegerError};
use std::io;
use std::path::PathBuf;
#[derive(Debug, Error)]
pub enum CompilerError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("creating: {}", _0)]
Creating(io::Error),
#[error("{}", _0)]
ImportError(ImportError),
#[error("{}", _0)]
IntegerError(IntegerError),
#[error("{}", _0)]
FunctionError(FunctionError),
#[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[error("Syntax error. Cannot parse the file")]
FileParsingError,
#[error("Main function not found")]
NoMain,
#[error("Main must be a function")]
NoMainFunction,
#[error("Unable to construct abstract syntax tree")]
SyntaxTreeError,
#[error("writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for CompilerError {
fn from(error: std::io::Error) -> Self {
CompilerError::Crate("std::io", format!("{}", error))
}
}
impl From<ImportError> for CompilerError {
fn from(error: ImportError) -> Self {
CompilerError::ImportError(error)
}
}
impl From<IntegerError> for CompilerError {
fn from(error: IntegerError) -> Self {
CompilerError::IntegerError(error)
}
}
impl From<FunctionError> for CompilerError {
fn from(error: FunctionError) -> Self {
CompilerError::FunctionError(error)
}
}

View File

@ -0,0 +1,31 @@
use snarkos_errors::gadgets::SynthesisError;
#[derive(Debug, Error)]
pub enum BooleanError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("Expected boolean parameter, got {}", _0)]
InvalidBoolean(String),
#[error("Cannot evaluate {}", _0)]
CannotEvaluate(String),
#[error("Cannot enforce {}", _0)]
CannotEnforce(String),
#[error("{}", _0)]
SynthesisError(SynthesisError),
}
impl From<std::io::Error> for BooleanError {
fn from(error: std::io::Error) -> Self {
BooleanError::Crate("std::io", format!("{}", error))
}
}
impl From<SynthesisError> for BooleanError {
fn from(error: SynthesisError) -> Self {
BooleanError::SynthesisError(error)
}
}

View File

@ -0,0 +1,106 @@
use crate::errors::{BooleanError, FieldElementError, FunctionError, IntegerError};
#[derive(Debug, Error)]
pub enum ExpressionError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
// Variables
#[error("Variable \"{}\" not found", _0)]
UndefinedVariable(String),
// Types
#[error("{}", _0)]
IncompatibleTypes(String),
#[error("{}", _0)]
IntegerError(IntegerError),
#[error("{}", _0)]
FieldElementError(FieldElementError),
#[error("{}", _0)]
BooleanError(BooleanError),
#[error("Exponent must be an integer, got field {}", _0)]
InvalidExponent(String),
// Arrays
#[error(
"Array {} must be declared before it is used in an inline expression",
_0
)]
UndefinedArray(String),
#[error("Cannot access array {}", _0)]
InvalidArrayAccess(String),
#[error("Spread should contain an array, got {}", _0)]
InvalidSpread(String),
#[error("Index must resolve to an integer, got {}", _0)]
InvalidIndex(String),
// Structs
#[error(
"Struct {} must be declared before it is used in an inline expression",
_0
)]
UndefinedStruct(String),
#[error("Struct field {} does not exist", _0)]
UndefinedStructField(String),
#[error("Expected struct field {}, got {}", _0, _1)]
InvalidStructField(String, String),
#[error("Cannot access struct {}", _0)]
InvalidStructAccess(String),
// Functions
#[error(
"Function {} must be declared before it is used in an inline expression",
_0
)]
UndefinedFunction(String),
#[error("Cannot evaluate function call")]
FunctionError(Box<FunctionError>),
#[error("Inline function call to {} did not return", _0)]
FunctionDidNotReturn(String),
// Conditionals
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
IfElseConditional(String),
}
impl From<std::io::Error> for ExpressionError {
fn from(error: std::io::Error) -> Self {
ExpressionError::Crate("std::io", format!("{}", error))
}
}
impl From<IntegerError> for ExpressionError {
fn from(error: IntegerError) -> Self {
ExpressionError::IntegerError(error)
}
}
impl From<FieldElementError> for ExpressionError {
fn from(error: FieldElementError) -> Self {
ExpressionError::FieldElementError(error)
}
}
impl From<BooleanError> for ExpressionError {
fn from(error: BooleanError) -> Self {
ExpressionError::BooleanError(error)
}
}
impl From<Box<FunctionError>> for ExpressionError {
fn from(error: Box<FunctionError>) -> Self {
ExpressionError::FunctionError(error)
}
}

View File

@ -0,0 +1,25 @@
use snarkos_errors::gadgets::SynthesisError;
#[derive(Debug, Error)]
pub enum FieldElementError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("Expected field element parameter, got {}", _0)]
InvalidField(String),
#[error("{}", _0)]
SynthesisError(SynthesisError),
}
impl From<std::io::Error> for FieldElementError {
fn from(error: std::io::Error) -> Self {
FieldElementError::Crate("std::io", format!("{}", error))
}
}
impl From<SynthesisError> for FieldElementError {
fn from(error: SynthesisError) -> Self {
FieldElementError::SynthesisError(error)
}
}

View File

@ -0,0 +1,69 @@
use crate::errors::{
BooleanError, ExpressionError, FieldElementError, IntegerError, StatementError,
};
#[derive(Debug, Error)]
pub enum FunctionError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("Function expected {} inputs, got {}", _0, _1)]
InputsLength(usize, usize),
#[error("Function input type not defined {}", _0)]
UndefinedInput(String),
#[error("Function expected input type {}, got {}", _0, _1)]
InvalidInput(String, String),
#[error("{}", _0)]
IntegerError(IntegerError),
#[error("{}", _0)]
FieldElementError(FieldElementError),
#[error("{}", _0)]
BooleanError(BooleanError),
#[error("{}", _0)]
ExpressionError(ExpressionError),
#[error("{}", _0)]
StatementError(StatementError),
}
impl From<std::io::Error> for FunctionError {
fn from(error: std::io::Error) -> Self {
FunctionError::Crate("std::io", format!("{}", error))
}
}
impl From<IntegerError> for FunctionError {
fn from(error: IntegerError) -> Self {
FunctionError::IntegerError(error)
}
}
impl From<FieldElementError> for FunctionError {
fn from(error: FieldElementError) -> Self {
FunctionError::FieldElementError(error)
}
}
impl From<BooleanError> for FunctionError {
fn from(error: BooleanError) -> Self {
FunctionError::BooleanError(error)
}
}
impl From<ExpressionError> for FunctionError {
fn from(error: ExpressionError) -> Self {
FunctionError::ExpressionError(error)
}
}
impl From<StatementError> for FunctionError {
fn from(error: StatementError) -> Self {
FunctionError::StatementError(error)
}
}

View File

@ -0,0 +1,20 @@
#[derive(Debug, Error)]
pub enum ImportError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("Cannot read from the provided file path - {}", _0)]
FileReadError(String),
#[error("Syntax error. Cannot parse the file")]
FileParsingError,
#[error("Unable to construct abstract syntax tree")]
SyntaxTreeError,
}
impl From<std::io::Error> for ImportError {
fn from(error: std::io::Error) -> Self {
ImportError::Crate("std::io", format!("{}", error))
}
}

View File

@ -0,0 +1,34 @@
use snarkos_errors::gadgets::SynthesisError;
#[derive(Debug, Error)]
pub enum IntegerError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("expected integer parameter type, got {}", _0)]
InvalidType(String),
#[error("Expected integer {} parameter, got {}", _0, _1)]
InvalidInteger(String, String),
#[error("Cannot evaluate {}", _0)]
CannotEvaluate(String),
#[error("Cannot enforce {}", _0)]
CannotEnforce(String),
#[error("{}", _0)]
SynthesisError(SynthesisError),
}
impl From<std::io::Error> for IntegerError {
fn from(error: std::io::Error) -> Self {
IntegerError::Crate("std::io", format!("{}", error))
}
}
impl From<SynthesisError> for IntegerError {
fn from(error: SynthesisError) -> Self {
IntegerError::SynthesisError(error)
}
}

View File

@ -0,0 +1,25 @@
//! Module containing errors returned when enforcing constraints in an Leo program
pub mod boolean;
pub use boolean::*;
pub mod function;
pub use function::*;
pub mod expression;
pub use expression::*;
pub mod import;
pub use import::*;
pub mod integer;
pub use integer::*;
pub mod field_element;
pub use field_element::*;
pub mod value;
pub use value::*;
pub mod statement;
pub use statement::*;

View File

@ -0,0 +1,88 @@
use crate::errors::{BooleanError, ExpressionError, FieldElementError, IntegerError, ValueError};
#[derive(Debug, Error)]
pub enum StatementError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("Attempted to assign to unknown variable {}", _0)]
UndefinedVariable(String),
#[error("{}", _0)]
ExpressionError(ExpressionError),
#[error("{}", _0)]
IntegerError(IntegerError),
#[error("{}", _0)]
FieldElementError(FieldElementError),
#[error("{}", _0)]
BooleanError(BooleanError),
#[error("{}", _0)]
ValueError(ValueError),
#[error("Cannot assign single index to array of values")]
ArrayAssignIndex,
#[error("Cannot assign range of array values to single value")]
ArrayAssignRange,
#[error("Cannot assign to unknown array {}", _0)]
UndefinedArray(String),
#[error("Attempted to assign to unknown struct {}", _0)]
UndefinedStruct(String),
#[error("Attempted to assign to unknown struct field {}", _0)]
UndefinedStructField(String),
#[error("Function return statement expected {} return values, got {}", _0, _1)]
InvalidNumberOfReturns(usize, usize),
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
IfElseConditional(String),
#[error("Cannot assert equality between {} == {}", _0, _1)]
AssertEq(String, String),
#[error("Expected assignment of return values for expression {}", _0)]
Unassigned(String),
}
impl From<std::io::Error> for StatementError {
fn from(error: std::io::Error) -> Self {
StatementError::Crate("std::io", format!("{}", error))
}
}
impl From<ExpressionError> for StatementError {
fn from(error: ExpressionError) -> Self {
StatementError::ExpressionError(error)
}
}
impl From<IntegerError> for StatementError {
fn from(error: IntegerError) -> Self {
StatementError::IntegerError(error)
}
}
impl From<FieldElementError> for StatementError {
fn from(error: FieldElementError) -> Self {
StatementError::FieldElementError(error)
}
}
impl From<BooleanError> for StatementError {
fn from(error: BooleanError) -> Self {
StatementError::BooleanError(error)
}
}
impl From<ValueError> for StatementError {
fn from(error: ValueError) -> Self {
StatementError::ValueError(error)
}
}

View File

@ -0,0 +1,34 @@
use crate::errors::IntegerError;
#[derive(Debug, Error)]
pub enum ValueError {
/// Unexpected array length
#[error("{}", _0)]
ArrayLength(String),
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("{}", _0)]
IntegerError(IntegerError),
/// Unexpected struct name
#[error("{}", _0)]
StructName(String),
/// Unexpected type
#[error("{}", _0)]
TypeError(String),
}
impl From<std::io::Error> for ValueError {
fn from(error: std::io::Error) -> Self {
ValueError::Crate("std::io", format!("{}", error))
}
}
impl From<IntegerError> for ValueError {
fn from(error: IntegerError) -> Self {
ValueError::IntegerError(error)
}
}

View File

@ -0,0 +1,5 @@
pub mod compiler;
pub use self::compiler::*;
pub mod constraints;
pub use self::constraints::*;

View File

@ -1,11 +1,9 @@
//! The Import type for a Leo program.
use crate::Variable;
use snarkos_models::curves::{Field, PrimeField};
use std::fmt;
use std::path::Path;
type ImportPath<'ast> = &'ast Path;
// pub(crate) type Variable<'ast = &'ast str;
#[derive(Clone)]
pub struct ImportSymbol<F: Field + PrimeField> {
@ -14,28 +12,43 @@ pub struct ImportSymbol<F: Field + PrimeField> {
}
#[derive(Clone)]
pub struct Import<'ast, F: Field + PrimeField> {
pub(crate) source: ImportPath<'ast>,
pub(crate) symbols: Vec<ImportSymbol<F>>,
pub struct Import<F: Field + PrimeField> {
pub path_string: String,
pub symbols: Vec<ImportSymbol<F>>,
}
impl<'ast, F: Field + PrimeField> Import<'ast, F> {
pub fn new(source: ImportPath<'ast>, symbols: Vec<ImportSymbol<F>>) -> Import<'ast, F> {
Import { source, symbols }
impl<F: Field + PrimeField> Import<F> {
pub fn new(source: String, symbols: Vec<ImportSymbol<F>>) -> Import<F> {
Import {
path_string: source,
symbols,
}
}
pub fn get_source(&self) -> &Path {
&self.source
}
pub fn get_file(&self) -> String {
let path = self.get_source().to_str().unwrap();
format!("{}.leo", path)
pub fn path_string_full(&self) -> String {
format!("{}.leo", self.path_string)
}
// from "./import" import *;
pub fn is_star(&self) -> bool {
self.symbols.is_empty()
}
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.path_string)?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
}
}
impl<F: Field + PrimeField> fmt::Display for ImportSymbol<F> {
@ -48,38 +61,14 @@ impl<F: Field + PrimeField> fmt::Display for ImportSymbol<F> {
}
}
impl<'ast, F: Field + PrimeField> fmt::Display for Import<'ast, F> {
impl<'ast, F: Field + PrimeField> fmt::Display for Import<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.source.display())?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
self.format(f)
}
}
impl<'ast, F: Field + PrimeField> fmt::Debug for Import<'ast, F> {
impl<'ast, F: Field + PrimeField> fmt::Debug for Import<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.source.display())?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
self.format(f)
}
}

View File

@ -0,0 +1,72 @@
/// Visibility
visibility_public = { "public" }
visibility_private = { "private" }
visibility = { visibility_public | visibility_private }
/// Types
ty_u32 = {"u32"}
ty_field = {"fe"}
ty_bool = {"bool"}
ty_basic = { ty_u32 | ty_field | ty_bool }
ty_struct = { variable }
ty_array = {ty_basic ~ ("[" ~ value ~ "]")+ }
ty = { ty_array | ty_basic | ty_struct }
/// Values
value_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
value_u32 = { value_number ~ ty_u32? }
value_field = { value_number ~ ty_field }
value_boolean = { "true" | "false" }
value = { value_field | value_boolean | value_u32 }
/// Variables
protected_name = { visibility | "return" }
variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
/// Arrays
inline_array_inner = _{(expression_term ~ ("," ~ NEWLINE* ~ expression_term)*)?}
expression_array_inline = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"}
expression_array_initializer = { "[" ~ expression_term ~ ";" ~ value ~ "]" }
/// Structs
inline_struct_member = { variable ~ ":" ~ expression_term }
inline_struct_member_list = _{(inline_struct_member ~ ("," ~ NEWLINE* ~ inline_struct_member)*)? ~ ","? }
expression_inline_struct = { variable ~ "{" ~ NEWLINE* ~ inline_struct_member_list ~ NEWLINE* ~ "}" }
/// Expressions
expression_primitive = { value | variable }
expression_term = {
expression_inline_struct
| expression_primitive
| expression_array_inline
| expression_array_initializer
}
/// Functions
parameter = { variable ~ ":" ~ visibility? ~ ty }
function_name = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
/// Section
header = { "[" ~ function_name ~ "]" }
assignment = { parameter ~ "=" ~ expression_term }
section = { header ~ NEWLINE+ ~ assignment* ~ NEWLINE* }
/// Utilities
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) }
WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* }
/// Program File
file = { SOI ~ NEWLINE* ~ section* ~ NEWLINE* ~ EOI }

View File

@ -1,4 +1,4 @@
/// Visibility
/// Visibili_type
visibility_public = { "public" }
visibility_private = { "private" }
@ -9,11 +9,9 @@ visibility = { visibility_public | visibility_private }
operation_pre_not = { "!" }
expression_not = { operation_pre_not ~ expression_term }
// operation_post_increment = { "++" }
// expression_increment = { operation_post_increment ~ expression_term }
// expression_increment = { expression+ ~ "++" }
//
// operation_post_decrement = { "--" }
// expression_decrement = { operation_post_decrement ~ expression_term }
// expression_decrement = { expression ~ "--" }
/// Binary Operations
@ -34,50 +32,72 @@ operation_mul = { "*" }
operation_div = { "/" }
operation_pow = { "**" }
operation_binary = _ {
operation_and | operation_or |
operation_compare = _{
operation_eq | operation_neq |
operation_geq | operation_gt | operation_leq | operation_lt |
operation_geq | operation_gt |
operation_leq | operation_lt
}
operation_binary = _{
operation_compare | operation_and | operation_or |
operation_add | operation_sub | operation_pow | operation_mul | operation_div
}
// operation_add_assign = { "+=" }
// operation_sub_assign = { "-=" }
// operation_mul_assign = { "*=" }
// operation_div_assign = { "/=" }
assign = { "=" }
operation_add_assign = { "+=" }
operation_sub_assign = { "-=" }
operation_mul_assign = { "*=" }
operation_div_assign = { "/=" }
operation_pow_assign = { "**=" }
/// Types
operation_assign = {
assign | operation_add_assign | operation_sub_assign |
operation_mul_assign | operation_div_assign | operation_pow_assign
}
ty_u32 = {"u32"}
ty_field = {"fe"}
ty_bool = {"bool"}
ty_basic = { ty_u32 | ty_field | ty_bool }
ty_struct = { variable }
ty_basic_or_struct = {ty_basic | ty_struct }
ty_array = {ty_basic ~ ("[" ~ value ~ "]")+ }
ty = {ty_array | ty_basic | ty_struct}
type_list = _{(ty ~ ("," ~ ty)*)?}
/// types
type_u8 = {"u8"}
type_u16 = {"u16"}
type_u32 = {"u32"}
type_u64 = {"u64"}
type_u128 = {"u128"}
type_integer = {
type_u8
| type_u16
| type_u32
| type_u64
| type_u128
}
type_field = {"fe"}
type_bool = {"bool"}
type_basic = { type_integer | type_field | type_bool }
type_struct = { variable }
type_basic_or_struct = {type_basic | type_struct }
type_array = {type_basic ~ ("[" ~ value ~ "]")+ }
_type = {type_array | type_basic | type_struct}
type_list = _{(_type ~ ("," ~ _type)*)?}
/// Values
value_number = @{ "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)}
value_u32 = { value_number ~ ty_u32? }
value_field = { value_number ~ ty_field }
value_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
value_integer = { value_number ~ type_integer? }
value_field = { value_number ~ type_field }
value_boolean = { "true" | "false" }
value = { value_field | value_boolean | value_u32 }
value = { value_field | value_boolean | value_integer }
/// Variables
// TODO: Include "import" and "conditional"
protected_name = { visibility | value_boolean | "return" }
protected_name = { visibility | "let" | "for"| "if" | "else" | "as" | "return" }
// keyword = @{ "as" | "in" | "return" | "export" | "false" |
// "def" | "in" | "return" | "struct" | "true" }
variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
optionally_typed_variable = { (ty ~ variable) | (variable)}
optionally_typed_variable = { (_type ~ variable) | (variable)}
optionally_typed_variable_tuple = _{ optionally_typed_variable ~ ("," ~ optionally_typed_variable)* }
expression_primitive = { value | variable }
@ -111,7 +131,7 @@ expression_array_initializer = { "[" ~ spread_or_expression ~ ";" ~ value ~ "]"
/// Structs
struct_field = { ty ~ variable }
struct_field = { variable ~ ":" ~ _type }
struct_field_list = _{(struct_field ~ (NEWLINE+ ~ struct_field)*)? }
struct_definition = { "struct" ~ variable ~ "{" ~ NEWLINE* ~ struct_field_list ~ NEWLINE* ~ "}" ~ NEWLINE* }
@ -121,7 +141,7 @@ expression_inline_struct = { variable ~ "{" ~ NEWLINE* ~ inline_struct_member_li
/// Conditionals
expression_conditional = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"}
expression_conditional = { "if" ~ expression ~ "?" ~ expression ~ ":" ~ expression}
/// Expressions
@ -141,31 +161,47 @@ expression_term = {
expression = { expression_term ~ (operation_binary ~ expression_term)* }
expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
/// Statements
/// Asserts
assert_eq = {"assert_eq" ~ "(" ~ NEWLINE* ~ expression ~ "," ~ NEWLINE* ~ expression ~ NEWLINE* ~ ")"}
// assert_true = {"assert"}
/// Conditionals
conditional_nested_or_end = { statement_conditional | "{" ~ NEWLINE* ~ statement+ ~ "}"}
/// Statements
statement_return = { "return" ~ expression_tuple }
statement_for = { "for" ~ variable ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"}
statement_multiple_assignment = { optionally_typed_variable_tuple ~ "=" ~ variable ~ "(" ~ expression_tuple ~ ")" }
statement_definition = { ty ~ variable ~ "=" ~ expression }
statement_assign = { assignee ~ "=" ~ expression }
statement_definition = { "let" ~ variable ~ (":" ~ _type)? ~ "=" ~ expression }
statement_assign = { assignee ~ operation_assign ~ expression }
statement_multiple_assignment = { "let" ~ "(" ~ optionally_typed_variable_tuple ~ ")" ~ "=" ~ variable ~ "(" ~ expression_tuple ~ ")" }
statement_conditional = {"if" ~ "(" ~ expression ~ ")" ~ "{" ~ NEWLINE* ~ statement+ ~ "}" ~ ("else" ~ conditional_nested_or_end)?}
statement_for = { "for" ~ variable ~ "in" ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
statement_assert = {
assert_eq
// | assert_true |
}
statement_expression = { expression }
statement = {
(statement_return
| (statement_for
| statement_multiple_assignment
| statement_conditional
| statement_for
| (statement_multiple_assignment
| statement_assert
| statement_definition
| statement_assign
| statement_expression
) ~ LINE_END
) ~ NEWLINE*
}
/// Functions
parameter = {variable ~ ":" ~ visibility? ~ ty}
parameter = {variable ~ ":" ~ visibility? ~ _type}
parameter_list = _{(parameter ~ ("," ~ parameter)*)?}
function_name = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
function_definition = {"function" ~ function_name ~ "(" ~ parameter_list ~ ")" ~ "->" ~ "(" ~ type_list ~ ")" ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
function_definition = {"function" ~ function_name ~ "(" ~ parameter_list ~ ")" ~ ("->" ~ (_type | "(" ~ type_list ~ ")"))? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
/// Utilities
@ -180,8 +216,6 @@ import_symbol = { variable ~ ("as" ~ variable)? }
import_symbol_tuple = _{ import_symbol ~ ("," ~ NEWLINE* ~ import_symbol)* }
import = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ ("*" | ("{" ~ NEWLINE* ~ import_symbol_tuple ~ NEWLINE* ~ "}")) ~ LINE_END}
// import = { main_import | from_import }
// main_import = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ variable)? ~ NEWLINE+}
/// Program File

View File

@ -1,10 +1,15 @@
//! Module containing structs and types that make up an Leo program.
//! Module containing structs and types that make up a Leo program.
#[macro_use]
extern crate thiserror;
extern crate from_pest;
#[macro_use]
extern crate lazy_static;
extern crate from_pest;
extern crate pest;
extern crate pest_ast;
#[macro_use]
extern crate pest_derive;
@ -15,6 +20,8 @@ pub mod compiler;
pub mod constraints;
pub use self::constraints::*;
pub mod errors;
pub mod imports;
pub use self::imports::*;

View File

@ -1,9 +1,16 @@
//! A typed program in aleo consists of import, struct, and function definitions.
//! A typed Leo program consists of import, struct, and function definitions.
//! Each defined type consists of typed statements and expressions.
use crate::Import;
use crate::{errors::IntegerError, Import};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{
r1cs::Variable as R1CSVariable,
utilities::{
boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64,
uint8::UInt8,
},
};
use std::collections::HashMap;
use std::marker::PhantomData;
@ -14,48 +21,81 @@ pub struct Variable<F: Field + PrimeField> {
pub(crate) _field: PhantomData<F>,
}
/// An integer type enum wrapping the integer value
#[derive(Debug, Clone)]
/// An integer type enum wrapping the integer value. Used only in expressions.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Integer {
// U8(u8),
U32(u32),
// U64(u64),
U8(UInt8),
U16(UInt16),
U32(UInt32),
U64(UInt64),
U128(UInt128),
}
impl Integer {
pub fn to_usize(&self) -> usize {
match *self {
// U8(u8)
Integer::U32(num) => num as usize,
// U64(u64)
match self {
Integer::U8(u8) => u8.value.unwrap() as usize,
Integer::U16(u16) => u16.value.unwrap() as usize,
Integer::U32(u32) => u32.value.unwrap() as usize,
Integer::U64(u64) => u64.value.unwrap() as usize,
Integer::U128(u128) => u128.value.unwrap() as usize,
}
}
pub(crate) fn get_type(&self) -> IntegerType {
match self {
Integer::U8(_u8) => IntegerType::U8,
Integer::U16(_u16) => IntegerType::U16,
Integer::U32(_u32) => IntegerType::U32,
Integer::U64(_u64) => IntegerType::U64,
Integer::U128(_u128) => IntegerType::U128,
}
}
pub(crate) fn expect_type(&self, integer_type: &IntegerType) -> Result<(), IntegerError> {
if self.get_type() != *integer_type {
unimplemented!(
"expected integer type {}, got {}",
self.get_type(),
integer_type
)
}
Ok(())
}
}
/// A constant or allocated element in the field
#[derive(Clone, PartialEq, Eq)]
pub enum FieldElement<F: Field + PrimeField> {
Constant(F),
Allocated(Option<F>, R1CSVariable),
}
/// Range or expression enum
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RangeOrExpression<F: Field + PrimeField> {
Range(Option<Integer>, Option<Integer>),
Expression(Expression<F>),
}
/// Spread or expression
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpreadOrExpression<F: Field + PrimeField> {
Spread(Expression<F>),
Expression(Expression<F>),
}
/// Expression that evaluates to a value
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expression<F: Field + PrimeField> {
// Variable
Variable(Variable<F>),
// Values
Integer(Integer),
FieldElement(F),
Boolean(bool),
FieldElement(FieldElement<F>),
Boolean(Boolean),
// Number operations
Add(Box<Expression<F>>, Box<Expression<F>>),
@ -79,7 +119,7 @@ pub enum Expression<F: Field + PrimeField> {
// Arrays
Array(Vec<Box<SpreadOrExpression<F>>>),
ArrayAccess(Box<Expression<F>>, Box<RangeOrExpression<F>>),
ArrayAccess(Box<Expression<F>>, Box<RangeOrExpression<F>>), // (array name, range)
// Structs
Struct(Variable<F>, Vec<StructMember<F>>),
@ -90,47 +130,73 @@ pub enum Expression<F: Field + PrimeField> {
}
/// Definition assignee: v, arr[0..2], Point p.x
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Assignee<F: Field + PrimeField> {
Variable(Variable<F>),
Array(Box<Assignee<F>>, RangeOrExpression<F>),
StructMember(Box<Assignee<F>>, Variable<F>),
}
/// Explicit type used for defining a variable or expression type
#[derive(Clone, Debug, PartialEq)]
pub enum Type<F: Field + PrimeField> {
/// Explicit integer type
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IntegerType {
U8,
U16,
U32,
U64,
U128,
}
/// Explicit type used for defining a variable or expression type
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type<F: Field + PrimeField> {
IntegerType(IntegerType),
FieldElement,
Boolean,
Array(Box<Type<F>>, usize),
Struct(Variable<F>),
}
#[derive(Clone, PartialEq, Eq)]
pub enum ConditionalNestedOrEnd<F: Field + PrimeField> {
Nested(Box<ConditionalStatement<F>>),
End(Vec<Statement<F>>),
}
#[derive(Clone, PartialEq, Eq)]
pub struct ConditionalStatement<F: Field + PrimeField> {
pub condition: Expression<F>,
pub statements: Vec<Statement<F>>,
pub next: Option<ConditionalNestedOrEnd<F>>,
}
/// Program statement that defines some action (or expression) to be carried out.
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub enum Statement<F: Field + PrimeField> {
// Declaration(Variable),
Return(Vec<Expression<F>>),
Definition(Assignee<F>, Option<Type<F>>, Expression<F>),
Assign(Assignee<F>, Expression<F>),
Definition(Type<F>, Assignee<F>, Expression<F>),
MultipleDefinition(Vec<Assignee<F>>, Expression<F>),
MultipleAssign(Vec<Assignee<F>>, Expression<F>),
Conditional(ConditionalStatement<F>),
For(Variable<F>, Integer, Integer, Vec<Statement<F>>),
AssertEq(Expression<F>, Expression<F>),
Expression(Expression<F>),
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructMember<F: Field + PrimeField> {
pub variable: Variable<F>,
pub expression: Expression<F>,
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct StructField<F: Field + PrimeField> {
pub variable: Variable<F>,
pub ty: Type<F>,
pub _type: Type<F>,
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct Struct<F: Field + PrimeField> {
pub variable: Variable<F>,
pub fields: Vec<StructField<F>>,
@ -138,21 +204,28 @@ pub struct Struct<F: Field + PrimeField> {
/// Function parameters
#[derive(Clone)]
pub struct Parameter<F: Field + PrimeField> {
#[derive(Clone, PartialEq, Eq)]
pub struct InputModel<F: Field + PrimeField> {
pub private: bool,
pub ty: Type<F>,
pub _type: Type<F>,
pub variable: Variable<F>,
}
#[derive(Clone, PartialEq, Eq)]
pub enum InputValue<F: Field + PrimeField> {
Integer(usize),
Field(F),
Boolean(bool),
}
/// The given name for a defined function in the program.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FunctionName(pub String);
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct Function<F: Field + PrimeField> {
pub function_name: FunctionName,
pub parameters: Vec<Parameter<F>>,
pub inputs: Vec<InputModel<F>>,
pub returns: Vec<Type<F>>,
pub statements: Vec<Statement<F>>,
}
@ -165,14 +238,28 @@ impl<F: Field + PrimeField> Function<F> {
/// A simple program with statement expressions, program arguments and program returns.
#[derive(Debug, Clone)]
pub struct Program<'ast, F: Field + PrimeField> {
pub struct Program<F: Field + PrimeField> {
pub name: Variable<F>,
pub imports: Vec<Import<'ast, F>>,
pub num_parameters: usize,
pub imports: Vec<Import<F>>,
pub structs: HashMap<Variable<F>, Struct<F>>,
pub functions: HashMap<FunctionName, Function<F>>,
}
impl<'ast, F: Field + PrimeField> Program<'ast, F> {
impl<'ast, F: Field + PrimeField> Program<F> {
pub fn new() -> Self {
Self {
name: Variable {
name: "".into(),
_field: PhantomData::<F>,
},
num_parameters: 0,
imports: vec![],
structs: HashMap::new(),
functions: HashMap::new(),
}
}
pub fn get_name(&self) -> String {
self.name.name.clone()
}

View File

@ -1,7 +1,8 @@
//! Format display functions for zokrates_program types.
//! Format display functions for Leo types.
use crate::{
Assignee, Expression, Function, FunctionName, Integer, Parameter, RangeOrExpression,
Assignee, ConditionalNestedOrEnd, ConditionalStatement, Expression, FieldElement, Function,
FunctionName, InputModel, InputValue, Integer, IntegerType, RangeOrExpression,
SpreadOrExpression, Statement, Struct, StructField, Type, Variable,
};
@ -21,12 +22,37 @@ impl<F: Field + PrimeField> fmt::Debug for Variable<F> {
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.to_usize(), self.get_type())
}
}
impl<F: Field + PrimeField> FieldElement<F> {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Integer::U32(ref num) => write!(f, "{}", num),
FieldElement::Constant(ref constant) => write!(f, "{}", constant),
FieldElement::Allocated(ref option, ref _r1cs_var) => {
if option.is_some() {
write!(f, "{}", option.unwrap())
} else {
write!(f, "allocated fe")
}
}
}
}
}
impl<F: Field + PrimeField> fmt::Display for FieldElement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl<F: Field + PrimeField> fmt::Debug for FieldElement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl<'ast, F: Field + PrimeField> fmt::Display for RangeOrExpression<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@ -63,7 +89,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression<F> {
// Values
Expression::Integer(ref integer) => write!(f, "{}", integer),
Expression::FieldElement(ref fe) => write!(f, "{}", fe),
Expression::Boolean(ref bool) => write!(f, "{}", bool),
Expression::Boolean(ref bool) => write!(f, "{}", bool.get_value().unwrap()),
// Number operations
Expression::Add(ref left, ref right) => write!(f, "{} + {}", left, right),
@ -87,6 +113,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression<F> {
write!(f, "if {} then {} else {} fi", first, second, third)
}
// Arrays
Expression::Array(ref array) => {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
@ -99,6 +126,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression<F> {
}
Expression::ArrayAccess(ref array, ref index) => write!(f, "{}[{}]", array, index),
// Structs
Expression::Struct(ref var, ref members) => {
write!(f, "{} {{", var)?;
for (i, member) in members.iter().enumerate() {
@ -112,6 +140,8 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression<F> {
Expression::StructMemberAccess(ref struct_variable, ref member) => {
write!(f, "{}.{}", struct_variable, member)
}
// Function calls
Expression::FunctionCall(ref function, ref arguments) => {
write!(f, "{}(", function,)?;
for (i, param) in arguments.iter().enumerate() {
@ -121,7 +151,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression<F> {
}
}
write!(f, ")")
} // _ => unimplemented!("can't display expression yet"),
}
}
}
}
@ -138,80 +168,88 @@ impl<F: Field + PrimeField> fmt::Display for Assignee<F> {
}
}
impl<F: Field + PrimeField> fmt::Display for Statement<F> {
impl<F: Field + PrimeField> fmt::Display for ConditionalNestedOrEnd<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Return(ref statements) => {
write!(f, "return ")?;
for (i, value) in statements.iter().enumerate() {
write!(f, "{}", value)?;
if i < statements.len() - 1 {
write!(f, ", ")?;
}
ConditionalNestedOrEnd::Nested(ref nested) => write!(f, "else {}", nested),
ConditionalNestedOrEnd::End(ref statements) => {
write!(f, "else {{\n")?;
for statement in statements.iter() {
write!(f, "\t\t{}\n", statement)?;
}
write!(f, "\n")
}
Statement::Assign(ref variable, ref statement) => {
write!(f, "{} = {};", variable, statement)
}
Statement::Definition(ref ty, ref assignee, ref statement) => {
write!(f, "{} {} = {};", ty, assignee, statement)
}
Statement::MultipleDefinition(ref assignees, ref function) => {
for (i, id) in assignees.iter().enumerate() {
write!(f, "{}", id)?;
if i < assignees.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, " = {};", function)
}
Statement::For(ref var, ref start, ref stop, ref list) => {
write!(f, "for {} in {}..{} do\n", var, start, stop)?;
for l in list {
write!(f, "\t\t{}\n", l)?;
}
write!(f, "\tendfor;")
write!(f, "\t}}")
}
}
}
}
impl<F: Field + PrimeField> fmt::Debug for Statement<F> {
impl<F: Field + PrimeField> fmt::Display for ConditionalStatement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "if ({}) {{\n", self.condition)?;
for statement in self.statements.iter() {
write!(f, "\t\t{}\n", statement)?;
}
match self.next.clone() {
Some(n_or_e) => write!(f, "\t}} {}", n_or_e),
None => write!(f, "\t}}"),
}
}
}
impl<F: Field + PrimeField> fmt::Display for Statement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Return(ref statements) => {
write!(f, "return ")?;
write!(f, "return (")?;
for (i, value) in statements.iter().enumerate() {
write!(f, "{}", value)?;
if i < statements.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "\n")
write!(f, ")\n")
}
Statement::Definition(ref assignee, ref ty, ref expression) => match ty {
Some(ref ty) => write!(f, "let {}: {} = {};", assignee, ty, expression),
None => write!(f, "let {} = {};", assignee, expression),
},
Statement::Assign(ref variable, ref statement) => {
write!(f, "{} = {};", variable, statement)
}
Statement::Definition(ref ty, ref assignee, ref statement) => {
write!(f, "{} {} = {};", ty, assignee, statement)
}
Statement::MultipleDefinition(ref assignees, ref function) => {
Statement::MultipleAssign(ref assignees, ref function) => {
write!(f, "let (")?;
for (i, id) in assignees.iter().enumerate() {
write!(f, "{}", id)?;
if i < assignees.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, " = {}();", function)
write!(f, ") = {};", function)
}
Statement::Conditional(ref statement) => write!(f, "{}", statement),
Statement::For(ref var, ref start, ref stop, ref list) => {
write!(f, "for {:?} in {:?}..{:?} do\n", var, start, stop)?;
write!(f, "for {} in {}..{} {{\n", var, start, stop)?;
for l in list {
write!(f, "\t\t{:?}\n", l)?;
write!(f, "\t\t{}\n", l)?;
}
write!(f, "\tendfor;")
write!(f, "\t}}")
}
Statement::AssertEq(ref left, ref right) => {
write!(f, "assert_eq({}, {});", left, right)
}
Statement::Expression(ref expression) => write!(f, "{};", expression),
}
}
}
impl fmt::Display for IntegerType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
IntegerType::U8 => write!(f, "u8"),
IntegerType::U16 => write!(f, "u16"),
IntegerType::U32 => write!(f, "u32"),
IntegerType::U64 => write!(f, "u64"),
IntegerType::U128 => write!(f, "u128"),
}
}
}
@ -219,7 +257,7 @@ impl<F: Field + PrimeField> fmt::Debug for Statement<F> {
impl<F: Field + PrimeField> fmt::Display for Type<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::U32 => write!(f, "u32"),
Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type),
Type::FieldElement => write!(f, "fe"),
Type::Boolean => write!(f, "bool"),
Type::Struct(ref variable) => write!(f, "{}", variable),
@ -230,12 +268,12 @@ impl<F: Field + PrimeField> fmt::Display for Type<F> {
impl<F: Field + PrimeField> fmt::Display for StructField<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} : {}", self.ty, self.variable)
write!(f, "{}: {}", self.variable, self._type)
}
}
impl<F: Field + PrimeField> fmt::Debug for Struct<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl<F: Field + PrimeField> Struct<F> {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "struct {} {{ \n", self.variable)?;
for field in self.fields.iter() {
write!(f, " {}\n", field)?;
@ -244,69 +282,92 @@ impl<F: Field + PrimeField> fmt::Debug for Struct<F> {
}
}
impl<F: Field + PrimeField> fmt::Display for Parameter<F> {
// impl<F: Field + PrimeField> fmt::Display for Struct<F> {// uncomment when we no longer print out Program
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// self.format(f)
// }
// }
impl<F: Field + PrimeField> fmt::Debug for Struct<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let visibility = if self.private { "private" } else { "public" };
write!(f, "{}: {} {}", self.variable, visibility, self.ty,)
self.format(f)
}
}
impl<F: Field + PrimeField> fmt::Debug for Parameter<F> {
impl<F: Field + PrimeField> fmt::Display for InputModel<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Parameter(variable: {:?})", self.ty)
let visibility = if self.private { "private" } else { "public" };
write!(f, "{}: {} {}", self.variable, visibility, self._type,)
}
}
impl<F: Field + PrimeField> fmt::Display for InputValue<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InputValue::Integer(ref integer) => write!(f, "{}", integer),
InputValue::Field(ref field) => write!(f, "{}", field),
InputValue::Boolean(ref bool) => write!(f, "{}", bool),
}
}
}
impl FunctionName {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl fmt::Debug for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
self.format(f)
}
}
impl<F: Field + PrimeField> fmt::Display for Function<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({}): ->({})\n{}",
self.parameters
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(","),
self.returns
.iter()
.map(|r| format!("{}", r))
.collect::<Vec<_>>()
.join(","),
self.statements
.iter()
.map(|x| format!("\t{}", x))
.collect::<Vec<_>>()
.join("\n"),
)
impl<F: Field + PrimeField> Function<F> {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "function {}", self.function_name)?;
let parameters = self
.inputs
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(",");
let returns = self
.returns
.iter()
.map(|r| format!("{}", r))
.collect::<Vec<_>>()
.join(",");
let statements = self
.statements
.iter()
.map(|s| format!("\t{}\n", s))
.collect::<Vec<_>>()
.join("");
if self.returns.len() == 0 {
write!(f, "({}) {{\n{}}}", parameters, statements,)
} else if self.returns.len() == 1 {
write!(f, "({}) -> {} {{\n{}}}", parameters, returns, statements,)
} else {
write!(f, "({}) -> ({}) {{\n{}}}", parameters, returns, statements,)
}
}
}
// impl<F: Field + PrimeField> fmt::Display for Function<F> {// uncomment when we no longer print out Program
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// self.format(f)
// }
// }
impl<F: Field + PrimeField> fmt::Debug for Function<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({}): -> ({})\n{}",
self.parameters
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(","),
self.returns
.iter()
.map(|r| format!("{}", r))
.collect::<Vec<_>>()
.join(","),
self.statements
.iter()
.map(|x| format!("\t{}", x))
.collect::<Vec<_>>()
.join("\n"),
)
self.format(f)
}
}

View File

@ -1,12 +1,13 @@
//! Logic to convert from an abstract syntax tree (ast) representation to a typed aleo program.
//! Logic to convert from an abstract syntax tree (ast) representation to a Leo program.
use crate::ast;
use crate::{types, Import, ImportSymbol};
use crate::{ast, types, Import, ImportSymbol};
use snarkos_models::curves::{Field, PrimeField};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::path::Path;
use snarkos_models::gadgets::utilities::{
boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64,
uint8::UInt8,
};
use std::{collections::HashMap, marker::PhantomData};
/// pest ast -> types::Variable
@ -26,15 +27,41 @@ impl<'ast, F: Field + PrimeField> From<ast::Variable<'ast>> for types::Expressio
}
/// pest ast - types::Integer
impl<'ast, F: Field + PrimeField> From<ast::U32<'ast>> for types::Expression<F> {
fn from(field: ast::U32<'ast>) -> Self {
types::Expression::Integer(types::Integer::U32(
field
.number
.value
.parse::<u32>()
.expect("unable to parse u32"),
))
impl<'ast> types::Integer {
pub(crate) fn from(number: ast::Number<'ast>, _type: ast::IntegerType) -> Self {
match _type {
ast::IntegerType::U8Type(_u8) => types::Integer::U8(UInt8::constant(
number.value.parse::<u8>().expect("unable to parse u8"),
)),
ast::IntegerType::U16Type(_u16) => types::Integer::U16(UInt16::constant(
number.value.parse::<u16>().expect("unable to parse u16"),
)),
ast::IntegerType::U32Type(_u32) => types::Integer::U32(UInt32::constant(
number.value.parse::<u32>().expect("unable to parse u32"),
)),
ast::IntegerType::U64Type(_u64) => types::Integer::U64(UInt64::constant(
number.value.parse::<u64>().expect("unable to parse u64"),
)),
ast::IntegerType::U128Type(_u128) => types::Integer::U128(UInt128::constant(
number.value.parse::<u128>().expect("unable to parse u128"),
)),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::Integer<'ast>> for types::Expression<F> {
fn from(field: ast::Integer<'ast>) -> Self {
types::Expression::Integer(match field._type {
Some(_type) => types::Integer::from(field.number, _type),
// default integer type is u32
None => types::Integer::U32(UInt32::constant(
field
.number
.value
.parse::<u32>()
.expect("unable to parse u32"),
)),
})
}
}
@ -55,7 +82,7 @@ impl<'ast, F: Field + PrimeField> From<ast::RangeOrExpression<'ast>>
let to = range.to.map(|to| match types::Expression::<F>::from(to.0) {
types::Expression::Integer(number) => number,
expression => {
unimplemented!("Range bounds should be intgers, found {}", expression)
unimplemented!("Range bounds should be integers, found {}", expression)
}
});
@ -72,7 +99,9 @@ impl<'ast, F: Field + PrimeField> From<ast::RangeOrExpression<'ast>>
impl<'ast, F: Field + PrimeField> From<ast::Field<'ast>> for types::Expression<F> {
fn from(field: ast::Field<'ast>) -> Self {
types::Expression::FieldElement(F::from_str(&field.number.value).unwrap_or_default())
types::Expression::FieldElement(types::FieldElement::Constant(
F::from_str(&field.number.value).unwrap_or_default(),
))
}
}
@ -80,12 +109,12 @@ impl<'ast, F: Field + PrimeField> From<ast::Field<'ast>> for types::Expression<F
impl<'ast, F: Field + PrimeField> From<ast::Boolean<'ast>> for types::Expression<F> {
fn from(boolean: ast::Boolean<'ast>) -> Self {
types::Expression::Boolean(
types::Expression::Boolean(Boolean::Constant(
boolean
.value
.parse::<bool>()
.expect("unable to parse boolean"),
)
))
}
}
@ -94,7 +123,7 @@ impl<'ast, F: Field + PrimeField> From<ast::Boolean<'ast>> for types::Expression
impl<'ast, F: Field + PrimeField> From<ast::Value<'ast>> for types::Expression<F> {
fn from(value: ast::Value<'ast>) -> Self {
match value {
ast::Value::U32(num) => types::Expression::from(num),
ast::Value::Integer(num) => types::Expression::from(num),
ast::Value::Field(fe) => types::Expression::from(fe),
ast::Value::Boolean(bool) => types::Expression::from(bool),
}
@ -285,7 +314,6 @@ impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Express
ast::Expression::ArrayInitializer(expression) => types::Expression::from(expression),
ast::Expression::StructInline(expression) => types::Expression::from(expression),
ast::Expression::Postfix(expression) => types::Expression::from(expression),
// _ => unimplemented!(),
}
}
}
@ -293,7 +321,7 @@ impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Express
impl<'ast, F: Field + PrimeField> types::Expression<F> {
fn get_count(count: ast::Value<'ast>) -> usize {
match count {
ast::Value::U32(f) => f
ast::Value::Integer(f) => f
.number
.value
.parse::<usize>()
@ -303,6 +331,30 @@ impl<'ast, F: Field + PrimeField> types::Expression<F> {
}
}
// ast::Assignee -> types::Expression for operator assign statements
impl<'ast, F: Field + PrimeField> From<ast::Assignee<'ast>> for types::Expression<F> {
fn from(assignee: ast::Assignee<'ast>) -> Self {
let variable = types::Expression::Variable(types::Variable::from(assignee.variable));
// we start with the id, and we fold the array of accesses by wrapping the current value
assignee
.accesses
.into_iter()
.fold(variable, |acc, access| match access {
ast::AssigneeAccess::Member(struct_member) => {
types::Expression::StructMemberAccess(
Box::new(acc),
types::Variable::from(struct_member.variable),
)
}
ast::AssigneeAccess::Array(array) => types::Expression::ArrayAccess(
Box::new(acc),
Box::new(types::RangeOrExpression::from(array.expression)),
),
})
}
}
/// pest ast -> types::Assignee
impl<'ast, F: Field + PrimeField> From<ast::Variable<'ast>> for types::Assignee<F> {
@ -346,6 +398,133 @@ impl<'ast, F: Field + PrimeField> From<ast::ReturnStatement<'ast>> for types::St
}
}
impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
types::Statement::Definition(
types::Assignee::from(statement.variable),
statement._type.map(|_type| types::Type::<F>::from(_type)),
types::Expression::from(statement.expression),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::AssignStatement<'ast>) -> Self {
match statement.assign {
ast::OperationAssign::Assign(ref _assign) => types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::from(statement.expression),
),
operation_assign => {
// convert assignee into postfix expression
let converted = types::Expression::from(statement.assignee.clone());
match operation_assign {
ast::OperationAssign::AddAssign(ref _assign) => types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::Add(
Box::new(converted),
Box::new(types::Expression::from(statement.expression)),
),
),
ast::OperationAssign::SubAssign(ref _assign) => types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::Sub(
Box::new(converted),
Box::new(types::Expression::from(statement.expression)),
),
),
ast::OperationAssign::MulAssign(ref _assign) => types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::Mul(
Box::new(converted),
Box::new(types::Expression::from(statement.expression)),
),
),
ast::OperationAssign::DivAssign(ref _assign) => types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::Div(
Box::new(converted),
Box::new(types::Expression::from(statement.expression)),
),
),
ast::OperationAssign::PowAssign(ref _assign) => types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::Pow(
Box::new(converted),
Box::new(types::Expression::from(statement.expression)),
),
),
ast::OperationAssign::Assign(ref _assign) => {
unimplemented!("cannot assign twice to assign statement")
}
}
}
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::MultipleAssignmentStatement<'ast>>
for types::Statement<F>
{
fn from(statement: ast::MultipleAssignmentStatement<'ast>) -> Self {
let assignees = statement
.assignees
.into_iter()
.map(|i| types::Assignee::Variable(types::Variable::from(i.id)))
.collect();
types::Statement::MultipleAssign(
assignees,
types::Expression::FunctionCall(
types::Variable::from(statement.function_name),
statement
.arguments
.into_iter()
.map(|e| types::Expression::from(e))
.collect(),
),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::ConditionalNestedOrEnd<'ast>>
for types::ConditionalNestedOrEnd<F>
{
fn from(statement: ast::ConditionalNestedOrEnd<'ast>) -> Self {
match statement {
ast::ConditionalNestedOrEnd::Nested(nested) => types::ConditionalNestedOrEnd::Nested(
Box::new(types::ConditionalStatement::from(*nested)),
),
ast::ConditionalNestedOrEnd::End(statements) => types::ConditionalNestedOrEnd::End(
statements
.into_iter()
.map(|statement| types::Statement::from(statement))
.collect(),
),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::ConditionalStatement<'ast>>
for types::ConditionalStatement<F>
{
fn from(statement: ast::ConditionalStatement<'ast>) -> Self {
types::ConditionalStatement {
condition: types::Expression::from(statement.condition),
statements: statement
.statements
.into_iter()
.map(|statement| types::Statement::from(statement))
.collect(),
next: statement
.next
.map(|n_or_e| Some(types::ConditionalNestedOrEnd::from(n_or_e)))
.unwrap_or(None),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::ForStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::ForStatement<'ast>) -> Self {
let from = match types::Expression::<F>::from(statement.start) {
@ -370,46 +549,20 @@ impl<'ast, F: Field + PrimeField> From<ast::ForStatement<'ast>> for types::State
}
}
impl<'ast, F: Field + PrimeField> From<ast::MultipleAssignmentStatement<'ast>>
for types::Statement<F>
{
fn from(statement: ast::MultipleAssignmentStatement<'ast>) -> Self {
let assignees = statement
.assignees
.into_iter()
.map(|i| types::Assignee::Variable(types::Variable::from(i.id)))
.collect();
types::Statement::MultipleDefinition(
assignees,
types::Expression::FunctionCall(
types::Variable::from(statement.function_name),
statement
.arguments
.into_iter()
.map(|e| types::Expression::from(e))
.collect(),
impl<'ast, F: Field + PrimeField> From<ast::AssertStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::AssertStatement<'ast>) -> Self {
match statement {
ast::AssertStatement::AssertEq(assert_eq) => types::Statement::AssertEq(
types::Expression::from(assert_eq.left),
types::Expression::from(assert_eq.right),
),
)
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::AssignStatement<'ast>) -> Self {
types::Statement::Assign(
types::Assignee::from(statement.assignee),
types::Expression::from(statement.expression),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
types::Statement::Definition(
types::Type::from(statement.ty),
types::Assignee::from(statement.variable),
types::Expression::from(statement.expression),
)
impl<'ast, F: Field + PrimeField> From<ast::ExpressionStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::ExpressionStatement<'ast>) -> Self {
types::Statement::Expression(types::Expression::from(statement.expression))
}
}
@ -417,20 +570,37 @@ impl<'ast, F: Field + PrimeField> From<ast::Statement<'ast>> for types::Statemen
fn from(statement: ast::Statement<'ast>) -> Self {
match statement {
ast::Statement::Return(statement) => types::Statement::from(statement),
ast::Statement::Iteration(statement) => types::Statement::from(statement),
ast::Statement::MultipleAssignment(statement) => types::Statement::from(statement),
ast::Statement::Assign(statement) => types::Statement::from(statement),
ast::Statement::Definition(statement) => types::Statement::from(statement),
ast::Statement::Assign(statement) => types::Statement::from(statement),
ast::Statement::MultipleAssignment(statement) => types::Statement::from(statement),
ast::Statement::Conditional(statement) => {
types::Statement::Conditional(types::ConditionalStatement::from(statement))
}
ast::Statement::Iteration(statement) => types::Statement::from(statement),
ast::Statement::Assert(statement) => types::Statement::from(statement),
ast::Statement::Expression(statement) => types::Statement::from(statement),
}
}
}
/// pest ast -> Explicit types::Type for defining struct members and function params
impl From<ast::IntegerType> for types::IntegerType {
fn from(integer_type: ast::IntegerType) -> Self {
match integer_type {
ast::IntegerType::U8Type(_type) => types::IntegerType::U8,
ast::IntegerType::U16Type(_type) => types::IntegerType::U16,
ast::IntegerType::U32Type(_type) => types::IntegerType::U32,
ast::IntegerType::U64Type(_type) => types::IntegerType::U64,
ast::IntegerType::U128Type(_type) => types::IntegerType::U128,
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::BasicType<'ast>> for types::Type<F> {
fn from(basic_type: ast::BasicType<'ast>) -> Self {
match basic_type {
ast::BasicType::U32(_ty) => types::Type::U32,
ast::BasicType::Integer(ty) => types::Type::IntegerType(types::IntegerType::from(ty)),
ast::BasicType::Field(_ty) => types::Type::FieldElement,
ast::BasicType::Boolean(_ty) => types::Type::Boolean,
}
@ -439,7 +609,7 @@ impl<'ast, F: Field + PrimeField> From<ast::BasicType<'ast>> for types::Type<F>
impl<'ast, F: Field + PrimeField> From<ast::ArrayType<'ast>> for types::Type<F> {
fn from(array_type: ast::ArrayType<'ast>) -> Self {
let element_type = Box::new(types::Type::from(array_type.ty));
let element_type = Box::new(types::Type::from(array_type._type));
let count = types::Expression::<F>::get_count(array_type.count);
types::Type::Array(element_type, count)
@ -453,11 +623,11 @@ impl<'ast, F: Field + PrimeField> From<ast::StructType<'ast>> for types::Type<F>
}
impl<'ast, F: Field + PrimeField> From<ast::Type<'ast>> for types::Type<F> {
fn from(ty: ast::Type<'ast>) -> Self {
match ty {
ast::Type::Basic(ty) => types::Type::from(ty),
ast::Type::Array(ty) => types::Type::from(ty),
ast::Type::Struct(ty) => types::Type::from(ty),
fn from(_type: ast::Type<'ast>) -> Self {
match _type {
ast::Type::Basic(_type) => types::Type::from(_type),
ast::Type::Array(_type) => types::Type::from(_type),
ast::Type::Struct(_type) => types::Type::from(_type),
}
}
}
@ -468,7 +638,7 @@ impl<'ast, F: Field + PrimeField> From<ast::StructField<'ast>> for types::Struct
fn from(struct_field: ast::StructField<'ast>) -> Self {
types::StructField {
variable: types::Variable::from(struct_field.variable),
ty: types::Type::from(struct_field.ty),
_type: types::Type::from(struct_field._type),
}
}
}
@ -488,9 +658,9 @@ impl<'ast, F: Field + PrimeField> From<ast::Struct<'ast>> for types::Struct<F> {
/// pest ast -> function types::Parameters
impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::Parameter<F> {
impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::InputModel<F> {
fn from(parameter: ast::Parameter<'ast>) -> Self {
let ty = types::Type::from(parameter.ty);
let _type = types::Type::from(parameter._type);
let variable = types::Variable::from(parameter.variable);
if parameter.visibility.is_some() {
@ -498,15 +668,15 @@ impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::Paramete
ast::Visibility::Private(_) => true,
ast::Visibility::Public(_) => false,
};
types::Parameter {
types::InputModel {
private,
ty,
_type,
variable,
}
} else {
types::Parameter {
types::InputModel {
private: true,
ty,
_type,
variable,
}
}
@ -527,7 +697,7 @@ impl<'ast, F: Field + PrimeField> From<ast::Function<'ast>> for types::Function<
let parameters = function_definition
.parameters
.into_iter()
.map(|parameter| types::Parameter::from(parameter))
.map(|parameter| types::InputModel::from(parameter))
.collect();
let returns = function_definition
.returns
@ -542,7 +712,7 @@ impl<'ast, F: Field + PrimeField> From<ast::Function<'ast>> for types::Function<
types::Function {
function_name,
parameters,
inputs: parameters,
returns,
statements,
}
@ -560,10 +730,10 @@ impl<'ast, F: Field + PrimeField> From<ast::ImportSymbol<'ast>> for ImportSymbol
}
}
impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<'ast, F> {
impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<F> {
fn from(import: ast::Import<'ast>) -> Self {
Import {
source: Path::new(import.source.span.as_str()),
path_string: import.source.value,
symbols: import
.symbols
.into_iter()
@ -575,17 +745,18 @@ impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<'ast, F> {
/// pest ast -> types::Program
impl<'ast, F: Field + PrimeField> From<ast::File<'ast>> for types::Program<'ast, F> {
fn from(file: ast::File<'ast>) -> Self {
impl<'ast, F: Field + PrimeField> types::Program<F> {
pub fn from(file: ast::File<'ast>, name: String) -> Self {
// Compiled ast -> aleo program representation
let imports = file
.imports
.into_iter()
.map(|import| Import::from(import))
.collect::<Vec<Import<'ast, F>>>();
.collect::<Vec<Import<F>>>();
let mut structs = HashMap::new();
let mut functions = HashMap::new();
let mut num_parameters = 0usize;
file.structs.into_iter().for_each(|struct_def| {
structs.insert(
@ -600,11 +771,16 @@ impl<'ast, F: Field + PrimeField> From<ast::File<'ast>> for types::Program<'ast,
);
});
if let Some(main_function) = functions.get(&types::FunctionName("main".into())) {
num_parameters = main_function.inputs.len();
}
types::Program {
name: types::Variable {
name: "".into(),
name,
_field: PhantomData::<F>,
},
num_parameters,
imports,
structs,
functions,

1
compiler/tests/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod u32;

View File

@ -0,0 +1,3 @@
function main() -> (u32) {
return 1 + 1
}

View File

@ -0,0 +1,3 @@
function main() -> (u32) {
return 1 - 1
}

View File

@ -0,0 +1,4 @@
// This program should panic at compilation.
function main() -> (u32) {
return 1 - 2
}

101
compiler/tests/u32/mod.rs Normal file
View File

@ -0,0 +1,101 @@
use leo_compiler::{compiler::Compiler, ConstrainedValue};
use snarkos_curves::bls12_377::Fr;
use snarkos_models::gadgets::r1cs::{ConstraintSynthesizer, TestConstraintSystem};
use snarkos_models::gadgets::utilities::uint32::UInt32;
use std::env::current_dir;
const DIRECTORY_NAME: &str = "tests/u32/";
fn compile_program(directory_name: &str, file_name: &str) -> Compiler<Fr> {
let path = current_dir().unwrap();
// Sanitize the package path to the test directory
let mut package_path = path.clone();
if package_path.is_file() {
package_path.pop();
}
// Construct the path to the test file in the test directory
let mut main_file_path = package_path.clone();
main_file_path.push(directory_name);
main_file_path.push(file_name);
println!("Compiling file - {:?}", main_file_path);
// Compile from the main file path
let program = Compiler::<Fr>::init(file_name.to_string(), main_file_path);
program
}
#[test]
fn test_zero() {
let mut cs = TestConstraintSystem::<Fr>::new();
let program = compile_program(DIRECTORY_NAME, "zero.leo");
let output = program.evaluate_program(&mut cs);
assert!(cs.is_satisfied());
let output = output.unwrap();
assert_eq!(
ConstrainedValue::<Fr>::Return(vec![ConstrainedValue::<Fr>::Integer(UInt32::constant(0))]),
output
);
println!("{}", output);
}
#[test]
fn test_one() {
let mut cs = TestConstraintSystem::<Fr>::new();
let program = compile_program(DIRECTORY_NAME, "one.leo");
let output = program.evaluate_program(&mut cs);
assert!(cs.is_satisfied());
let output = output.unwrap();
assert_eq!(
ConstrainedValue::<Fr>::Return(vec![ConstrainedValue::<Fr>::Integer(UInt32::constant(1))]),
output
);
println!("{}", output);
}
#[test]
fn test_1_plus_1() {
let mut cs = TestConstraintSystem::<Fr>::new();
let program = compile_program(DIRECTORY_NAME, "1+1.leo");
let output = program.evaluate_program(&mut cs);
assert!(cs.is_satisfied());
let output = output.unwrap();
assert_eq!(
ConstrainedValue::<Fr>::Return(vec![ConstrainedValue::<Fr>::Integer(UInt32::constant(2))]),
output
);
println!("{}", output);
}
#[test]
fn test_1_minus_1() {
let mut cs = TestConstraintSystem::<Fr>::new();
let program = compile_program(DIRECTORY_NAME, "1-1.leo");
let output = program.evaluate_program(&mut cs);
assert!(cs.is_satisfied());
let output = output.unwrap();
assert_eq!(
ConstrainedValue::<Fr>::Return(vec![ConstrainedValue::<Fr>::Integer(UInt32::constant(0))]),
output
);
println!("{}", output);
}
#[test]
fn test_1_minus_2_should_fail() {
// TODO (howardwu): Catch panic from subtraction overflow
let mut cs = TestConstraintSystem::<Fr>::new();
let program = compile_program(DIRECTORY_NAME, "1-2.leo");
let output = program.evaluate_program(&mut cs);
assert!(output.is_err());
}

View File

@ -0,0 +1,3 @@
function main() -> (u32) {
return 1
}

View File

@ -0,0 +1,3 @@
function main() -> (u32) {
return 0
}

View File

@ -18,10 +18,7 @@ pub trait CLI {
fn new<'a, 'b>() -> App<'a, 'b> {
let arguments = &Self::ARGUMENTS
.iter()
.map(|a| Arg::with_name(a.0)
.help(a.1)
.required(a.2)
.index(a.3))
.map(|a| Arg::with_name(a.0).help(a.1).required(a.2).index(a.3))
.collect::<Vec<Arg<'static, 'static>>>();
let flags = &Self::FLAGS
.iter()
@ -34,7 +31,9 @@ pub trait CLI {
.conflicts_with_all(a.1)
.possible_values(a.2)
.requires_all(a.3),
false => Arg::from_usage(a.0).conflicts_with_all(a.1).requires_all(a.3),
false => Arg::from_usage(a.0)
.conflicts_with_all(a.1)
.requires_all(a.3),
})
.collect::<Vec<Arg<'static, 'static>>>();
let subcommands = Self::SUBCOMMANDS
@ -49,7 +48,9 @@ pub trait CLI {
.conflicts_with_all(a.1)
.possible_values(a.2)
.requires_all(a.3),
false => Arg::from_usage(a.0).conflicts_with_all(a.1).requires_all(a.3),
false => Arg::from_usage(a.0)
.conflicts_with_all(a.1)
.requires_all(a.3),
})
.collect::<Vec<Arg<'static, 'static>>>(),
)

View File

@ -21,4 +21,9 @@ pub type OptionType = (
&'static [&'static str],
);
pub type SubCommandType = (NameType, AboutType, &'static [OptionType], &'static [AppSettings]);
pub type SubCommandType = (
NameType,
AboutType,
&'static [OptionType],
&'static [AppSettings],
);

View File

@ -1,11 +1,12 @@
use crate::{cli::*, cli_types::*};
use crate::directories::{OutputsDirectory, source::SOURCE_DIRECTORY_NAME};
use crate::errors::{CLIError, BuildError};
use crate::files::{MainFile, MAIN_FILE_NAME};
use crate::directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory};
use crate::errors::{BuildError, CLIError};
use crate::files::{ChecksumFile, MainFile, MAIN_FILE_NAME};
use crate::manifest::Manifest;
use crate::{cli::*, cli_types::*};
use leo_compiler::compiler::Compiler;
use snarkos_curves::bls12_377::Fr;
use snarkos_algorithms::snark::KeypairAssembly;
use snarkos_curves::bls12_377::{Bls12_377, Fr};
use clap::ArgMatches;
use std::convert::TryFrom;
@ -16,7 +17,7 @@ pub struct BuildCommand;
impl CLI for BuildCommand {
type Options = ();
type Output = Compiler<Fr>;
type Output = (Compiler<Fr>, bool);
const NAME: NameType = "build";
const ABOUT: AboutType = "Compile the current package";
@ -33,7 +34,10 @@ impl CLI for BuildCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(_options: Self::Options) -> Result<Self::Output, CLIError> {
let path = current_dir()?;
let _manifest = Manifest::try_from(&path)?;
// Get the package name
let manifest = Manifest::try_from(&path)?;
let package_name = manifest.get_package_name();
// Sanitize the package path to the root directory
let mut package_path = path.clone();
@ -43,7 +47,9 @@ impl CLI for BuildCommand {
// Verify the main file exists
if !MainFile::exists_at(&package_path) {
return Err(BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into());
return Err(
BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into(),
);
}
// Create the outputs directory
@ -54,11 +60,39 @@ impl CLI for BuildCommand {
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
log::info!("Compiling program located in {:?}", main_file_path);
// Compute the current program checksum
let mut program = Compiler::<Fr>::init(package_name.clone(), main_file_path.clone());
let checksum = program.checksum()?;
// Compile from the main file path
let circuit = Compiler::<Fr>::init(main_file_path);
// If a checksum file exists, check if it differs from the new checksum
let checksum_file = ChecksumFile::new(&package_name);
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?;
checksum != previous_checksum
} else {
// By default, the checksum differs if there is no checksum to compare against
true
};
Ok(circuit)
// If checksum differs, compile the program
if checksum_differs {
// Write the new checksum to the outputs directory
checksum_file.write_to(&path, checksum)?;
// Generate the program on the constraint system and verify correctness
// let mut cs = KeypairAssembly::<Bls12_377> {
// num_inputs: 0,
// num_aux: 0,
// num_constraints: 0,
// at: vec![],
// bt: vec![],
// ct: vec![],
// };
program.evaluate_program::<KeypairAssembly::<Bls12_377>>()?;
}
log::info!("Compiled program in {:?}", main_file_path);
Ok((program, checksum_differs))
}
}

View File

@ -1,8 +1,8 @@
use crate::{cli::*, cli_types::*};
use crate::directories::{InputsDirectory, SourceDirectory};
use crate::errors::{CLIError, InitError};
use crate::files::MainFile;
use crate::manifest::Manifest;
use crate::{cli::*, cli_types::*};
use clap::ArgMatches;
use std::env::current_dir;

View File

@ -7,6 +7,9 @@ pub use self::init::*;
pub mod new;
pub use self::new::*;
pub mod prove;
pub use self::prove::*;
pub mod run;
pub use self::run::*;

View File

@ -1,8 +1,8 @@
use crate::{cli::*, cli_types::*};
use crate::directories::{InputsDirectory, SourceDirectory};
use crate::errors::{CLIError, NewError};
use crate::files::MainFile;
use crate::manifest::Manifest;
use crate::{cli::*, cli_types::*};
use clap::ArgMatches;
use std::env::current_dir;
@ -19,7 +19,12 @@ impl CLI for NewCommand {
const ABOUT: AboutType = "Creates a new Leo package";
const ARGUMENTS: &'static [ArgumentType] = &[
// (name, description, required, index)
("NAME", "Sets the resulting package name, defaults to the directory name", true, 1u64)
(
"NAME",
"Sets the resulting package name, defaults to the directory name",
true,
1u64,
),
];
const FLAGS: &'static [FlagType] = &[];
const OPTIONS: &'static [OptionType] = &[];
@ -29,7 +34,7 @@ impl CLI for NewCommand {
fn parse(arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
match arguments.value_of("NAME") {
Some(name) => Ok(Some(name.to_string())),
None => Ok(None)
None => Ok(None),
}
}
@ -56,9 +61,8 @@ impl CLI for NewCommand {
}
// Create the package directory
fs::create_dir_all(&path).map_err(|error| {
NewError::CreatingRootDirectory(path.as_os_str().to_owned(), error)
})?;
fs::create_dir_all(&path)
.map_err(|error| NewError::CreatingRootDirectory(path.as_os_str().to_owned(), error))?;
// Create the manifest file
Manifest::new(&package_name).write_to(&path)?;

60
leo/commands/prove.rs Normal file
View File

@ -0,0 +1,60 @@
use crate::{cli::*, cli_types::*};
use crate::commands::SetupCommand;
use crate::errors::CLIError;
use crate::files::ProofFile;
use crate::manifest::Manifest;
use snarkos_algorithms::snark::{create_random_proof, Proof};
use snarkos_curves::bls12_377::Bls12_377;
use clap::ArgMatches;
use rand::thread_rng;
use std::convert::TryFrom;
use std::env::current_dir;
use std::time::Instant;
#[derive(Debug)]
pub struct ProveCommand;
impl CLI for ProveCommand {
type Options = ();
type Output = Proof<Bls12_377>;
const NAME: NameType = "prove";
const ABOUT: AboutType = "Run the program and produce a proof";
const ARGUMENTS: &'static [ArgumentType] = &[];
const FLAGS: &'static [FlagType] = &[];
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
#[cfg_attr(tarpaulin, skip)]
fn parse(_arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
Ok(())
}
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (program, parameters, _) = SetupCommand::output(options)?;
// Get the package name
let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name();
// Start the timer
let start = Instant::now();
let rng = &mut thread_rng();
let program_proof = create_random_proof(program, &parameters, rng).unwrap();
log::info!("Prover completed in {:?} milliseconds", start.elapsed().as_millis());
// Write the proof file to the outputs directory
let mut proof = vec![];
program_proof.write(&mut proof)?;
ProofFile::new(&package_name).write_to(&path, &proof)?;
log::info!("Completed program proving");
Ok(program_proof)
}
}

View File

@ -1,11 +1,10 @@
use crate::{cli::*, cli_types::*};
use crate::commands::SetupCommand;
use crate::commands::{SetupCommand, ProveCommand};
use crate::errors::CLIError;
use crate::{cli::*, cli_types::*};
use snarkos_algorithms::snark::{create_random_proof, verify_proof};
use snarkos_algorithms::snark::verify_proof;
use clap::ArgMatches;
use rand::thread_rng;
use std::time::{Duration, Instant};
#[derive(Debug)]
@ -29,18 +28,11 @@ impl CLI for RunCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<(), CLIError> {
let (circuit, parameters, prepared_verifying_key) = SetupCommand::output(options)?;
let (_program, _parameters, prepared_verifying_key) = SetupCommand::output(options)?;
let proof = ProveCommand::output(options)?;
let rng = &mut thread_rng();
let mut proving = Duration::new(0, 0);
let mut verifying = Duration::new(0, 0);
let start = Instant::now();
let proof = create_random_proof(circuit, &parameters, rng).unwrap();
proving += start.elapsed();
// let _inputs: Vec<_> = [1u32; 1].to_vec();
let start = Instant::now();
@ -50,7 +42,6 @@ impl CLI for RunCommand {
verifying += start.elapsed();
println!(" ");
println!(" Prover time : {:?} milliseconds", proving.as_millis());
println!(
" Verifier time : {:?} milliseconds",
verifying.as_millis()

View File

@ -1,13 +1,20 @@
use crate::{cli::*, cli_types::*};
use crate::commands::BuildCommand;
use crate::errors::CLIError;
use crate::errors::{CLIError, VerificationKeyFileError};
use crate::files::{ProvingKeyFile, VerificationKeyFile};
use crate::manifest::Manifest;
use leo_compiler::compiler::Compiler;
use snarkos_algorithms::snark::{generate_random_parameters, prepare_verifying_key, Parameters, PreparedVerifyingKey};
use snarkos_algorithms::snark::{
generate_random_parameters, prepare_verifying_key, Parameters, PreparedVerifyingKey,
};
use snarkos_curves::bls12_377::{Bls12_377, Fr};
use snarkos_utilities::bytes::ToBytes;
use clap::ArgMatches;
use rand::thread_rng;
use std::convert::TryFrom;
use std::env::current_dir;
use std::time::Instant;
#[derive(Debug)]
@ -15,7 +22,11 @@ pub struct SetupCommand;
impl CLI for SetupCommand {
type Options = ();
type Output = (Compiler<Fr>, Parameters<Bls12_377>, PreparedVerifyingKey<Bls12_377>);
type Output = (
Compiler<Fr>,
Parameters<Bls12_377>,
PreparedVerifyingKey<Bls12_377>,
);
const NAME: NameType = "setup";
const ABOUT: AboutType = "Run a program setup";
@ -31,20 +42,67 @@ impl CLI for SetupCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let circuit = BuildCommand::output(options)?;
let (program, checksum_differs) = BuildCommand::output(options)?;
let start = Instant::now();
// Get the package name
let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name();
let rng = &mut thread_rng();
let parameters = generate_random_parameters::<Bls12_377, _, _>(circuit.clone(), rng).unwrap();
// Check if a proving key and verification key already exists
let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path)
&& VerificationKeyFile::new(&package_name).exists_at(&path);
// If keys do not exist or the checksum differs, run the program setup
if !keys_exist || checksum_differs {
// Start the timer
let start = Instant::now();
// Run the program setup operation
let rng = &mut thread_rng();
let parameters =
generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap();
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
// End the timer
log::info!("Setup completed in {:?} milliseconds", start.elapsed().as_millis());
// TODO (howardwu): Convert parameters to a 'proving key' struct for serialization.
// Write the proving key file to the outputs directory
let mut proving_key = vec![];
parameters.write(&mut proving_key)?;
ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?;
// Write the proving key file to the outputs directory
let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?;
VerificationKeyFile::new(&package_name).write_to(&path, &verification_key)?;
}
// Read the proving key file from the outputs directory
let proving_key = ProvingKeyFile::new(&package_name).read_from(&path)?;
let parameters = Parameters::<Bls12_377>::read(proving_key.as_slice(), true)?;
// Read the proving key file from the outputs directory
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
{
// Load the stored verification key from the outputs directory
let stored_vk = VerificationKeyFile::new(&package_name).read_from(&path)?;
let finish = start.elapsed();
// Convert the prepared_verifying_key to a buffer
let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?;
println!(" ");
println!(" Setup time : {:?} milliseconds", finish.as_millis());
println!(" ");
// Check that the constructed prepared verification key matches the stored verification key
let compare: Vec<(u8, u8)> = verification_key.into_iter().zip(stored_vk.into_iter()).collect();
for (a, b) in compare {
if a != b {
return Err(VerificationKeyFileError::IncorrectVerificationKey.into())
}
}
}
Ok((circuit, parameters, prepared_verifying_key))
log::info!("Completed program setup");
Ok((program, parameters, prepared_verifying_key))
}
}

View File

@ -32,9 +32,9 @@ impl InputsDirectory {
let file_path = file_entry.path();
// Verify that the entry is structured as a valid file
let file_type = file_entry
.file_type()
.map_err(|error| InputsDirectoryError::GettingFileType(file_path.as_os_str().to_owned(), error))?;
let file_type = file_entry.file_type().map_err(|error| {
InputsDirectoryError::GettingFileType(file_path.as_os_str().to_owned(), error)
})?;
if !file_type.is_file() {
return Err(InputsDirectoryError::InvalidFileType(
file_path.as_os_str().to_owned(),
@ -43,9 +43,9 @@ impl InputsDirectory {
}
// Verify that the file has the default file extension
let file_extension = file_path
.extension()
.ok_or_else(|| InputsDirectoryError::GettingFileExtension(file_path.as_os_str().to_owned()))?;
let file_extension = file_path.extension().ok_or_else(|| {
InputsDirectoryError::GettingFileExtension(file_path.as_os_str().to_owned())
})?;
if file_extension != INPUTS_FILE_EXTENSION {
return Err(InputsDirectoryError::InvalidFileExtension(
file_path.as_os_str().to_owned(),

View File

@ -32,9 +32,9 @@ impl SourceDirectory {
let file_path = file_entry.path();
// Verify that the entry is structured as a valid file
let file_type = file_entry
.file_type()
.map_err(|error| SourceDirectoryError::GettingFileType(file_path.as_os_str().to_owned(), error))?;
let file_type = file_entry.file_type().map_err(|error| {
SourceDirectoryError::GettingFileType(file_path.as_os_str().to_owned(), error)
})?;
if !file_type.is_file() {
return Err(SourceDirectoryError::InvalidFileType(
file_path.as_os_str().to_owned(),
@ -43,9 +43,9 @@ impl SourceDirectory {
}
// Verify that the file has the default file extension
let file_extension = file_path
.extension()
.ok_or_else(|| SourceDirectoryError::GettingFileExtension(file_path.as_os_str().to_owned()))?;
let file_extension = file_path.extension().ok_or_else(|| {
SourceDirectoryError::GettingFileExtension(file_path.as_os_str().to_owned())
})?;
if file_extension != SOURCE_FILE_EXTENSION {
return Err(SourceDirectoryError::InvalidFileExtension(
file_path.as_os_str().to_owned(),

View File

@ -2,13 +2,15 @@ use crate::errors::*;
#[derive(Debug, Fail)]
pub enum CLIError {
#[fail(display = "{}", _0)]
BuildError(BuildError),
#[fail(display = "{}: {}", _0, _1)]
Crate(&'static str, String),
#[fail(display = "{}", _0)]
ChecksumFileError(ChecksumFileError),
#[fail(display = "{}", _0)]
InitError(InitError),
@ -27,12 +29,20 @@ pub enum CLIError {
#[fail(display = "{}", _0)]
OutputsDirectoryError(OutputsDirectoryError),
#[fail(display = "{}", _0)]
ProofFileError(ProofFileError),
#[fail(display = "{}", _0)]
ProvingKeyFileError(ProvingKeyFileError),
#[fail(display = "{}", _0)]
RunError(RunError),
#[fail(display = "{}", _0)]
SourceDirectoryError(SourceDirectoryError),
#[fail(display = "{}", _0)]
VerificationKeyFileError(VerificationKeyFileError),
}
impl From<BuildError> for CLIError {
@ -41,6 +51,12 @@ impl From<BuildError> for CLIError {
}
}
impl From<ChecksumFileError> for CLIError {
fn from(error: ChecksumFileError) -> Self {
CLIError::ChecksumFileError(error)
}
}
impl From<InitError> for CLIError {
fn from(error: InitError) -> Self {
CLIError::InitError(error)
@ -77,6 +93,18 @@ impl From<OutputsDirectoryError> for CLIError {
}
}
impl From<ProofFileError> for CLIError {
fn from(error: ProofFileError) -> Self {
CLIError::ProofFileError(error)
}
}
impl From<ProvingKeyFileError> for CLIError {
fn from(error: ProvingKeyFileError) -> Self {
CLIError::ProvingKeyFileError(error)
}
}
impl From<RunError> for CLIError {
fn from(error: RunError) -> Self {
CLIError::RunError(error)
@ -89,6 +117,18 @@ impl From<SourceDirectoryError> for CLIError {
}
}
impl From<VerificationKeyFileError> for CLIError {
fn from(error: VerificationKeyFileError) -> Self {
CLIError::VerificationKeyFileError(error)
}
}
impl From<leo_compiler::errors::CompilerError> for CLIError {
fn from(error: leo_compiler::errors::CompilerError) -> Self {
CLIError::Crate("leo_compiler", format!("{}", error))
}
}
impl From<serde_json::error::Error> for CLIError {
fn from(error: serde_json::error::Error) -> Self {
CLIError::Crate("serde_json", format!("{}", error))

View File

@ -4,13 +4,11 @@ use std::ffi::OsString;
#[derive(Debug, Fail)]
pub enum BuildError {
#[fail(display = "main file {:?} does not exist", _0)]
MainFileDoesNotExist(OsString),
#[fail(display = "{}", _0)]
ManifestError(ManifestError),
}
impl From<ManifestError> for BuildError {

View File

@ -5,7 +5,6 @@ use std::io;
#[derive(Debug, Fail)]
pub enum InitError {
#[fail(display = "root directory {:?} creating: {}", _0, _1)]
CreatingRootDirectory(OsString, io::Error),
@ -20,7 +19,6 @@ pub enum InitError {
#[fail(display = "package name is missing - {:?}", _0)]
ProjectNameInvalid(OsString),
}
impl From<ManifestError> for InitError {

View File

@ -5,7 +5,6 @@ use std::io;
#[derive(Debug, Fail)]
pub enum NewError {
#[fail(display = "root directory {:?} creating: {}", _0, _1)]
CreatingRootDirectory(OsString, io::Error),
@ -20,7 +19,6 @@ pub enum NewError {
#[fail(display = "package name is missing - {:?}", _0)]
ProjectNameInvalid(OsString),
}
impl From<ManifestError> for NewError {

View File

@ -4,13 +4,11 @@ use std::ffi::OsString;
#[derive(Debug, Fail)]
pub enum RunError {
#[fail(display = "main file {:?} does not exist", _0)]
MainFileDoesNotExist(OsString),
#[fail(display = "{}", _0)]
ManifestError(ManifestError),
}
impl From<ManifestError> for RunError {

View File

@ -2,7 +2,6 @@ use std::{ffi::OsString, fs::FileType, io};
#[derive(Debug, Fail)]
pub enum InputsDirectoryError {
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
@ -23,5 +22,4 @@ pub enum InputsDirectoryError {
#[fail(display = "reading: {}", _0)]
Reading(io::Error),
}

View File

@ -2,7 +2,6 @@ use std::{ffi::OsString, fs::FileType, io};
#[derive(Debug, Fail)]
pub enum OutputsDirectoryError {
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
@ -26,5 +25,4 @@ pub enum OutputsDirectoryError {
#[fail(display = "removing: {}", _0)]
Removing(io::Error),
}

View File

@ -2,7 +2,6 @@ use std::{ffi::OsString, fs::FileType, io};
#[derive(Debug, Fail)]
pub enum SourceDirectoryError {
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
@ -23,5 +22,4 @@ pub enum SourceDirectoryError {
#[fail(display = "reading: {}", _0)]
Reading(io::Error),
}

View File

@ -0,0 +1,23 @@
use std::io;
use std::path::PathBuf;
#[derive(Debug, Fail)]
pub enum ChecksumFileError {
#[fail(display = "{}: {}", _0, _1)]
Crate(&'static str, String),
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
#[fail(display = "Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[fail(display = "writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for ChecksumFileError {
fn from(error: std::io::Error) -> Self {
ChecksumFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -2,7 +2,6 @@ use std::io;
#[derive(Debug, Fail)]
pub enum MainFileError {
#[fail(display = "{}: {}", _0, _1)]
Crate(&'static str, String),
@ -11,7 +10,6 @@ pub enum MainFileError {
#[fail(display = "writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for MainFileError {

View File

@ -1,2 +1,14 @@
pub mod checksum;
pub use self::checksum::*;
pub mod main;
pub use self::main::*;
pub mod proof;
pub use self::proof::*;
pub mod proving_key;
pub use self::proving_key::*;
pub mod verification_key;
pub use self::verification_key::*;

23
leo/errors/files/proof.rs Normal file
View File

@ -0,0 +1,23 @@
use std::io;
use std::path::PathBuf;
#[derive(Debug, Fail)]
pub enum ProofFileError {
#[fail(display = "{}: {}", _0, _1)]
Crate(&'static str, String),
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
#[fail(display = "Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[fail(display = "writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for ProofFileError {
fn from(error: std::io::Error) -> Self {
ProofFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -0,0 +1,23 @@
use std::io;
use std::path::PathBuf;
#[derive(Debug, Fail)]
pub enum ProvingKeyFileError {
#[fail(display = "{}: {}", _0, _1)]
Crate(&'static str, String),
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
#[fail(display = "Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[fail(display = "writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for ProvingKeyFileError {
fn from(error: std::io::Error) -> Self {
ProvingKeyFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -0,0 +1,26 @@
use std::io;
use std::path::PathBuf;
#[derive(Debug, Fail)]
pub enum VerificationKeyFileError {
#[fail(display = "{}: {}", _0, _1)]
Crate(&'static str, String),
#[fail(display = "creating: {}", _0)]
Creating(io::Error),
#[fail(display = "Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[fail(display = "Verification key file was corrupted")]
IncorrectVerificationKey,
#[fail(display = "writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for VerificationKeyFileError {
fn from(error: std::io::Error) -> Self {
VerificationKeyFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -2,7 +2,6 @@ use std::io;
#[derive(Debug, Fail)]
pub enum ManifestError {
#[fail(display = "`{}` creating: {}", _0, _1)]
Creating(&'static str, io::Error),
@ -20,5 +19,4 @@ pub enum ManifestError {
#[fail(display = "`{}` writing: {}", _0, _1)]
Writing(&'static str, io::Error),
}

59
leo/files/checksum.rs Normal file
View File

@ -0,0 +1,59 @@
//! The build checksum file.
use crate::directories::outputs::OUTPUTS_DIRECTORY_NAME;
use crate::errors::ChecksumFileError;
use serde::Deserialize;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
pub static CHECKSUM_FILE_EXTENSION: &str = ".leo.checksum";
#[derive(Deserialize)]
pub struct ChecksumFile {
pub package_name: String,
}
impl ChecksumFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &PathBuf) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
/// Reads the checksum from the given file path if it exists.
pub fn read_from(&self, path: &PathBuf) -> Result<String, ChecksumFileError> {
let path = self.setup_file_path(path);
Ok(fs::read_to_string(&path).map_err(|_| ChecksumFileError::FileReadError(path.clone()))?)
}
/// Writes the given checksum to a file.
pub fn write_to(&self, path: &PathBuf, checksum: String) -> Result<(), ChecksumFileError> {
let path = self.setup_file_path(path);
let mut file = File::create(&path)?;
file.write_all(checksum.as_bytes())?;
log::info!("Checksum stored to {:?}", path);
Ok(())
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME));
}
path.push(PathBuf::from(format!("{}{}", self.package_name, CHECKSUM_FILE_EXTENSION)));
}
path
}
}

View File

@ -17,7 +17,9 @@ pub struct MainFile {
impl MainFile {
pub fn new(package_name: &str) -> Self {
Self { package_name: package_name.to_string() }
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(path: &PathBuf) -> bool {
@ -48,11 +50,11 @@ impl MainFile {
format!(
r#"// The '{}' main function.
function main() -> (u32) {{
a = 1 + 1
a = 1 + 1;
return a
}}
"#,
self.package_name
)
}
}
}

View File

@ -1,2 +1,14 @@
pub mod checksum;
pub use self::checksum::*;
pub mod main;
pub use self::main::*;
pub mod proof;
pub use self::proof::*;
pub mod proving_key;
pub use self::proving_key::*;
pub mod verification_key;
pub use self::verification_key::*;

60
leo/files/proof.rs Normal file
View File

@ -0,0 +1,60 @@
//! The proof file.
use crate::directories::outputs::OUTPUTS_DIRECTORY_NAME;
use crate::errors::ProofFileError;
use serde::Deserialize;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
pub static PROOF_FILE_EXTENSION: &str = ".leo.proof";
#[derive(Deserialize)]
pub struct ProofFile {
pub package_name: String,
}
impl ProofFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &PathBuf) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
/// Reads the proof from the given file path if it exists.
pub fn read_from(&self, path: &PathBuf) -> Result<String, ProofFileError> {
let path = self.setup_file_path(path);
let proof = fs::read_to_string(&path).map_err(|_| ProofFileError::FileReadError(path.clone()))?;
Ok(proof)
}
/// Writes the given proof to a file.
pub fn write_to(&self, path: &PathBuf, proof: &[u8]) -> Result<(), ProofFileError> {
let path = self.setup_file_path(path);
let mut file = File::create(&path)?;
file.write_all(proof)?;
log::info!("Proof stored to {:?}", path);
Ok(())
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME));
}
path.push(PathBuf::from(format!("{}{}", self.package_name, PROOF_FILE_EXTENSION)));
}
path
}
}

59
leo/files/proving_key.rs Normal file
View File

@ -0,0 +1,59 @@
//! The proving key file.
use crate::directories::outputs::OUTPUTS_DIRECTORY_NAME;
use crate::errors::ProvingKeyFileError;
use serde::Deserialize;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
pub static PROVING_KEY_FILE_EXTENSION: &str = ".leo.pk";
#[derive(Deserialize)]
pub struct ProvingKeyFile {
pub package_name: String,
}
impl ProvingKeyFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &PathBuf) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
/// Reads the proving key from the given file path if it exists.
pub fn read_from(&self, path: &PathBuf) -> Result<Vec<u8>, ProvingKeyFileError> {
let path = self.setup_file_path(path);
Ok(fs::read(&path).map_err(|_| ProvingKeyFileError::FileReadError(path.clone()))?)
}
/// Writes the given proving key to a file.
pub fn write_to(&self, path: &PathBuf, proving_key: &[u8]) -> Result<(), ProvingKeyFileError> {
let path = self.setup_file_path(path);
let mut file = File::create(&path)?;
file.write_all(proving_key)?;
log::info!("Proving key stored to {:?}", path);
Ok(())
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME));
}
path.push(PathBuf::from(format!("{}{}", self.package_name, PROVING_KEY_FILE_EXTENSION)));
}
path
}
}

View File

@ -0,0 +1,59 @@
//! The verification key file.
use crate::directories::outputs::OUTPUTS_DIRECTORY_NAME;
use crate::errors::VerificationKeyFileError;
use serde::Deserialize;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
pub static VERIFICATION_KEY_FILE_EXTENSION: &str = ".leo.vk";
#[derive(Deserialize)]
pub struct VerificationKeyFile {
pub package_name: String,
}
impl VerificationKeyFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &PathBuf) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
/// Reads the verification key from the given file path if it exists.
pub fn read_from(&self, path: &PathBuf) -> Result<Vec<u8>, VerificationKeyFileError> {
let path = self.setup_file_path(path);
Ok(fs::read(&path).map_err(|_| VerificationKeyFileError::FileReadError(path.clone()))?)
}
/// Writes the given verification key to a file.
pub fn write_to(&self, path: &PathBuf, verification_key: &[u8]) -> Result<(), VerificationKeyFileError> {
let path = self.setup_file_path(path);
let mut file = File::create(&path)?;
file.write_all(verification_key)?;
log::info!("Verification key stored to {:?}", path);
Ok(())
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME));
}
path.push(PathBuf::from(format!("{}{}", self.package_name, VERIFICATION_KEY_FILE_EXTENSION)));
}
path
}
}

View File

@ -14,6 +14,17 @@ fn level_string(level: log::Level) -> colored::ColoredString {
}
}
#[allow(dead_code)]
fn colored_string(level: log::Level, message: &str) -> colored::ColoredString {
match level {
log::Level::Error => message.bold().red(),
log::Level::Warn => message.bold().yellow(),
log::Level::Info => message.bold().blue(),
log::Level::Debug => message.bold().magenta(),
log::Level::Trace => message.bold(),
}
}
/// Initialize logger with custom format and verbosity.
///
/// # Arguments
@ -35,9 +46,9 @@ pub fn init_logger(app_name: &'static str, verbosity: usize) {
writeln!(
buf,
"[{:>5} {:>5}] {}",
"{:>5}{:>5} {}",
level_string(record.level()),
app_name,
colored_string(record.level(), app_name),
record.args().to_string().replace("\n", &padding)
)
})

View File

@ -1,5 +1,5 @@
use leo::{cli::*, commands::*, logger};
use leo::errors::CLIError;
use leo::{cli::*, commands::*, logger};
use clap::{App, AppSettings};
@ -23,6 +23,7 @@ fn main() -> Result<(), CLIError> {
InitCommand::new(),
BuildCommand::new(),
SetupCommand::new(),
ProveCommand::new(),
RunCommand::new(),
])
.set_term_width(0)
@ -34,11 +35,15 @@ fn main() -> Result<(), CLIError> {
("build", Some(arguments)) => {
BuildCommand::output(BuildCommand::parse(arguments)?)?;
Ok(())
},
}
("setup", Some(arguments)) => {
SetupCommand::output(SetupCommand::parse(arguments)?)?;
Ok(())
},
}
("prove", Some(arguments)) => {
ProveCommand::output(ProveCommand::parse(arguments)?)?;
Ok(())
}
("run", Some(arguments)) => RunCommand::output(RunCommand::parse(arguments)?),
_ => unreachable!(),
}

View File

@ -38,14 +38,18 @@ impl Manifest {
path.exists()
}
pub fn get_package_name(&self) -> String {
self.package.name.clone()
}
pub fn write_to(self, path: &PathBuf) -> Result<(), ManifestError> {
let mut path = path.to_owned();
if path.is_dir() {
path.push(PathBuf::from(FILE_NAME_DEFAULT));
}
let mut file =
File::create(&path).map_err(|error| ManifestError::Creating(FILE_NAME_DEFAULT, error))?;
let mut file = File::create(&path)
.map_err(|error| ManifestError::Creating(FILE_NAME_DEFAULT, error))?;
file.write_all(self.template().as_bytes())
.map_err(|error| ManifestError::Writing(FILE_NAME_DEFAULT, error))
}
@ -81,6 +85,7 @@ impl TryFrom<&PathBuf> for Manifest {
file.read_to_string(&mut buffer)
.map_err(|error| ManifestError::Reading(FILE_NAME_DEFAULT, error))?;
Ok(toml::from_str(&buffer).map_err(|error| ManifestError::Parsing(FILE_NAME_DEFAULT, error))?)
Ok(toml::from_str(&buffer)
.map_err(|error| ManifestError::Parsing(FILE_NAME_DEFAULT, error))?)
}
}