mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-09-11 05:45:30 +03:00
commit
bd553714e1
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,7 @@
|
||||
/target
|
||||
**.idea/
|
||||
Leo.toml
|
||||
src/
|
||||
inputs/
|
||||
outputs/
|
||||
*.DS_Store
|
||||
|
453
Cargo.lock
generated
453
Cargo.lock
generated
@ -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"
|
||||
|
@ -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
256
README.md
@ -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
|
||||
```
|
||||
|
@ -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];
|
||||
}
|
@ -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>(¶ms.vk);
|
||||
|
||||
setup += start.elapsed();
|
||||
|
||||
let start = Instant::now();
|
||||
let proof = {
|
||||
let c = Benchmark::new();
|
||||
create_random_proof(c, ¶ms, 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, ¶ms, 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();
|
||||
|
@ -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" }
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, ¶meter.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, ¶meter_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)?)
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
¶meter.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(),
|
||||
¶meter.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(),
|
||||
¶meter.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);
|
||||
}
|
||||
}
|
@ -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,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, ¶meter.variable);
|
||||
let parameter_variable = new_variable_from_variable(scope, ¶meter_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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
260
compiler/src/constraints/function.rs
Normal file
260
compiler/src/constraints/function.rs
Normal 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(())
|
||||
}
|
||||
}
|
113
compiler/src/constraints/import.rs
Normal file
113
compiler/src/constraints/import.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
@ -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, ¶meter.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, ¶meter.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(),
|
||||
)
|
||||
}
|
||||
}
|
272
compiler/src/constraints/integer/integer.rs
Normal file
272
compiler/src/constraints/integer/integer.rs
Normal 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 ¶meter_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, ¶meter.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
|
||||
)))
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
19
compiler/src/constraints/integer/mod.rs
Normal file
19
compiler/src/constraints/integer/mod.rs
Normal 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::*;
|
149
compiler/src/constraints/integer/uint128.rs
Normal file
149
compiler/src/constraints/integer/uint128.rs
Normal 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, ¶meter_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, ¶meter.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,
|
||||
)?)
|
||||
}
|
||||
}
|
149
compiler/src/constraints/integer/uint16.rs
Normal file
149
compiler/src/constraints/integer/uint16.rs
Normal 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, ¶meter_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, ¶meter.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,
|
||||
)?)
|
||||
}
|
||||
}
|
149
compiler/src/constraints/integer/uint32.rs
Normal file
149
compiler/src/constraints/integer/uint32.rs
Normal 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, ¶meter_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, ¶meter.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,
|
||||
)?)
|
||||
}
|
||||
}
|
149
compiler/src/constraints/integer/uint64.rs
Normal file
149
compiler/src/constraints/integer/uint64.rs
Normal 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, ¶meter_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, ¶meter.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,
|
||||
)?)
|
||||
}
|
||||
}
|
149
compiler/src/constraints/integer/uint8.rs
Normal file
149
compiler/src/constraints/integer/uint8.rs
Normal 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, ¶meter_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, ¶meter.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,
|
||||
)?)
|
||||
}
|
||||
}
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
127
compiler/src/constraints/value.rs
Normal file
127
compiler/src/constraints/value.rs
Normal 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)
|
||||
}
|
||||
}
|
64
compiler/src/errors/compiler.rs
Normal file
64
compiler/src/errors/compiler.rs
Normal 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)
|
||||
}
|
||||
}
|
31
compiler/src/errors/constraints/boolean.rs
Normal file
31
compiler/src/errors/constraints/boolean.rs
Normal 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)
|
||||
}
|
||||
}
|
106
compiler/src/errors/constraints/expression.rs
Normal file
106
compiler/src/errors/constraints/expression.rs
Normal 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)
|
||||
}
|
||||
}
|
25
compiler/src/errors/constraints/field_element.rs
Normal file
25
compiler/src/errors/constraints/field_element.rs
Normal 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)
|
||||
}
|
||||
}
|
69
compiler/src/errors/constraints/function.rs
Normal file
69
compiler/src/errors/constraints/function.rs
Normal 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)
|
||||
}
|
||||
}
|
20
compiler/src/errors/constraints/import.rs
Normal file
20
compiler/src/errors/constraints/import.rs
Normal 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))
|
||||
}
|
||||
}
|
34
compiler/src/errors/constraints/integer.rs
Normal file
34
compiler/src/errors/constraints/integer.rs
Normal 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)
|
||||
}
|
||||
}
|
25
compiler/src/errors/constraints/mod.rs
Normal file
25
compiler/src/errors/constraints/mod.rs
Normal 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::*;
|
88
compiler/src/errors/constraints/statement.rs
Normal file
88
compiler/src/errors/constraints/statement.rs
Normal 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)
|
||||
}
|
||||
}
|
34
compiler/src/errors/constraints/value.rs
Normal file
34
compiler/src/errors/constraints/value.rs
Normal 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)
|
||||
}
|
||||
}
|
5
compiler/src/errors/mod.rs
Normal file
5
compiler/src/errors/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub mod compiler;
|
||||
pub use self::compiler::*;
|
||||
|
||||
pub mod constraints;
|
||||
pub use self::constraints::*;
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
72
compiler/src/leo-inputs.pest
Normal file
72
compiler/src/leo-inputs.pest
Normal 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 }
|
@ -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
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
1
compiler/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod u32;
|
3
compiler/tests/u32/1+1.leo
Normal file
3
compiler/tests/u32/1+1.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() -> (u32) {
|
||||
return 1 + 1
|
||||
}
|
3
compiler/tests/u32/1-1.leo
Normal file
3
compiler/tests/u32/1-1.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() -> (u32) {
|
||||
return 1 - 1
|
||||
}
|
4
compiler/tests/u32/1-2.leo
Normal file
4
compiler/tests/u32/1-2.leo
Normal 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
101
compiler/tests/u32/mod.rs
Normal 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());
|
||||
}
|
3
compiler/tests/u32/one.leo
Normal file
3
compiler/tests/u32/one.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() -> (u32) {
|
||||
return 1
|
||||
}
|
3
compiler/tests/u32/zero.leo
Normal file
3
compiler/tests/u32/zero.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() -> (u32) {
|
||||
return 0
|
||||
}
|
13
leo/cli.rs
13
leo/cli.rs
@ -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>>>(),
|
||||
)
|
||||
|
@ -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],
|
||||
);
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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
60
leo/commands/prove.rs
Normal 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, ¶meters, 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)
|
||||
}
|
||||
}
|
@ -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, ¶meters, 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()
|
||||
|
@ -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>(¶meters.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>(¶meters.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))
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
||||
}
|
||||
|
@ -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),
|
||||
|
||||
}
|
||||
|
@ -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),
|
||||
|
||||
}
|
||||
|
23
leo/errors/files/checksum.rs
Normal file
23
leo/errors/files/checksum.rs
Normal 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))
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
23
leo/errors/files/proof.rs
Normal 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))
|
||||
}
|
||||
}
|
23
leo/errors/files/proving_key.rs
Normal file
23
leo/errors/files/proving_key.rs
Normal 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))
|
||||
}
|
||||
}
|
26
leo/errors/files/verification_key.rs
Normal file
26
leo/errors/files/verification_key.rs
Normal 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))
|
||||
}
|
||||
}
|
@ -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
59
leo/files/checksum.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
60
leo/files/proof.rs
Normal 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
59
leo/files/proving_key.rs
Normal 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
|
||||
}
|
||||
}
|
59
leo/files/verification_key.rs
Normal file
59
leo/files/verification_key.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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)
|
||||
)
|
||||
})
|
||||
|
11
leo/main.rs
11
leo/main.rs
@ -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!(),
|
||||
}
|
||||
|
@ -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))?)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user