diff --git a/Cargo.lock b/Cargo.lock index 71b34d3eeb..680523d15e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,30 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" +dependencies = [ + "bitflags", + "cexpr", + "cfg-if", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2 1.0.19", + "quote 1.0.7", + "regex", + "rustc-hash", + "shlex", + "which", +] + [[package]] name = "bitflags" version = "1.2.1" @@ -128,7 +152,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.3", + "generic-array 0.14.4", ] [[package]] @@ -211,6 +235,18 @@ name = "cc" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -220,9 +256,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chrono" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" dependencies = [ "num-integer", "num-traits", @@ -239,6 +275,17 @@ dependencies = [ "envmnt", ] +[[package]] +name = "clang-sys" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "2.33.3" @@ -254,6 +301,15 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + [[package]] name = "colored" version = "2.0.0" @@ -412,6 +468,36 @@ dependencies = [ "memchr", ] +[[package]] +name = "curl" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9447ad28eee2a5cfb031c329d46bef77487244fff6a724b378885b8691a35f78" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "curl-sys" +version = "0.4.34+curl-7.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad4eff0be6985b7e709f64b5a541f700e9ad1407190a29f4884319eb663ed1d6" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi 0.3.9", +] + [[package]] name = "derivative" version = "2.1.1" @@ -420,7 +506,7 @@ checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", ] [[package]] @@ -438,7 +524,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.3", + "generic-array 0.14.4", ] [[package]] @@ -449,9 +535,9 @@ checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" [[package]] name = "either" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" [[package]] name = "encoding_rs" @@ -503,7 +589,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", "synstructure", ] @@ -677,9 +763,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60fb4bb6bba52f78a471264d9a3b7d026cc0af47b22cd2cffbc0b787ca003e63" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", "version_check", @@ -711,6 +797,12 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "h2" version = "0.2.6" @@ -738,9 +830,9 @@ checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" [[package]] name = "hashbrown" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb" +checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" dependencies = [ "autocfg", ] @@ -846,9 +938,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7" +checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" dependencies = [ "autocfg", "hashbrown", @@ -874,6 +966,12 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" + [[package]] name = "iovec" version = "0.1.4" @@ -913,6 +1011,15 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.44" @@ -970,6 +1077,7 @@ dependencies = [ "leo-gadgets", "leo-input", "leo-package", + "leo-state", "log", "notify", "num-bigint", @@ -1014,6 +1122,8 @@ dependencies = [ "leo-ast", "leo-gadgets", "leo-input", + "leo-package", + "leo-state", "leo-typed", "log", "num-bigint", @@ -1077,6 +1187,25 @@ dependencies = [ "zip", ] +[[package]] +name = "leo-state" +version = "0.1.0" +dependencies = [ + "leo-input", + "leo-typed", + "rand", + "rand_xorshift", + "snarkos-algorithms", + "snarkos-curves", + "snarkos-dpc", + "snarkos-errors", + "snarkos-models", + "snarkos-objects", + "snarkos-storage", + "snarkos-utilities", + "thiserror", +] + [[package]] name = "leo-typed" version = "0.1.0" @@ -1097,6 +1226,49 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi 0.3.9", +] + +[[package]] +name = "librocksdb-sys" +version = "6.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883213ae3d09bfc3d104aefe94b25ebb183b6f4d3a515b23b14817e1f4854005" +dependencies = [ + "bindgen", + "cc", + "glob", + "libc", +] + +[[package]] +name = "libz-sys" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca8894883d250240341478bf987467332fbdd5da5c42426c69a8f93dbc302f2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.11" @@ -1242,6 +1414,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0" +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "notify" version = "4.0.15" @@ -1363,6 +1545,38 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +dependencies = [ + "cfg-if", + "cloudabi", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1411,7 +1625,7 @@ dependencies = [ "pest_meta", "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", ] [[package]] @@ -1442,7 +1656,7 @@ checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", ] [[package]] @@ -1682,12 +1896,28 @@ dependencies = [ "winreg", ] +[[package]] +name = "rocksdb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12069b106981c6103d3eab7dd1c86751482d0779a520b7c14954c8b586c1e643" +dependencies = [ + "libc", + "librocksdb-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" @@ -1805,7 +2035,7 @@ checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", ] [[package]] @@ -1856,6 +2086,12 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "single" version = "1.0.0" @@ -1873,14 +2109,14 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" [[package]] name = "snarkos-algorithms" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "blake2", "derivative", @@ -1900,7 +2136,7 @@ dependencies = [ [[package]] name = "snarkos-curves" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "derivative", "rand", @@ -1915,17 +2151,17 @@ dependencies = [ [[package]] name = "snarkos-derives" version = "0.1.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", ] [[package]] name = "snarkos-dpc" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "blake2", "derivative", @@ -1946,20 +2182,23 @@ dependencies = [ [[package]] name = "snarkos-errors" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "base58", "bech32", "bincode", + "curl", "hex", "jsonrpc-core", + "rocksdb", "thiserror", + "toml", ] [[package]] name = "snarkos-gadgets" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "derivative", "digest 0.8.1", @@ -1974,7 +2213,7 @@ dependencies = [ [[package]] name = "snarkos-models" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "bincode", "derivative", @@ -1990,7 +2229,7 @@ dependencies = [ [[package]] name = "snarkos-objects" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "base58", "bech32", @@ -2011,8 +2250,9 @@ dependencies = [ [[package]] name = "snarkos-parameters" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ + "curl", "hex", "snarkos-algorithms", "snarkos-errors", @@ -2023,12 +2263,31 @@ dependencies = [ [[package]] name = "snarkos-profiler" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" + +[[package]] +name = "snarkos-storage" +version = "0.8.0" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" +dependencies = [ + "bincode", + "hex", + "parking_lot", + "rand", + "rocksdb", + "serde", + "snarkos-algorithms", + "snarkos-errors", + "snarkos-models", + "snarkos-objects", + "snarkos-parameters", + "snarkos-utilities", +] [[package]] name = "snarkos-utilities" version = "0.8.0" -source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#a0d7afb4398abc9b2eb7980811b8b9b808df4019" +source = "git+ssh://git@github.com/AleoHQ/snarkOS.git#57fef6a27035419e0b22fee5b72ca6639e15e1ac" dependencies = [ "bincode", "rand", @@ -2073,9 +2332,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.36" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250" +checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", @@ -2090,7 +2349,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", "unicode-xid 0.2.1", ] @@ -2143,7 +2402,7 @@ checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", ] [[package]] @@ -2406,7 +2665,7 @@ dependencies = [ "log", "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", "wasm-bindgen-shared", ] @@ -2440,7 +2699,7 @@ checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556" dependencies = [ "proc-macro2 1.0.19", "quote 1.0.7", - "syn 1.0.36", + "syn 1.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2461,6 +2720,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 6019755eb8..0b3546764f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,13 +13,14 @@ name = "leo" path = "leo/main.rs" [workspace] -members = [ "ast", "compiler", "gadgets", "input", "linter", "package", "typed" ] +members = [ "ast", "compiler", "gadgets", "input", "linter", "package", "typed", "state"] [dependencies] leo-compiler = { path = "compiler", version = "0.1.0" } leo-gadgets = { path = "gadgets", version = "0.1.0" } leo-input = { path = "input", version = "0.1.0" } leo-package = { path = "package", version = "0.1.0" } +leo-state = { path = "state", version = "0.1.0" } snarkos-algorithms = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-algorithms", default-features = false } snarkos-curves = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-curves", default-features = false } diff --git a/ast/src/annotations/annotation_arguments.rs b/ast/src/annotations/annotation_arguments.rs new file mode 100644 index 0000000000..b25859a50b --- /dev/null +++ b/ast/src/annotations/annotation_arguments.rs @@ -0,0 +1,27 @@ +use crate::{ + ast::{span_into_string, Rule}, + SpanDef, +}; + +use pest::Span; +use pest_ast::FromPest; +use serde::Serialize; + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::annotation_arguments))] +pub struct AnnotationArguments<'ast> { + pub arguments: Vec>, + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::annotation_argument))] +pub struct AnnotationArgument<'ast> { + #[pest_ast(outer(with(span_into_string)))] + pub value: String, + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} diff --git a/ast/src/annotations/annotation_name.rs b/ast/src/annotations/annotation_name.rs new file mode 100644 index 0000000000..e5260cda85 --- /dev/null +++ b/ast/src/annotations/annotation_name.rs @@ -0,0 +1,19 @@ +use crate::{ast::Rule, SpanDef}; + +use pest::Span; +use pest_ast::FromPest; +use serde::Serialize; + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::annotation_name))] +pub enum AnnotationName<'ast> { + Context(Context<'ast>), +} + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::context))] +pub struct Context<'ast> { + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} diff --git a/ast/src/annotations/annotation_symbol.rs b/ast/src/annotations/annotation_symbol.rs new file mode 100644 index 0000000000..b5e2b10bf5 --- /dev/null +++ b/ast/src/annotations/annotation_symbol.rs @@ -0,0 +1,13 @@ +use crate::{ast::Rule, SpanDef}; + +use pest::Span; +use pest_ast::FromPest; +use serde::Serialize; + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::annotation_symbol))] +pub struct AnnotationSymbol<'ast> { + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} diff --git a/ast/src/annotations/annotations.rs b/ast/src/annotations/annotations.rs new file mode 100644 index 0000000000..69b01558dd --- /dev/null +++ b/ast/src/annotations/annotations.rs @@ -0,0 +1,20 @@ +use crate::{ + annotations::{AnnotationArguments, AnnotationName, AnnotationSymbol}, + ast::Rule, + SpanDef, +}; + +use pest::Span; +use pest_ast::FromPest; +use serde::Serialize; + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::annotation))] +pub struct Annotation<'ast> { + pub symbol: AnnotationSymbol<'ast>, + pub name: AnnotationName<'ast>, + pub arguments: AnnotationArguments<'ast>, + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} diff --git a/ast/src/annotations/mod.rs b/ast/src/annotations/mod.rs new file mode 100644 index 0000000000..787b15ef19 --- /dev/null +++ b/ast/src/annotations/mod.rs @@ -0,0 +1,11 @@ +pub mod annotations; +pub use annotations::*; + +pub mod annotation_symbol; +pub use annotation_symbol::*; + +pub mod annotation_name; +pub use annotation_name::*; + +pub mod annotation_arguments; +pub use annotation_arguments::*; diff --git a/ast/src/definitions/annotated_definition.rs b/ast/src/definitions/annotated_definition.rs new file mode 100644 index 0000000000..8c3b15f6d8 --- /dev/null +++ b/ast/src/definitions/annotated_definition.rs @@ -0,0 +1,15 @@ +use crate::{annotations::Annotation, ast::Rule, definitions::Definition, SpanDef}; + +use pest::Span; +use pest_ast::FromPest; +use serde::Serialize; + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::definition_annotated))] +pub struct AnnotatedDefinition<'ast> { + pub annotation: Annotation<'ast>, + pub definition: Box>, + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} diff --git a/ast/src/definitions/definition.rs b/ast/src/definitions/definition.rs index 5f643f3cc0..c2858f4535 100644 --- a/ast/src/definitions/definition.rs +++ b/ast/src/definitions/definition.rs @@ -1,6 +1,7 @@ use crate::{ ast::Rule, circuits::Circuit, + definitions::AnnotatedDefinition, functions::{Function, TestFunction}, imports::Import, }; @@ -11,6 +12,7 @@ use serde::Serialize; #[derive(Clone, Debug, FromPest, PartialEq, Serialize)] #[pest_ast(rule(Rule::definition))] pub enum Definition<'ast> { + Annotated(AnnotatedDefinition<'ast>), Import(Import<'ast>), Circuit(Circuit<'ast>), Function(Function<'ast>), diff --git a/ast/src/definitions/mod.rs b/ast/src/definitions/mod.rs index c4cce58786..83cde009a3 100644 --- a/ast/src/definitions/mod.rs +++ b/ast/src/definitions/mod.rs @@ -1,2 +1,5 @@ +pub mod annotated_definition; +pub use annotated_definition::*; + pub mod definition; pub use definition::*; diff --git a/ast/src/leo.pest b/ast/src/leo.pest index 0ce0c2a3ec..70ee089170 100644 --- a/ast/src/leo.pest +++ b/ast/src/leo.pest @@ -8,12 +8,16 @@ file = { SOI ~ NEWLINE* ~ definition* ~ NEWLINE* ~ EOI } // Declared in definitions/definition.rs definition = { - import + definition_annotated + | import | circuit | function | test_function } +// Declared in definitions/annotated_definition.rs +definition_annotated = { annotation ~ NEWLINE* ~ definition} + // Declared in common/identifier.rs identifier = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* } protected_name = { @@ -230,9 +234,20 @@ value_boolean = { "true" | "false" } value_field = ${ value_number ~ type_field } // Declared in values/group_value.rs -value_group = ${ group_single_or_tuple ~ type_group } -group_tuple = !{"(" ~ value_number ~ "," ~ value_number ~ ")"} -group_single_or_tuple = {value_number | group_tuple} +value_group = ${ group_tuple ~ type_group } +group_tuple = !{"(" ~ group_coordinate ~ "," ~ group_coordinate ~ ")"} + +// Declared in values/group_coordinate.rs +group_coordinate = { + value_number + | sign_high + | sign_low + | inferred +} + +sign_high = @{"+"} +sign_low = @{"-"} +inferred = @{"_"} // Declared in values/address.rs address = @{ "aleo1" ~ (LOWERCASE_LETTER | ASCII_DIGIT){58} } @@ -457,3 +472,23 @@ debug = {"debug"} // Declared in macros/error.rs error = {"error"} +/// Annotations + +// Declared in annotations/annotation.rs +annotation = ${annotation_symbol ~ annotation_name ~ annotation_arguments} + +// Declared in annotations/annotation_symbol.rs +annotation_symbol = ${"@"} + +// Declared in annotations/annotation_name.rs +annotation_name = { + context +} + +// Declared in annotations/context.rs +context = {"context"} + +// Declared in annotations/annotation_argument.rs +annotation_arguments = !{"(" ~ NEWLINE* ~ annotation_argument ~ ("," ~ NEWLINE* ~ annotation_argument)* ~ ","? ~ NEWLINE* ~ ")"} + +annotation_argument = @{ (ASCII_ALPHANUMERIC | "_")+ } diff --git a/ast/src/lib.rs b/ast/src/lib.rs index 8585fcec6e..8b11d62b33 100644 --- a/ast/src/lib.rs +++ b/ast/src/lib.rs @@ -8,6 +8,7 @@ extern crate thiserror; mod ast; pub mod access; +pub mod annotations; pub mod circuits; pub mod common; pub mod definitions; diff --git a/ast/src/values/group_coordinate.rs b/ast/src/values/group_coordinate.rs new file mode 100644 index 0000000000..01b7fbda55 --- /dev/null +++ b/ast/src/values/group_coordinate.rs @@ -0,0 +1,61 @@ +use crate::{ast::Rule, values::NumberValue, SpanDef}; + +use pest::Span; +use pest_ast::FromPest; +use serde::Serialize; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::group_coordinate))] +pub enum GroupCoordinate<'ast> { + Number(NumberValue<'ast>), + SignHigh(SignHigh<'ast>), + SignLow(SignLow<'ast>), + Inferred(Inferred<'ast>), +} + +impl<'ast> GroupCoordinate<'ast> { + pub fn span(&self) -> &Span<'ast> { + match self { + GroupCoordinate::Number(number) => &number.span(), + GroupCoordinate::SignHigh(sign_high) => &sign_high.span, + GroupCoordinate::SignLow(sign_low) => &sign_low.span, + GroupCoordinate::Inferred(inferred) => &inferred.span, + } + } +} + +impl<'ast> fmt::Display for GroupCoordinate<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GroupCoordinate::Number(number) => write!(f, "{}", number), + GroupCoordinate::SignHigh(_) => write!(f, "+"), + GroupCoordinate::SignLow(_) => write!(f, "-"), + GroupCoordinate::Inferred(_) => write!(f, "_"), + } + } +} + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::sign_high))] +pub struct SignHigh<'ast> { + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::sign_low))] +pub struct SignLow<'ast> { + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} + +#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] +#[pest_ast(rule(Rule::inferred))] +pub struct Inferred<'ast> { + #[pest_ast(outer())] + #[serde(with = "SpanDef")] + pub span: Span<'ast>, +} diff --git a/ast/src/values/group_value.rs b/ast/src/values/group_value.rs index 606940bd36..ee9310de90 100644 --- a/ast/src/values/group_value.rs +++ b/ast/src/values/group_value.rs @@ -1,4 +1,4 @@ -use crate::{ast::Rule, types::GroupType, values::NumberValue, SpanDef}; +use crate::{ast::Rule, types::GroupType, values::GroupCoordinate, SpanDef}; use pest::Span; use pest_ast::FromPest; @@ -8,8 +8,8 @@ use std::fmt; #[derive(Clone, Debug, FromPest, PartialEq, Serialize)] #[pest_ast(rule(Rule::value_group))] pub struct GroupValue<'ast> { - pub value: GroupRepresentation<'ast>, - pub _type: GroupType, + pub value: GroupTuple<'ast>, + pub type_: GroupType, #[pest_ast(outer())] #[serde(with = "SpanDef")] pub span: Span<'ast>, @@ -21,27 +21,11 @@ impl<'ast> fmt::Display for GroupValue<'ast> { } } -#[derive(Clone, Debug, FromPest, PartialEq, Serialize)] -#[pest_ast(rule(Rule::group_single_or_tuple))] -pub enum GroupRepresentation<'ast> { - Single(NumberValue<'ast>), - Tuple(GroupTuple<'ast>), -} - -impl<'ast> fmt::Display for GroupRepresentation<'ast> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GroupRepresentation::Single(number) => write!(f, "{}", number), - GroupRepresentation::Tuple(tuple) => write!(f, "{}", tuple), - } - } -} - #[derive(Clone, Debug, FromPest, PartialEq, Serialize)] #[pest_ast(rule(Rule::group_tuple))] pub struct GroupTuple<'ast> { - pub x: NumberValue<'ast>, - pub y: NumberValue<'ast>, + pub x: GroupCoordinate<'ast>, + pub y: GroupCoordinate<'ast>, #[pest_ast(outer())] #[serde(with = "SpanDef")] pub span: Span<'ast>, diff --git a/ast/src/values/mod.rs b/ast/src/values/mod.rs index e9015180d4..e1ca123f27 100644 --- a/ast/src/values/mod.rs +++ b/ast/src/values/mod.rs @@ -10,6 +10,9 @@ pub use boolean_value::*; pub mod field_value; pub use field_value::*; +pub mod group_coordinate; +pub use group_coordinate::*; + pub mod group_value; pub use group_value::*; diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 35bf31e318..db5ff685ac 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -8,7 +8,9 @@ edition = "2018" leo-ast = { path = "../ast", version = "0.1.0" } leo-gadgets = { path = "../gadgets", version = "0.1.0" } leo-input = { path = "../input", version = "0.1.0" } +leo-package = { path = "../package", version = "0.1.0"} leo-typed = { path = "../typed", version = "0.1.0" } +leo-state = { path = "../state", version = "0.1.0" } snarkos-curves = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-curves", default-features = false } snarkos-dpc = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-dpc", default-features = false } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index bcde2061c4..01e68e8aff 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -10,8 +10,11 @@ use crate::{ }; use leo_ast::LeoAst; use leo_input::LeoInputParser; +use leo_package::inputs::InputPairs; +use leo_state::verify_local_data_commitment; use leo_typed::{Input, LeoTypedAst, MainInput, Program}; +use snarkos_dpc::{base_dpc::instantiated::Components, SystemParameters}; use snarkos_errors::gadgets::SynthesisError; use snarkos_models::{ curves::{Field, PrimeField}, @@ -124,6 +127,16 @@ impl> Compiler { self.program_input.set_main_input(input); } + /// Verifies the input to the program + pub fn verify_local_data_commitment( + &self, + system_parameters: &SystemParameters, + ) -> Result { + let result = verify_local_data_commitment(system_parameters, &self.program_input)?; + + Ok(result) + } + pub fn checksum(&self) -> Result { // Read in the main file as string let unparsed_file = fs::read_to_string(&self.main_file_path) @@ -151,8 +164,13 @@ impl> Compiler { } /// Synthesizes the circuit for test functions with program input. - pub fn compile_test_constraints(self) -> Result<(), CompilerError> { - generate_test_constraints::(self.program, self.program_input, &self.imported_programs) + pub fn compile_test_constraints(self, input_pairs: InputPairs) -> Result<(), CompilerError> { + generate_test_constraints::( + self.program, + input_pairs, + &self.imported_programs, + &self.output_directory, + ) } /// Calls the internal generate_constraints method with arguments @@ -204,6 +222,9 @@ impl> ConstraintSynthesizer for Compil // Write results to file let output_file = OutputFile::new(&package_name); + + log::info!("Writing to output registers..."); + output_file.write(&output_directory, result.bytes()).unwrap(); Ok(()) diff --git a/compiler/src/constraints/constraints.rs b/compiler/src/constraints/constraints.rs index ba4aaec894..5880bdadc7 100644 --- a/compiler/src/constraints/constraints.rs +++ b/compiler/src/constraints/constraints.rs @@ -8,13 +8,17 @@ use crate::{ GroupType, ImportParser, OutputBytes, + OutputFile, }; use leo_typed::{Input, Program}; +use leo_input::LeoInputParser; +use leo_package::inputs::InputPairs; use snarkos_models::{ curves::{Field, PrimeField}, gadgets::r1cs::{ConstraintSystem, TestConstraintSystem}, }; +use std::path::PathBuf; pub fn generate_constraints, CS: ConstraintSystem>( cs: &mut CS, @@ -43,27 +47,61 @@ pub fn generate_constraints, CS: Constrai pub fn generate_test_constraints>( program: Program, - input: Input, + input: InputPairs, imported_programs: &ImportParser, + output_directory: &PathBuf, ) -> Result<(), CompilerError> { let mut resolved_program = ConstrainedProgram::::new(); let program_name = program.get_name(); let tests = program.tests.clone(); + // Store definitions resolved_program.store_definitions(program, imported_programs)?; + // Get default input + let default = input.pairs.get(&program_name); + log::info!("Running {} tests", tests.len()); - for (test_name, test_function) in tests.into_iter() { + for (test_name, test) in tests.into_iter() { let cs = &mut TestConstraintSystem::::new(); let full_test_name = format!("{}::{}", program_name.clone(), test_name.to_string()); + let mut output_file_name = program_name.clone(); - let result = resolved_program.enforce_main_function( + // get input file name from annotation or use test_name + let input_pair = match test.input_file { + Some(file_id) => { + let file_name = file_id.name; + + output_file_name = file_name.clone(); + + match input.pairs.get(&file_name) { + Some(pair) => pair.to_owned(), + None => return Err(CompilerError::InvalidTestContext(file_name)), + } + } + None => default.ok_or(CompilerError::NoTestInput)?, + }; + + // parse input files to abstract syntax trees + let input_file = &input_pair.input_file; + let state_file = &input_pair.state_file; + + let input_ast = LeoInputParser::parse_file(input_file)?; + let state_ast = LeoInputParser::parse_file(state_file)?; + + // parse input files into input struct + let mut input = Input::new(); + input.parse_input(input_ast)?; + input.parse_state(state_ast)?; + + // run test function on new program with input + let result = resolved_program.clone().enforce_main_function( cs, program_name.clone(), - test_function.0, - input.clone(), // pass program input into every test + test.function, + input, // pass program input into every test ); if result.is_ok() { @@ -72,6 +110,14 @@ pub fn generate_test_constraints>( full_test_name, cs.is_satisfied() ); + + // write result to file + let output = result?; + let output_file = OutputFile::new(&output_file_name); + + log::info!("\tWriting output to registers in `{}.out` ...", output_file_name); + + output_file.write(output_directory, output.bytes()).unwrap(); } else { log::error!("test {} errored: {}", full_test_name, result.unwrap_err()); } diff --git a/compiler/src/errors/compiler.rs b/compiler/src/errors/compiler.rs index ff76268520..fbb66b9f2f 100644 --- a/compiler/src/errors/compiler.rs +++ b/compiler/src/errors/compiler.rs @@ -1,6 +1,7 @@ use crate::errors::{FunctionError, ImportError, OutputBytesError, OutputFileError}; use leo_ast::ParserError; use leo_input::InputParserError; +use leo_state::LocalDataVerificationError; use bincode::Error as SerdeError; use std::path::PathBuf; @@ -13,18 +14,27 @@ pub enum CompilerError { #[error("{}", _0)] InputParserError(#[from] InputParserError), + #[error("Cannot find input files with context name `{}`", _0)] + InvalidTestContext(String), + #[error("{}", _0)] FunctionError(#[from] FunctionError), #[error("Cannot read from the provided file path - {:?}", _0)] FileReadError(PathBuf), + #[error("{}", _0)] + LocalDataVerificationError(#[from] LocalDataVerificationError), + #[error("`main` function not found")] NoMain, #[error("`main` must be a function")] NoMainFunction, + #[error("Failed to find input files for the current test")] + NoTestInput, + #[error("{}", _0)] OutputError(#[from] OutputFileError), diff --git a/compiler/src/errors/value/group.rs b/compiler/src/errors/value/group.rs index 2f4086522f..d09b6aefec 100644 --- a/compiler/src/errors/value/group.rs +++ b/compiler/src/errors/value/group.rs @@ -52,4 +52,34 @@ impl GroupError { Self::new_from_span(message, span) } + + pub fn x_invalid(x: String, span: Span) -> Self { + let message = format!("invalid x coordinate `{}`", x); + + Self::new_from_span(message, span) + } + + pub fn y_invalid(y: String, span: Span) -> Self { + let message = format!("invalid y coordinate `{}`", y); + + Self::new_from_span(message, span) + } + + pub fn not_on_curve(element: String, span: Span) -> Self { + let message = format!("group element `{}` is not on the supported curve", element); + + Self::new_from_span(message, span) + } + + pub fn x_recover(span: Span) -> Self { + let message = format!("could not recover group element from x coordinate"); + + Self::new_from_span(message, span) + } + + pub fn y_recover(span: Span) -> Self { + let message = format!("could not recover group element from y coordinate"); + + Self::new_from_span(message, span) + } } diff --git a/compiler/src/errors/value/value.rs b/compiler/src/errors/value/value.rs index af47546962..5c824f40ec 100644 --- a/compiler/src/errors/value/value.rs +++ b/compiler/src/errors/value/value.rs @@ -45,4 +45,10 @@ impl ValueError { Self::new_from_span(message, span) } + + pub fn implicit_group(span: Span) -> Self { + let message = format!("group coordinates should be in (x, y)group format"); + + Self::new_from_span(message, span) + } } diff --git a/compiler/src/expression/expression.rs b/compiler/src/expression/expression.rs index 3aaca3e829..e8b2ebaef2 100644 --- a/compiler/src/expression/expression.rs +++ b/compiler/src/expression/expression.rs @@ -38,7 +38,7 @@ impl> ConstrainedProgram { Expression::Address(address, span) => Ok(ConstrainedValue::Address(Address::new(address, span)?)), Expression::Boolean(boolean, span) => Ok(ConstrainedValue::Boolean(new_bool_constant(boolean, span)?)), Expression::Field(field, span) => Ok(ConstrainedValue::Field(FieldType::constant(field, span)?)), - Expression::Group(group_affine, span) => Ok(ConstrainedValue::Group(G::constant(group_affine, span)?)), + Expression::Group(group_affine) => Ok(ConstrainedValue::Group(G::constant(group_affine)?)), Expression::Implicit(value, span) => Ok(enforce_number_implicit(expected_type, value, span)?), Expression::Integer(type_, integer, span) => { Ok(ConstrainedValue::Integer(Integer::new_constant(&type_, integer, span)?)) diff --git a/compiler/src/output/output_file.rs b/compiler/src/output/output_file.rs index d60f60321d..b495422919 100644 --- a/compiler/src/output/output_file.rs +++ b/compiler/src/output/output_file.rs @@ -40,7 +40,6 @@ impl OutputFile { // create output file let path = self.setup_file_path(path); let mut file = File::create(&path)?; - log::info!("Writing to output registers..."); Ok(file.write_all(bytes)?) } diff --git a/compiler/src/program/program.rs b/compiler/src/program/program.rs index c8f97cf0ae..9aa4d85657 100644 --- a/compiler/src/program/program.rs +++ b/compiler/src/program/program.rs @@ -6,6 +6,7 @@ use snarkos_models::curves::{Field, PrimeField}; use std::collections::HashMap; +#[derive(Clone)] pub struct ConstrainedProgram> { pub identifiers: HashMap>, } diff --git a/compiler/src/value/group/group_type.rs b/compiler/src/value/group/group_type.rs index 71cbdad435..fc7534e2ed 100644 --- a/compiler/src/value/group/group_type.rs +++ b/compiler/src/value/group/group_type.rs @@ -1,7 +1,7 @@ //! A data type that represents members in the group formed by the set of affine points on a curve. use crate::errors::GroupError; -use leo_typed::Span; +use leo_typed::{GroupValue, Span}; use snarkos_models::{ curves::{Field, One}, @@ -27,12 +27,12 @@ pub trait GroupType: + EvaluateEqGadget + EqGadget + ConditionalEqGadget - + AllocGadget + + AllocGadget + CondSelectGadget + ToBitsGadget + ToBytesGadget { - fn constant(string: String, span: Span) -> Result; + fn constant(value: GroupValue) -> Result; fn to_allocated>(&self, cs: CS, span: Span) -> Result; diff --git a/compiler/src/value/group/input.rs b/compiler/src/value/group/input.rs index 4f0e44017f..a5df8f4112 100644 --- a/compiler/src/value/group/input.rs +++ b/compiler/src/value/group/input.rs @@ -1,7 +1,7 @@ //! Methods to enforce constraints on input group values in a Leo program. use crate::{errors::GroupError, ConstrainedValue, GroupType}; -use leo_typed::{InputValue, Span}; +use leo_typed::{GroupValue, InputValue, Span}; use snarkos_errors::gadgets::SynthesisError; use snarkos_models::{ @@ -12,7 +12,7 @@ use snarkos_models::{ pub(crate) fn allocate_group, CS: ConstraintSystem>( cs: &mut CS, name: String, - option: Option, + option: Option, span: Span, ) -> Result { let group_name = format!("{}: group", name); diff --git a/compiler/src/value/group/targets/edwards_bls12.rs b/compiler/src/value/group/targets/edwards_bls12.rs index 0b9fadd7ca..a6e20a4769 100644 --- a/compiler/src/value/group/targets/edwards_bls12.rs +++ b/compiler/src/value/group/targets/edwards_bls12.rs @@ -1,5 +1,5 @@ use crate::{errors::GroupError, GroupType}; -use leo_typed::Span; +use leo_typed::{GroupCoordinate, GroupValue, Span}; use snarkos_curves::{ edwards_bls12::{EdwardsAffine, EdwardsParameters, Fq}, @@ -36,14 +36,8 @@ pub enum EdwardsGroupType { } impl GroupType for EdwardsGroupType { - fn constant(string: String, span: Span) -> Result { - // 1group = generator - if string.eq("1") { - return Ok(Self::one()); - } - - let value = - Self::edwards_affine_from_str(string.clone()).map_err(|_| GroupError::invalid_group(string, span))?; + fn constant(group: GroupValue) -> Result { + let value = Self::edwards_affine_from_value(group)?; Ok(EdwardsGroupType::Constant(value)) } @@ -124,10 +118,57 @@ impl GroupType for EdwardsGroupType { } impl EdwardsGroupType { - pub fn edwards_affine_from_str(string: String) -> Result { - // x or (x, y) - match Fq::from_str(&string).ok() { - Some(x) => { + pub fn edwards_affine_from_value(group: GroupValue) -> Result { + let span = group.span; + let x = group.x; + let y = group.y; + + match (x, y) { + // (x, y) + (GroupCoordinate::Number(x_string, x_span), GroupCoordinate::Number(y_string, y_span)) => { + Self::edwards_affine_from_pair(x_string, y_string, x_span, y_span, span) + } + // (x, +) + (GroupCoordinate::Number(x_string, x_span), GroupCoordinate::SignHigh) => { + Self::edwards_affine_from_x_str(x_string, x_span, Some(true), span) + } + // (x, -) + (GroupCoordinate::Number(x_string, x_span), GroupCoordinate::SignLow) => { + Self::edwards_affine_from_x_str(x_string, x_span, Some(false), span) + } + // (x, _) + (GroupCoordinate::Number(x_string, x_span), GroupCoordinate::Inferred) => { + Self::edwards_affine_from_x_str(x_string, x_span, None, span) + } + // (+, y) + (GroupCoordinate::SignHigh, GroupCoordinate::Number(y_string, y_span)) => { + Self::edwards_affine_from_y_str(y_string, y_span, Some(true), span) + } + // (-, y) + (GroupCoordinate::SignLow, GroupCoordinate::Number(y_string, y_span)) => { + Self::edwards_affine_from_y_str(y_string, y_span, Some(false), span) + } + // (_, y) + (GroupCoordinate::Inferred, GroupCoordinate::Number(y_string, y_span)) => { + Self::edwards_affine_from_y_str(y_string, y_span, None, span) + } + // Invalid + (x, y) => Err(GroupError::invalid_group(format!("({}, {})", x, y), span)), + } + } + + pub fn edwards_affine_from_x_str( + x_string: String, + x_span: Span, + greatest: Option, + element_span: Span, + ) -> Result { + let x = Fq::from_str(&x_string).map_err(|_| GroupError::x_invalid(x_string, x_span))?; + match greatest { + // Sign provided + Some(greatest) => EdwardsAffine::from_x_coordinate(x, greatest).ok_or(GroupError::x_recover(element_span)), + // Sign inferred + None => { // Attempt to recover with a sign_low bit. if let Some(element) = EdwardsAffine::from_x_coordinate(x.clone(), false) { return Ok(element); @@ -139,29 +180,71 @@ impl EdwardsGroupType { } // Otherwise return error. - Err(SynthesisError::AssignmentMissing) + Err(GroupError::x_recover(element_span)) } - None => EdwardsAffine::from_str(&string).map_err(|_| SynthesisError::AssignmentMissing), } } - pub fn alloc_x_helper Result, T: Borrow>( + pub fn edwards_affine_from_y_str( + y_string: String, + y_span: Span, + greatest: Option, + element_span: Span, + ) -> Result { + let y = Fq::from_str(&y_string).map_err(|_| GroupError::y_invalid(y_string, y_span))?; + + match greatest { + // Sign provided + Some(greatest) => EdwardsAffine::from_y_coordinate(y, greatest).ok_or(GroupError::y_recover(element_span)), + // Sign inferred + None => { + // Attempt to recover with a sign_low bit. + if let Some(element) = EdwardsAffine::from_y_coordinate(y.clone(), false) { + return Ok(element); + } + + // Attempt to recover with a sign_high bit. + if let Some(element) = EdwardsAffine::from_y_coordinate(y, true) { + return Ok(element); + } + + // Otherwise return error. + Err(GroupError::y_recover(element_span)) + } + } + } + + pub fn edwards_affine_from_pair( + x_string: String, + y_string: String, + x_span: Span, + y_span: Span, + element_span: Span, + ) -> Result { + let x = Fq::from_str(&x_string).map_err(|_| GroupError::x_invalid(x_string, x_span))?; + let y = Fq::from_str(&y_string).map_err(|_| GroupError::y_invalid(y_string, y_span))?; + + let element = EdwardsAffine::new(x, y); + + if element.is_on_curve() { + Ok(element) + } else { + Err(GroupError::not_on_curve(format!("{}", element), element_span)) + } + } + + pub fn alloc_helper Result, T: Borrow>( value_gen: Fn, ) -> Result { - let affine_string = match value_gen() { + let group_value = match value_gen() { Ok(value) => { - let string_value = value.borrow().clone(); - Ok(string_value) + let group_value = value.borrow().clone(); + Ok(group_value) } _ => Err(SynthesisError::AssignmentMissing), }?; - // 1group = generator - if affine_string.eq("1") { - Ok(edwards_affine_one()) - } else { - Self::edwards_affine_from_str(affine_string) - } + Self::edwards_affine_from_value(group_value).map_err(|_| SynthesisError::AssignmentMissing) } pub fn allocated>(&self, mut cs: CS) -> Result { @@ -189,24 +272,24 @@ impl EdwardsGroupType { } } -impl AllocGadget for EdwardsGroupType { - fn alloc Result, T: Borrow, CS: ConstraintSystem>( +impl AllocGadget for EdwardsGroupType { + fn alloc Result, T: Borrow, CS: ConstraintSystem>( cs: CS, value_gen: Fn, ) -> Result { let value = , Fq>>::alloc(cs, || { - Self::alloc_x_helper(value_gen) + Self::alloc_helper(value_gen) })?; Ok(EdwardsGroupType::Allocated(value)) } - fn alloc_input Result, T: Borrow, CS: ConstraintSystem>( + fn alloc_input Result, T: Borrow, CS: ConstraintSystem>( cs: CS, value_gen: Fn, ) -> Result { let value = , Fq>>::alloc_input(cs, || { - Self::alloc_x_helper(value_gen) + Self::alloc_helper(value_gen) })?; Ok(EdwardsGroupType::Allocated(value)) diff --git a/compiler/src/value/value.rs b/compiler/src/value/value.rs index 1f96127f33..2fc3c0053a 100644 --- a/compiler/src/value/value.rs +++ b/compiler/src/value/value.rs @@ -69,7 +69,7 @@ impl> ConstrainedValue { Type::Address => Ok(ConstrainedValue::Address(Address::new(value, span)?)), Type::Boolean => Ok(ConstrainedValue::Boolean(new_bool_constant(value, span)?)), Type::Field => Ok(ConstrainedValue::Field(FieldType::constant(value, span)?)), - Type::Group => Ok(ConstrainedValue::Group(G::constant(value, span)?)), + Type::Group => Err(ValueError::implicit_group(span)), Type::IntegerType(integer_type) => Ok(ConstrainedValue::Integer(Integer::new_constant( integer_type, value, diff --git a/compiler/tests/group/double_high.leo b/compiler/tests/group/double_high.leo new file mode 100644 index 0000000000..a8217d6d40 --- /dev/null +++ b/compiler/tests/group/double_high.leo @@ -0,0 +1,3 @@ +function main() { + let element = (+, +)group; +} \ No newline at end of file diff --git a/compiler/tests/group/double_inferred.leo b/compiler/tests/group/double_inferred.leo new file mode 100644 index 0000000000..b3b8b512c3 --- /dev/null +++ b/compiler/tests/group/double_inferred.leo @@ -0,0 +1,3 @@ +function main() { + let element = (_, _)group; +} \ No newline at end of file diff --git a/compiler/tests/group/double_low.leo b/compiler/tests/group/double_low.leo new file mode 100644 index 0000000000..0519c6d17d --- /dev/null +++ b/compiler/tests/group/double_low.leo @@ -0,0 +1,3 @@ +function main() { + let element = (-, -)group; +} \ No newline at end of file diff --git a/compiler/tests/group/input/invalid.in b/compiler/tests/group/input/invalid.in new file mode 100644 index 0000000000..d55c096738 --- /dev/null +++ b/compiler/tests/group/input/invalid.in @@ -0,0 +1,3 @@ +[main] +a: group = (1, 0)group; +b: group = (+, +)group; diff --git a/compiler/tests/group/input/one_one.in b/compiler/tests/group/input/one_one.in deleted file mode 100644 index 27d529509f..0000000000 --- a/compiler/tests/group/input/one_one.in +++ /dev/null @@ -1,3 +0,0 @@ -[main] -a: group = 1; -b: group = 1; diff --git a/compiler/tests/group/input/one_zero.in b/compiler/tests/group/input/one_zero.in deleted file mode 100644 index 98bcfb999d..0000000000 --- a/compiler/tests/group/input/one_zero.in +++ /dev/null @@ -1,3 +0,0 @@ -[main] -a: group = 1; -b: group = 0; diff --git a/compiler/tests/group/input/valid.in b/compiler/tests/group/input/valid.in new file mode 100644 index 0000000000..651a28bea6 --- /dev/null +++ b/compiler/tests/group/input/valid.in @@ -0,0 +1,3 @@ +[main] +a: group = (0, -)group; +b: group = (0, _)group; diff --git a/compiler/tests/group/mod.rs b/compiler/tests/group/mod.rs index b10e6b0842..676059271f 100644 --- a/compiler/tests/group/mod.rs +++ b/compiler/tests/group/mod.rs @@ -1,39 +1,37 @@ use crate::{ assert_satisfied, + expect_compiler_error, expect_synthesis_error, field::field_to_decimal_string, generate_main_input, parse_program, parse_program_with_input, }; -use leo_typed::InputValue; +use leo_typed::{GroupCoordinate, GroupValue, InputValue, Span}; use snarkos_curves::edwards_bls12::EdwardsAffine; use rand::{Rng, SeedableRng}; use rand_xorshift::XorShiftRng; -pub fn group_to_decimal_string(g: EdwardsAffine) -> String { +pub fn group_element_to_input_value(g: EdwardsAffine) -> GroupValue { let x = field_to_decimal_string(g.x); let y = field_to_decimal_string(g.y); - format!("({}, {})", x, y) -} + format!("({}, {})", x, y); -#[test] -fn test_zero() { - let bytes = include_bytes!("zero.leo"); - let program = parse_program(bytes).unwrap(); + let fake_span = Span { + text: "".to_string(), + line: 0, + start: 0, + end: 0, + }; - assert_satisfied(program); -} - -#[test] -fn test_one() { - let bytes = include_bytes!("one.leo"); - let program = parse_program(bytes).unwrap(); - - assert_satisfied(program) + GroupValue { + x: GroupCoordinate::Number(x, fake_span.clone()), + y: GroupCoordinate::Number(y, fake_span.clone()), + span: fake_span, + } } #[test] @@ -44,6 +42,81 @@ fn test_point() { assert_satisfied(program); } +#[test] +fn test_x_sign_high() { + let bytes = include_bytes!("x_sign_high.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_x_sign_low() { + let bytes = include_bytes!("x_sign_low.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_x_sign_inferred() { + let bytes = include_bytes!("x_sign_inferred.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_y_sign_high() { + let bytes = include_bytes!("y_sign_high.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_y_sign_low() { + let bytes = include_bytes!("y_sign_low.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_y_sign_inferred() { + let bytes = include_bytes!("y_sign_inferred.leo"); + let program = parse_program(bytes).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_double_high() { + let bytes = include_bytes!("double_high.leo"); + + let program = parse_program(bytes).unwrap(); + + expect_compiler_error(program); +} + +#[test] +fn test_double_low() { + let bytes = include_bytes!("double_low.leo"); + + let program = parse_program(bytes).unwrap(); + + expect_compiler_error(program); +} + +#[test] +fn test_double_inferred() { + let bytes = include_bytes!("double_inferred.leo"); + + let program = parse_program(bytes).unwrap(); + + expect_compiler_error(program); +} + #[test] fn test_point_input() { let program_bytes = include_bytes!("point_input.leo"); @@ -57,8 +130,8 @@ fn test_point_input() { #[test] fn test_input() { let program_bytes = include_bytes!("input.leo"); - let input_bytes_pass = include_bytes!("input/one_one.in"); - let input_bytes_fail = include_bytes!("input/one_zero.in"); + let input_bytes_pass = include_bytes!("input/valid.in"); + let input_bytes_fail = include_bytes!("input/invalid.in"); let program = parse_program_with_input(program_bytes, input_bytes_pass).unwrap(); @@ -66,7 +139,7 @@ fn test_input() { let program = parse_program_with_input(program_bytes, input_bytes_fail).unwrap(); - expect_synthesis_error(program); + expect_compiler_error(program); } #[test] @@ -79,15 +152,15 @@ fn test_negate() { let a: EdwardsAffine = rng.gen(); let b = a.neg(); - let a_string = group_to_decimal_string(a); - let b_string = group_to_decimal_string(b); + let a_element = group_element_to_input_value(a); + let b_element = group_element_to_input_value(b); let bytes = include_bytes!("negate.leo"); let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string))), - ("b", Some(InputValue::Group(b_string))), + ("a", Some(InputValue::Group(a_element))), + ("b", Some(InputValue::Group(b_element))), ]); program.set_main_input(main_input); @@ -106,17 +179,17 @@ fn test_add() { let b: EdwardsAffine = rng.gen(); let c = a.add(&b); - let a_string = group_to_decimal_string(a); - let b_string = group_to_decimal_string(b); - let c_string = group_to_decimal_string(c); + let a_element = group_element_to_input_value(a); + let b_element = group_element_to_input_value(b); + let c_element = group_element_to_input_value(c); let bytes = include_bytes!("add.leo"); let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string))), - ("b", Some(InputValue::Group(b_string))), - ("c", Some(InputValue::Group(c_string))), + ("a", Some(InputValue::Group(a_element))), + ("b", Some(InputValue::Group(b_element))), + ("c", Some(InputValue::Group(c_element))), ]); program.set_main_input(main_input); @@ -135,17 +208,17 @@ fn test_sub() { let b: EdwardsAffine = rng.gen(); let c = a.sub(&b); - let a_string = group_to_decimal_string(a); - let b_string = group_to_decimal_string(b); - let c_string = group_to_decimal_string(c); + let a_element = group_element_to_input_value(a); + let b_element = group_element_to_input_value(b); + let c_element = group_element_to_input_value(c); let bytes = include_bytes!("sub.leo"); let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string))), - ("b", Some(InputValue::Group(b_string))), - ("c", Some(InputValue::Group(c_string))), + ("a", Some(InputValue::Group(a_element))), + ("b", Some(InputValue::Group(b_element))), + ("c", Some(InputValue::Group(c_element))), ]); program.set_main_input(main_input); @@ -160,14 +233,14 @@ fn test_assert_eq_pass() { for _ in 0..10 { let a: EdwardsAffine = rng.gen(); - let a_string = group_to_decimal_string(a); + let a_element = group_element_to_input_value(a); let bytes = include_bytes!("assert_eq.leo"); let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string.clone()))), - ("b", Some(InputValue::Group(a_string))), + ("a", Some(InputValue::Group(a_element.clone()))), + ("b", Some(InputValue::Group(a_element))), ]); program.set_main_input(main_input); @@ -188,15 +261,15 @@ fn test_assert_eq_fail() { continue; } - let a_string = group_to_decimal_string(a); - let b_string = group_to_decimal_string(b); + let a_element = group_element_to_input_value(a); + let b_element = group_element_to_input_value(b); let bytes = include_bytes!("assert_eq.leo"); let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string))), - ("b", Some(InputValue::Group(b_string))), + ("a", Some(InputValue::Group(a_element))), + ("b", Some(InputValue::Group(b_element))), ]); program.set_main_input(main_input); @@ -214,8 +287,8 @@ fn test_eq() { let a: EdwardsAffine = rng.gen(); let b: EdwardsAffine = rng.gen(); - let a_string = group_to_decimal_string(a); - let b_string = group_to_decimal_string(b); + let a_element = group_element_to_input_value(a); + let b_element = group_element_to_input_value(b); // test equal @@ -223,8 +296,8 @@ fn test_eq() { let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string.clone()))), - ("b", Some(InputValue::Group(a_string.clone()))), + ("a", Some(InputValue::Group(a_element.clone()))), + ("b", Some(InputValue::Group(a_element.clone()))), ("c", Some(InputValue::Boolean(true))), ]); @@ -239,8 +312,8 @@ fn test_eq() { let mut program = parse_program(bytes).unwrap(); let main_input = generate_main_input(vec![ - ("a", Some(InputValue::Group(a_string))), - ("b", Some(InputValue::Group(b_string))), + ("a", Some(InputValue::Group(a_element))), + ("b", Some(InputValue::Group(b_element))), ("c", Some(InputValue::Boolean(c))), ]); @@ -257,8 +330,8 @@ fn test_ternary() { let a: EdwardsAffine = rng.gen(); let b: EdwardsAffine = rng.gen(); - let a_string = group_to_decimal_string(a); - let b_string = group_to_decimal_string(b); + let a_element = group_element_to_input_value(a); + let b_element = group_element_to_input_value(b); let bytes = include_bytes!("ternary.leo"); let mut program = parse_program(bytes).unwrap(); @@ -266,9 +339,9 @@ fn test_ternary() { // true -> field a let main_input = generate_main_input(vec![ ("s", Some(InputValue::Boolean(true))), - ("a", Some(InputValue::Group(a_string.clone()))), - ("b", Some(InputValue::Group(b_string.clone()))), - ("c", Some(InputValue::Group(a_string.clone()))), + ("a", Some(InputValue::Group(a_element.clone()))), + ("b", Some(InputValue::Group(b_element.clone()))), + ("c", Some(InputValue::Group(a_element.clone()))), ]); program.set_main_input(main_input); @@ -280,9 +353,9 @@ fn test_ternary() { // false -> field b let main_input = generate_main_input(vec![ ("s", Some(InputValue::Boolean(false))), - ("a", Some(InputValue::Group(a_string))), - ("b", Some(InputValue::Group(b_string.clone()))), - ("c", Some(InputValue::Group(b_string))), + ("a", Some(InputValue::Group(a_element))), + ("b", Some(InputValue::Group(b_element.clone()))), + ("c", Some(InputValue::Group(b_element))), ]); program.set_main_input(main_input); diff --git a/compiler/tests/group/one.leo b/compiler/tests/group/one.leo deleted file mode 100644 index 78188d4893..0000000000 --- a/compiler/tests/group/one.leo +++ /dev/null @@ -1,3 +0,0 @@ -function main() { - let a = 1group; -} \ No newline at end of file diff --git a/compiler/tests/group/x_and_y.leo b/compiler/tests/group/x_and_y.leo new file mode 100644 index 0000000000..1b64702db1 --- /dev/null +++ b/compiler/tests/group/x_and_y.leo @@ -0,0 +1,3 @@ +function main(element: group) { + let b = element; +} \ No newline at end of file diff --git a/compiler/tests/group/x_sign_high.leo b/compiler/tests/group/x_sign_high.leo new file mode 100644 index 0000000000..1b8d397d02 --- /dev/null +++ b/compiler/tests/group/x_sign_high.leo @@ -0,0 +1,3 @@ +function main() { + let element = (0, +)group; +} \ No newline at end of file diff --git a/compiler/tests/group/x_sign_inferred.leo b/compiler/tests/group/x_sign_inferred.leo new file mode 100644 index 0000000000..9e9622a4a4 --- /dev/null +++ b/compiler/tests/group/x_sign_inferred.leo @@ -0,0 +1,3 @@ +function main() { + let element = (0, _)group; +} \ No newline at end of file diff --git a/compiler/tests/group/x_sign_low.leo b/compiler/tests/group/x_sign_low.leo new file mode 100644 index 0000000000..a5058bda52 --- /dev/null +++ b/compiler/tests/group/x_sign_low.leo @@ -0,0 +1,3 @@ +function main() { + let element = (0, -)group; +} \ No newline at end of file diff --git a/compiler/tests/group/y_sign_high.leo b/compiler/tests/group/y_sign_high.leo new file mode 100644 index 0000000000..fe16883b39 --- /dev/null +++ b/compiler/tests/group/y_sign_high.leo @@ -0,0 +1,3 @@ +function main() { + let element = (+, 1)group; +} \ No newline at end of file diff --git a/compiler/tests/group/y_sign_inferred.leo b/compiler/tests/group/y_sign_inferred.leo new file mode 100644 index 0000000000..003c373cf2 --- /dev/null +++ b/compiler/tests/group/y_sign_inferred.leo @@ -0,0 +1,3 @@ +function main() { + let element = (_, 1)group; +} \ No newline at end of file diff --git a/compiler/tests/group/y_sign_low.leo b/compiler/tests/group/y_sign_low.leo new file mode 100644 index 0000000000..9e61bfd2bb --- /dev/null +++ b/compiler/tests/group/y_sign_low.leo @@ -0,0 +1,3 @@ +function main() { + let element = (-, 1)group; +} \ No newline at end of file diff --git a/compiler/tests/group/zero.leo b/compiler/tests/group/zero.leo deleted file mode 100644 index 199a8498a7..0000000000 --- a/compiler/tests/group/zero.leo +++ /dev/null @@ -1,3 +0,0 @@ -function main() { - let a = 0group; -} \ No newline at end of file diff --git a/input/src/errors/parser.rs b/input/src/errors/parser.rs index d09c6e9fe7..d5ae505ae8 100644 --- a/input/src/errors/parser.rs +++ b/input/src/errors/parser.rs @@ -48,6 +48,12 @@ impl InputParserError { Self::new_from_span(message, implicit.span().clone()) } + pub fn implicit_group(number: NumberValue) -> Self { + let message = format!("group coordinates should be in (x, y)group format, found `{}`", number); + + Self::new_from_span(message, number.span().clone()) + } + pub fn data_type_mismatch(data_type: DataType, value: Value) -> Self { let message = format!("expected `{}`, found `{}`", data_type.to_string(), value.to_string()); let span = value.span().to_owned(); diff --git a/input/src/leo-input.pest b/input/src/leo-input.pest index afafbf8ba3..2c9469317e 100644 --- a/input/src/leo-input.pest +++ b/input/src/leo-input.pest @@ -131,9 +131,20 @@ value_boolean = { "true" | "false" } value_field = ${ value_number ~ type_field } // Declared in values/group_value.rs -value_group = ${ group_single_or_tuple ~ type_group } -group_tuple = !{"(" ~ value_number ~ "," ~ value_number ~ ")"} -group_single_or_tuple = {value_number | group_tuple} +value_group = ${ group_tuple ~ type_group } +group_tuple = !{"(" ~ group_coordinate ~ "," ~ group_coordinate ~ ")"} + +// Declared in values/group_coordinate.rs +group_coordinate = { + value_number + | sign_high + | sign_low + | inferred +} + +sign_high = @{"+"} +sign_low = @{"-"} +inferred = @{"_"} // Declared in values/address.rs address = @{ "aleo" ~ ASCII_DIGIT ~ (LOWERCASE_LETTER | ASCII_DIGIT){58} } diff --git a/input/src/values/group_coordinate.rs b/input/src/values/group_coordinate.rs new file mode 100644 index 0000000000..1592ad9fd8 --- /dev/null +++ b/input/src/values/group_coordinate.rs @@ -0,0 +1,57 @@ +use crate::{ast::Rule, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::group_coordinate))] +pub enum GroupCoordinate<'ast> { + Number(NumberValue<'ast>), + SignHigh(SignHigh<'ast>), + SignLow(SignLow<'ast>), + Inferred(Inferred<'ast>), +} + +impl<'ast> GroupCoordinate<'ast> { + pub fn span(&self) -> &Span<'ast> { + match self { + GroupCoordinate::Number(number) => &number.span(), + GroupCoordinate::SignHigh(sign_high) => &sign_high.span, + GroupCoordinate::SignLow(sign_low) => &sign_low.span, + GroupCoordinate::Inferred(inferred) => &inferred.span, + } + } +} + +impl<'ast> fmt::Display for GroupCoordinate<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GroupCoordinate::Number(number) => write!(f, "{}", number), + GroupCoordinate::SignHigh(_) => write!(f, "+"), + GroupCoordinate::SignLow(_) => write!(f, "-"), + GroupCoordinate::Inferred(_) => write!(f, "_"), + } + } +} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::sign_high))] +pub struct SignHigh<'ast> { + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::sign_low))] +pub struct SignLow<'ast> { + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::inferred))] +pub struct Inferred<'ast> { + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/input/src/values/group_value.rs b/input/src/values/group_value.rs index c31e070b49..c37de3ffd5 100644 --- a/input/src/values/group_value.rs +++ b/input/src/values/group_value.rs @@ -1,4 +1,4 @@ -use crate::{ast::Rule, types::GroupType, values::NumberValue}; +use crate::{ast::Rule, types::GroupType, values::GroupCoordinate}; use pest::Span; use pest_ast::FromPest; @@ -7,7 +7,7 @@ use std::fmt; #[derive(Clone, Debug, FromPest, PartialEq, Eq)] #[pest_ast(rule(Rule::value_group))] pub struct GroupValue<'ast> { - pub value: GroupRepresentation<'ast>, + pub value: GroupTuple<'ast>, pub type_: GroupType, #[pest_ast(outer())] pub span: Span<'ast>, @@ -19,27 +19,11 @@ impl<'ast> fmt::Display for GroupValue<'ast> { } } -#[derive(Clone, Debug, FromPest, PartialEq, Eq)] -#[pest_ast(rule(Rule::group_single_or_tuple))] -pub enum GroupRepresentation<'ast> { - Single(NumberValue<'ast>), - Tuple(GroupTuple<'ast>), -} - -impl<'ast> fmt::Display for GroupRepresentation<'ast> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GroupRepresentation::Single(number) => write!(f, "{}", number), - GroupRepresentation::Tuple(tuple) => write!(f, "{}", tuple), - } - } -} - #[derive(Clone, Debug, FromPest, PartialEq, Eq)] #[pest_ast(rule(Rule::group_tuple))] pub struct GroupTuple<'ast> { - pub x: NumberValue<'ast>, - pub y: NumberValue<'ast>, + pub x: GroupCoordinate<'ast>, + pub y: GroupCoordinate<'ast>, #[pest_ast(outer())] pub span: Span<'ast>, } diff --git a/input/src/values/mod.rs b/input/src/values/mod.rs index 089a9420ed..f8ded05f4e 100644 --- a/input/src/values/mod.rs +++ b/input/src/values/mod.rs @@ -13,6 +13,9 @@ pub use boolean_value::*; pub mod field_value; pub use field_value::*; +pub mod group_coordinate; +pub use group_coordinate::*; + pub mod group_value; pub use group_value::*; diff --git a/leo/commands/publish.rs b/leo/commands/publish.rs index 8fbbe37f56..ec3925f942 100644 --- a/leo/commands/publish.rs +++ b/leo/commands/publish.rs @@ -1,7 +1,7 @@ use crate::{ cli::*, cli_types::*, - commands::{BuildCommand, LoginCommand}, + commands::LoginCommand, errors::{ commands::PublishError::{ConnectionUnavalaible, PackageNotPublished}, CLIError, diff --git a/leo/commands/test.rs b/leo/commands/test.rs index 458edfb241..ec998675b2 100644 --- a/leo/commands/test.rs +++ b/leo/commands/test.rs @@ -6,7 +6,7 @@ use crate::{ use leo_compiler::{compiler::Compiler, group::targets::edwards_bls12::EdwardsGroupType}; use leo_package::{ inputs::*, - outputs::OUTPUTS_DIRECTORY_NAME, + outputs::{OutputsDirectory, OUTPUTS_DIRECTORY_NAME}, root::Manifest, source::{MainFile, MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, }; @@ -63,27 +63,23 @@ impl CLI for TestCommand { let mut output_directory = package_path.clone(); output_directory.push(OUTPUTS_DIRECTORY_NAME); - // Load the input file at `package_name` - let input_string = InputFile::new(&package_name).read_from(&path)?; + // Create the output directory + OutputsDirectory::create(&package_path)?; - // Load the state file at `package_name.in` - let state_string = StateFile::new(&package_name).read_from(&path)?; - - // Compute the current program checksum - let program = Compiler::::parse_program_with_input( + // Parse the current main program file + let program = Compiler::::parse_program_without_input( package_name.clone(), main_file_path.clone(), output_directory, - &input_string, - &state_string, )?; - // Generate the program on the constraint system and verify correctness - { - let temporary_program = program.clone(); - let output = temporary_program.compile_test_constraints()?; - log::debug!("Compiled constraints - {:#?}", output); - } + // Parse all inputs as input pairs + let pairs = InputPairs::try_from(&package_path)?; + + // Run tests + let temporary_program = program.clone(); + let output = temporary_program.compile_test_constraints(pairs)?; + log::debug!("Compiled constraints - {:#?}", output); Ok(()) } diff --git a/package/src/errors/inputs/directory.rs b/package/src/errors/inputs/directory.rs index 398271b2fd..b18f1f1292 100644 --- a/package/src/errors/inputs/directory.rs +++ b/package/src/errors/inputs/directory.rs @@ -1,3 +1,5 @@ +use crate::{InputFileError, StateFileError}; + use std::{ffi::OsString, fs::FileType, io}; #[derive(Debug, Error)] @@ -11,15 +13,24 @@ pub enum InputsDirectoryError { #[error("file {:?} extension getting", _0)] GettingFileExtension(OsString), + #[error("file {:?} name getting", _0)] + GettingFileName(OsString), + #[error("file {:?} type getting: {}", _0, _1)] GettingFileType(OsString, io::Error), + #[error("{}", _0)] + InputFileError(#[from] InputFileError), + #[error("invalid file {:?} extension: {:?}", _0, _1)] - InvalidFileExtension(OsString, OsString), + InvalidFileExtension(String, OsString), #[error("invalid file {:?} type: {:?}", _0, _1)] InvalidFileType(OsString, FileType), #[error("reading: {}", _0)] Reading(io::Error), + + #[error("{}", _0)] + StateFileError(#[from] StateFileError), } diff --git a/package/src/inputs/directory.rs b/package/src/inputs/directory.rs index 8a9bd1d8a6..4dc70b9e28 100644 --- a/package/src/inputs/directory.rs +++ b/package/src/inputs/directory.rs @@ -1,6 +1,6 @@ -use crate::{errors::InputsDirectoryError, inputs::INPUT_FILE_EXTENSION}; +use crate::errors::InputsDirectoryError; -use std::{fs, path::PathBuf}; +use std::{fs, fs::ReadDir, path::PathBuf}; pub static INPUTS_DIRECTORY_NAME: &str = "inputs/"; @@ -17,42 +17,42 @@ impl InputsDirectory { fs::create_dir_all(&path).map_err(InputsDirectoryError::Creating) } - /// Returns a list of files in the source directory. + /// Returns a list of files in the input directory. pub fn files(path: &PathBuf) -> Result, InputsDirectoryError> { let mut path = path.to_owned(); path.push(PathBuf::from(INPUTS_DIRECTORY_NAME)); let directory = fs::read_dir(&path).map_err(InputsDirectoryError::Reading)?; let mut file_paths = Vec::new(); - for file_entry in directory.into_iter() { - let file_entry = file_entry.map_err(InputsDirectoryError::GettingFileEntry)?; - 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))?; - if !file_type.is_file() { - return Err(InputsDirectoryError::InvalidFileType( - file_path.as_os_str().to_owned(), - file_type, - )); - } - - // 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()))?; - if file_extension != INPUT_FILE_EXTENSION { - return Err(InputsDirectoryError::InvalidFileExtension( - file_path.as_os_str().to_owned(), - file_extension.to_owned(), - )); - } - - file_paths.push(file_path); - } + parse_file_paths(directory, &mut file_paths)?; Ok(file_paths) } } + +fn parse_file_paths(directory: ReadDir, file_paths: &mut Vec) -> Result<(), InputsDirectoryError> { + for file_entry in directory.into_iter() { + let file_entry = file_entry.map_err(InputsDirectoryError::GettingFileEntry)?; + let file_path = file_entry.path(); + + // Verify that the entry is structured as a valid file or directory + let file_type = file_entry + .file_type() + .map_err(|error| InputsDirectoryError::GettingFileType(file_path.as_os_str().to_owned(), error))?; + if file_type.is_dir() { + let directory = fs::read_dir(&file_path).map_err(InputsDirectoryError::Reading)?; + + parse_file_paths(directory, file_paths)?; + continue; + } else if !file_type.is_file() { + return Err(InputsDirectoryError::InvalidFileType( + file_path.as_os_str().to_owned(), + file_type, + )); + } + + file_paths.push(file_path); + } + + Ok(()) +} diff --git a/package/src/inputs/mod.rs b/package/src/inputs/mod.rs index 504289bc48..befb73256e 100644 --- a/package/src/inputs/mod.rs +++ b/package/src/inputs/mod.rs @@ -4,5 +4,8 @@ pub use directory::*; pub mod input; pub use input::*; +pub mod pairs; +pub use pairs::*; + pub mod state; pub use state::*; diff --git a/package/src/inputs/pairs.rs b/package/src/inputs/pairs.rs new file mode 100644 index 0000000000..b41947cc46 --- /dev/null +++ b/package/src/inputs/pairs.rs @@ -0,0 +1,79 @@ +use crate::{ + inputs::{InputFile, InputsDirectory, StateFile, INPUT_FILE_EXTENSION, STATE_FILE_EXTENSION}, + InputsDirectoryError, +}; + +use std::{collections::HashMap, convert::TryFrom, path::PathBuf}; + +pub struct InputPairs { + /// Maps file names to input file pairs + pub pairs: HashMap, +} + +pub struct InputPair { + pub input_file: String, + pub state_file: String, +} + +impl InputPairs { + pub fn new() -> Self { + Self { pairs: HashMap::new() } + } +} + +impl TryFrom<&PathBuf> for InputPairs { + type Error = InputsDirectoryError; + + fn try_from(directory: &PathBuf) -> Result { + let files = InputsDirectory::files(directory)?; + + let mut pairs = HashMap::::new(); + + for file in files { + let file_extension = file + .extension() + .ok_or_else(|| InputsDirectoryError::GettingFileExtension(file.as_os_str().to_owned()))?; + + let file_name = file + .file_stem() + .ok_or(InputsDirectoryError::GettingFileName(file.as_os_str().to_owned()))? + .to_str() + .ok_or(InputsDirectoryError::GettingFileName(file.as_os_str().to_owned()))?; + + if file_extension == INPUT_FILE_EXTENSION.trim_start_matches(".") { + let input_file = InputFile::new(file_name).read_from(&file)?; + + if pairs.contains_key(file_name) { + let pair = pairs.get_mut(file_name).unwrap(); + pair.input_file = input_file; + } else { + let pair = InputPair { + input_file, + state_file: "".to_owned(), + }; + pairs.insert(file_name.to_owned(), pair); + } + } else if file_extension == STATE_FILE_EXTENSION.trim_start_matches(".") { + let state_file = StateFile::new(file_name).read_from(&file)?; + + if pairs.contains_key(file_name) { + let pair = pairs.get_mut(file_name).unwrap(); + pair.state_file = state_file; + } else { + let pair = InputPair { + input_file: "".to_owned(), + state_file, + }; + pairs.insert(file_name.to_owned(), pair); + } + } else { + return Err(InputsDirectoryError::InvalidFileExtension( + file_name.to_owned(), + file_extension.to_owned(), + )); + } + } + + Ok(InputPairs { pairs }) + } +} diff --git a/state/Cargo.toml b/state/Cargo.toml new file mode 100644 index 0000000000..b7619ef19a --- /dev/null +++ b/state/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "leo-state" +version = "0.1.0" +authors = ["The Aleo Team "] +edition = "2018" + +[dependencies] +leo-input = { path = "../input", version = "0.1.0" } +leo-typed = { path = "../typed", version = "0.1.0" } + +snarkos-algorithms = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-algorithms", default-features = false } +snarkos-curves = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-curves", default-features = false } +snarkos-dpc = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-dpc", default-features = false } +snarkos-errors = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-errors", default-features = false } +snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-models", default-features = false } +snarkos-objects = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-objects" } +snarkos-utilities = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-utilities" } + + +rand = { version = "0.7" } +rand_xorshift = { version = "0.2" } +thiserror = { version = "1.0" } + + +[dev-dependencies] +snarkos-storage = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "snarkos-storage" } + diff --git a/state/src/errors/dpc_record_values.rs b/state/src/errors/dpc_record_values.rs new file mode 100644 index 0000000000..f659b134b0 --- /dev/null +++ b/state/src/errors/dpc_record_values.rs @@ -0,0 +1,20 @@ +use crate::InputValueError; + +use snarkos_errors::objects::account::AccountError; + +use std::{num::ParseIntError, str::ParseBoolError}; + +#[derive(Debug, Error)] +pub enum DPCRecordValuesError { + #[error("{}", _0)] + AccountError(#[from] AccountError), + + #[error("{}", _0)] + InputValueError(#[from] InputValueError), + + #[error("{}", _0)] + ParseBoolError(#[from] ParseBoolError), + + #[error("{}", _0)] + ParseIntError(#[from] ParseIntError), +} diff --git a/state/src/errors/input_value.rs b/state/src/errors/input_value.rs new file mode 100644 index 0000000000..1504d0ebef --- /dev/null +++ b/state/src/errors/input_value.rs @@ -0,0 +1,16 @@ +use std::num::ParseIntError; + +#[derive(Debug, Error)] +pub enum InputValueError { + #[error("expected parameter array of u8 bytes, found `{}`", _0)] + ExpectedBytes(String), + + #[error("expected integer parameter, found `{}`", _0)] + ExpectedInteger(String), + + #[error("input parameter `{}` not found in state file", _0)] + MissingParameter(String), + + #[error("{}", _0)] + ParseIntError(#[from] ParseIntError), +} diff --git a/state/src/errors/local_data_commitment.rs b/state/src/errors/local_data_commitment.rs new file mode 100644 index 0000000000..ecdc2b8c18 --- /dev/null +++ b/state/src/errors/local_data_commitment.rs @@ -0,0 +1,26 @@ +use crate::{RecordVerificationError, StateLeafValuesError, StateValuesError}; + +use snarkos_errors::algorithms::{CommitmentError, MerkleError}; + +use std::io::Error as IOError; + +#[derive(Debug, Error)] +pub enum LocalDataVerificationError { + #[error("{}", _0)] + CommitmentError(#[from] CommitmentError), + + #[error("{}", _0)] + MerkleError(#[from] MerkleError), + + #[error("{}", _0)] + IOError(#[from] IOError), + + #[error("{}", _0)] + RecordVerificationError(#[from] RecordVerificationError), + + #[error("{}", _0)] + StateLeafValuesError(#[from] StateLeafValuesError), + + #[error("{}", _0)] + StateValuesError(#[from] StateValuesError), +} diff --git a/state/src/errors/mod.rs b/state/src/errors/mod.rs new file mode 100644 index 0000000000..fd5b2ade5a --- /dev/null +++ b/state/src/errors/mod.rs @@ -0,0 +1,17 @@ +pub mod dpc_record_values; +pub use self::dpc_record_values::*; + +pub mod input_value; +pub use self::input_value::*; + +pub mod state_leaf_values; +pub use self::state_leaf_values::*; + +pub mod state_values; +pub use self::state_values::*; + +pub mod local_data_commitment; +pub use self::local_data_commitment::*; + +pub mod record_commitment; +pub use self::record_commitment::*; diff --git a/state/src/errors/record_commitment.rs b/state/src/errors/record_commitment.rs new file mode 100644 index 0000000000..a00bb8eead --- /dev/null +++ b/state/src/errors/record_commitment.rs @@ -0,0 +1,20 @@ +use crate::DPCRecordValuesError; + +use snarkos_errors::algorithms::CommitmentError; + +use std::io::Error as IOError; + +#[derive(Debug, Error)] +pub enum RecordVerificationError { + #[error("record commitment does not match record data")] + CommitmentsDoNotMatch, + + #[error("{}", _0)] + CommitmentError(#[from] CommitmentError), + + #[error("{}", _0)] + DPCRecordValuesError(#[from] DPCRecordValuesError), + + #[error("{}", _0)] + IOError(#[from] IOError), +} diff --git a/state/src/errors/state_leaf_values.rs b/state/src/errors/state_leaf_values.rs new file mode 100644 index 0000000000..b14620eb89 --- /dev/null +++ b/state/src/errors/state_leaf_values.rs @@ -0,0 +1,15 @@ +use crate::InputValueError; + +use std::{num::ParseIntError, str::ParseBoolError}; + +#[derive(Debug, Error)] +pub enum StateLeafValuesError { + #[error("{}", _0)] + InputValueError(#[from] InputValueError), + + #[error("{}", _0)] + ParseBoolError(#[from] ParseBoolError), + + #[error("{}", _0)] + ParseIntError(#[from] ParseIntError), +} diff --git a/state/src/errors/state_values.rs b/state/src/errors/state_values.rs new file mode 100644 index 0000000000..72b47c51bc --- /dev/null +++ b/state/src/errors/state_values.rs @@ -0,0 +1,15 @@ +use crate::InputValueError; + +use std::{num::ParseIntError, str::ParseBoolError}; + +#[derive(Debug, Error)] +pub enum StateValuesError { + #[error("{}", _0)] + InputValueError(#[from] InputValueError), + + #[error("{}", _0)] + ParseBoolError(#[from] ParseBoolError), + + #[error("{}", _0)] + ParseIntError(#[from] ParseIntError), +} diff --git a/state/src/lib.rs b/state/src/lib.rs new file mode 100644 index 0000000000..40cfd206e8 --- /dev/null +++ b/state/src/lib.rs @@ -0,0 +1,14 @@ +#[macro_use] +extern crate thiserror; + +pub mod errors; +pub use self::errors::*; + +pub mod local_data_commitment; +pub use self::local_data_commitment::*; + +pub mod record_commitment; +pub use self::record_commitment::*; + +pub mod utilities; +pub use self::utilities::*; diff --git a/state/src/local_data_commitment/local_data_commitment.rs b/state/src/local_data_commitment/local_data_commitment.rs new file mode 100644 index 0000000000..53a84b8149 --- /dev/null +++ b/state/src/local_data_commitment/local_data_commitment.rs @@ -0,0 +1,69 @@ +use crate::{verify_record_commitment, LocalDataVerificationError, StateLeafValues, StateValues}; +use leo_typed::Input as TypedInput; + +use snarkos_algorithms::commitment_tree::CommitmentMerklePath; +use snarkos_dpc::base_dpc::{ + instantiated::{Components, LocalDataCRH, LocalDataCommitment}, + parameters::SystemParameters, +}; +use snarkos_models::{ + algorithms::{CommitmentScheme, CRH}, + dpc::DPCComponents, +}; +use snarkos_utilities::{bytes::ToBytes, to_bytes, FromBytes}; + +use std::convert::TryFrom; + +pub fn verify_local_data_commitment( + system_parameters: &SystemParameters, + typed_input: &TypedInput, +) -> Result { + // verify record commitment + let typed_record = typed_input.get_record(); + let dpc_record_values = verify_record_commitment(system_parameters, typed_record)?; + let record_commitment: Vec = dpc_record_values.commitment; + let record_serial_number: Vec = dpc_record_values.serial_number; + + // parse typed state values + let typed_state = typed_input.get_state(); + let state_values = StateValues::try_from(typed_state)?; + let leaf_index: u32 = state_values.leaf_index; + let root: Vec = state_values.root; + + // parse typed state leaf values + let typed_state_leaf = typed_input.get_state_leaf(); + let state_leaf_values = StateLeafValues::try_from(typed_state_leaf)?; + let path: Vec = state_leaf_values.path; + let memo: Vec = state_leaf_values.memo; + let network_id: u8 = state_leaf_values.network_id; + let leaf_randomness: Vec = state_leaf_values.leaf_randomness; + + // Select local data commitment input bytes + let is_death = leaf_index < (Components::NUM_INPUT_RECORDS as u32); + let input_bytes = if is_death { + to_bytes![record_serial_number, record_commitment, memo, network_id]? + } else { + to_bytes![record_commitment, memo, network_id]? + }; + + // Construct local data commitment leaf + let local_data_leaf_randomness = ::Randomness::read(&leaf_randomness[..])?; + let local_data_commitment_leaf = LocalDataCommitment::commit( + &system_parameters.local_data_commitment, + &input_bytes, + &local_data_leaf_randomness, + )?; + + // Construct record commitment merkle path + let local_data_merkle_path = CommitmentMerklePath::::read(&path[..])?; + + // Check record commitment merkle path is valid for the given local data commitment root + let local_data_commitment_root = ::Output::read(&root[..])?; + let result = local_data_merkle_path.verify( + &system_parameters.local_data_crh, + &local_data_commitment_root, + &local_data_commitment_leaf, + )?; + + Ok(result) +} diff --git a/state/src/local_data_commitment/mod.rs b/state/src/local_data_commitment/mod.rs new file mode 100644 index 0000000000..77a5949d51 --- /dev/null +++ b/state/src/local_data_commitment/mod.rs @@ -0,0 +1,8 @@ +pub mod state_values; +pub use self::state_values::*; + +pub mod state_leaf_values; +pub use self::state_leaf_values::*; + +pub mod local_data_commitment; +pub use self::local_data_commitment::*; diff --git a/state/src/local_data_commitment/state_leaf_values.rs b/state/src/local_data_commitment/state_leaf_values.rs new file mode 100644 index 0000000000..b52badd1da --- /dev/null +++ b/state/src/local_data_commitment/state_leaf_values.rs @@ -0,0 +1,47 @@ +use crate::{find_input, input_to_integer_string, input_to_u8_vec, StateLeafValuesError}; +use leo_typed::StateLeaf as TypedStateLeaf; + +use std::convert::TryFrom; + +static PATH_PARAMETER_STRING: &str = "path"; +static MEMO_PARAMETER_STRING: &str = "memo"; +static NETWORK_ID_PARAMETER_STRING: &str = "network_id"; +static LEAF_RANDOMNESS_PARAMETER_STRING: &str = "leaf_randomness"; + +pub struct StateLeafValues { + pub path: Vec, + pub memo: Vec, + pub network_id: u8, + pub leaf_randomness: Vec, +} + +impl TryFrom<&TypedStateLeaf> for StateLeafValues { + type Error = StateLeafValuesError; + + fn try_from(state_leaf: &TypedStateLeaf) -> Result { + let parameters = state_leaf.values(); + + // Lookup path + let path_value = find_input(PATH_PARAMETER_STRING.to_owned(), ¶meters)?; + let path = input_to_u8_vec(path_value)?; + + // Lookup memo + let memo_value = find_input(MEMO_PARAMETER_STRING.to_owned(), ¶meters)?; + let memo = input_to_u8_vec(memo_value)?; + + // Lookup network id + let network_id_value = find_input(NETWORK_ID_PARAMETER_STRING.to_owned(), ¶meters)?; + let network_id = input_to_integer_string(network_id_value)?.parse::()?; + + // Lookup leaf randomness + let leaf_randomness_value = find_input(LEAF_RANDOMNESS_PARAMETER_STRING.to_owned(), ¶meters)?; + let leaf_randomness = input_to_u8_vec(leaf_randomness_value)?; + + Ok(Self { + path, + memo, + network_id, + leaf_randomness, + }) + } +} diff --git a/state/src/local_data_commitment/state_values.rs b/state/src/local_data_commitment/state_values.rs new file mode 100644 index 0000000000..5a71e19395 --- /dev/null +++ b/state/src/local_data_commitment/state_values.rs @@ -0,0 +1,30 @@ +use crate::{find_input, input_to_integer_string, input_to_u8_vec, StateValuesError}; +use leo_typed::State as TypedState; + +use std::convert::TryFrom; + +static LEAF_INDEX_PARAMETER_STRING: &str = "leaf_index"; +static ROOT_PARAMETER_STRING: &str = "root"; + +pub struct StateValues { + pub leaf_index: u32, + pub root: Vec, +} + +impl TryFrom<&TypedState> for StateValues { + type Error = StateValuesError; + + fn try_from(state: &TypedState) -> Result { + let parameters = state.values(); + + // Lookup leaf index + let leaf_index_value = find_input(LEAF_INDEX_PARAMETER_STRING.to_owned(), ¶meters)?; + let leaf_index = input_to_integer_string(leaf_index_value)?.parse::()?; + + // Lookup root + let root_value = find_input(ROOT_PARAMETER_STRING.to_owned(), ¶meters)?; + let root = input_to_u8_vec(root_value)?; + + Ok(Self { leaf_index, root }) + } +} diff --git a/state/src/record_commitment/dpc_record_values.rs b/state/src/record_commitment/dpc_record_values.rs new file mode 100644 index 0000000000..986eaee90d --- /dev/null +++ b/state/src/record_commitment/dpc_record_values.rs @@ -0,0 +1,92 @@ +use crate::{utilities::*, DPCRecordValuesError}; +use leo_typed::Record as TypedRecord; + +use snarkos_dpc::base_dpc::instantiated::Components; +use snarkos_objects::AccountAddress; + +use std::{convert::TryFrom, str::FromStr}; + +static SERIAL_NUMBER_PARAMETER_STRING: &str = "serial_number"; +static OWNER_PARAMETER_STRING: &str = "owner"; +static IS_DUMMY_PARAMETER_STRING: &str = "is_dummy"; +static VALUE_PARAMETER_STRING: &str = "value"; +static PAYLOAD_PARAMETER_STRING: &str = "payload"; +static BIRTH_PROGRAM_ID_PARAMETER_STRING: &str = "birth_program_id"; +static DEATH_PROGRAM_ID_PARAMETER_STRING: &str = "death_program_id"; +static SERIAL_NUMBER_NONCE_PARAMETER_STRING: &str = "serial_number_nonce"; +static COMMITMENT_PARAMETER_STRING: &str = "commitment"; +static COMMITMENT_RANDOMNESS_PARAMETER_STRING: &str = "commitment_randomness"; + +pub struct DPCRecordValues { + pub serial_number: Vec, + pub owner: AccountAddress, + pub is_dummy: bool, + pub value: u64, + pub payload: Vec, + pub birth_program_id: Vec, + pub death_program_id: Vec, + pub serial_number_nonce: Vec, + pub commitment: Vec, + pub commitment_randomness: Vec, +} + +impl TryFrom<&TypedRecord> for DPCRecordValues { + type Error = DPCRecordValuesError; + + fn try_from(record: &TypedRecord) -> Result { + let parameters = record.values(); + + // Lookup serial number + let serial_number_value = find_input(SERIAL_NUMBER_PARAMETER_STRING.to_owned(), ¶meters)?; + let serial_number = input_to_u8_vec(serial_number_value)?; + + // Lookup record owner + let owner_value = find_input(OWNER_PARAMETER_STRING.to_owned(), ¶meters)?; + let owner = AccountAddress::::from_str(&format!("{}", owner_value))?; + + // Lookup record is_dummy + let is_dummy_value = find_input(IS_DUMMY_PARAMETER_STRING.to_owned(), ¶meters)?; + let is_dummy = is_dummy_value.to_string().parse::()?; + + // Lookup record value + let value_value = find_input(VALUE_PARAMETER_STRING.to_owned(), ¶meters)?; + let value = input_to_integer_string(value_value)?.parse::()?; + + // Lookup record payload + let payload_value = find_input(PAYLOAD_PARAMETER_STRING.to_owned(), ¶meters)?; + let payload = input_to_u8_vec(payload_value)?; + + // Lookup record birth program id + let birth_program_id_value = find_input(BIRTH_PROGRAM_ID_PARAMETER_STRING.to_owned(), ¶meters)?; + let birth_program_id = input_to_u8_vec(birth_program_id_value)?; + + // Lookup record death program id + let death_program_id_value = find_input(DEATH_PROGRAM_ID_PARAMETER_STRING.to_owned(), ¶meters)?; + let death_program_id = input_to_u8_vec(death_program_id_value)?; + + // Lookup record serial number nonce + let serial_number_nonce_value = find_input(SERIAL_NUMBER_NONCE_PARAMETER_STRING.to_owned(), ¶meters)?; + let serial_number_nonce = input_to_u8_vec(serial_number_nonce_value)?; + + // Lookup record commitment + let commitment_value = find_input(COMMITMENT_PARAMETER_STRING.to_owned(), ¶meters)?; + let commitment = input_to_u8_vec(commitment_value)?; + + // Lookup record commitment randomness + let commitment_randomness_value = find_input(COMMITMENT_RANDOMNESS_PARAMETER_STRING.to_owned(), ¶meters)?; + let commitment_randomness = input_to_u8_vec(commitment_randomness_value)?; + + Ok(Self { + serial_number, + owner, + is_dummy, + value, + payload, + birth_program_id, + death_program_id, + serial_number_nonce, + commitment, + commitment_randomness, + }) + } +} diff --git a/state/src/record_commitment/mod.rs b/state/src/record_commitment/mod.rs new file mode 100644 index 0000000000..9d2f688f6e --- /dev/null +++ b/state/src/record_commitment/mod.rs @@ -0,0 +1,5 @@ +pub mod dpc_record_values; +pub use self::dpc_record_values::*; + +pub mod record_commitment; +pub use self::record_commitment::*; diff --git a/state/src/record_commitment/record_commitment.rs b/state/src/record_commitment/record_commitment.rs new file mode 100644 index 0000000000..55493e874f --- /dev/null +++ b/state/src/record_commitment/record_commitment.rs @@ -0,0 +1,46 @@ +use crate::{DPCRecordValues, RecordVerificationError}; +use leo_typed::Record as TypedRecord; + +use snarkos_dpc::base_dpc::{ + instantiated::{Components, RecordCommitment}, + parameters::SystemParameters, +}; +use snarkos_models::algorithms::CommitmentScheme; +use snarkos_utilities::{bytes::ToBytes, to_bytes, FromBytes}; + +use std::convert::TryFrom; + +pub fn verify_record_commitment( + system_parameters: &SystemParameters, + typed_record: &TypedRecord, +) -> Result { + // generate a dpc record from the typed record + let record = DPCRecordValues::try_from(typed_record)?; + + // verify record commitment + let record_commitment_input = to_bytes![ + record.owner, + record.is_dummy, + record.value, + record.payload, + record.birth_program_id, + record.death_program_id, + record.serial_number_nonce + ]?; + + let commitment = ::Output::read(&record.commitment[..])?; + let commitment_randomness = + ::Randomness::read(&record.commitment_randomness[..])?; + + let record_commitment = RecordCommitment::commit( + &system_parameters.record_commitment, + &record_commitment_input, + &commitment_randomness, + )?; + + if record_commitment == commitment { + Ok(record) + } else { + Err(RecordVerificationError::CommitmentsDoNotMatch) + } +} diff --git a/state/src/utilities/input_value.rs b/state/src/utilities/input_value.rs new file mode 100644 index 0000000000..e453093d8f --- /dev/null +++ b/state/src/utilities/input_value.rs @@ -0,0 +1,63 @@ +use crate::InputValueError; +use leo_typed::{InputValue, Parameter}; + +use std::collections::HashMap; + +pub fn find_input( + name: String, + parameters: &HashMap>, +) -> Result { + let matched_parameter = parameters + .iter() + .find(|(parameter, _value)| parameter.variable.name == name); + + match matched_parameter { + Some((_parameter, value_option)) => match value_option { + Some(value) => Ok(value.clone()), + None => Err(InputValueError::MissingParameter(name)), + }, + None => Err(InputValueError::MissingParameter(name)), + } +} + +pub fn input_to_integer_string(input: InputValue) -> Result { + match input { + InputValue::Integer(_type, string) => Ok(string), + value => Err(InputValueError::ExpectedInteger(value.to_string())), + } +} + +pub fn input_to_u8_vec(input: InputValue) -> Result, InputValueError> { + let input_array = match input { + InputValue::Array(values) => values, + value => return Err(InputValueError::ExpectedBytes(value.to_string())), + }; + + let mut result_vec = vec![]; + + for input in input_array { + let integer_string = input_to_integer_string(input)?; + let byte = integer_string.parse::()?; + + result_vec.push(byte); + } + + Ok(result_vec) +} + +pub fn input_to_nested_u8_vec(input: InputValue) -> Result>, InputValueError> { + let inner_arrays = match input { + InputValue::Array(arrays) => arrays, + value => return Err(InputValueError::ExpectedBytes(value.to_string())), + }; + + let mut result_vec = vec![]; + + for input_array in inner_arrays { + let array = input_to_u8_vec(input_array)?; + + result_vec.push(array); + } + + Ok(result_vec) +} diff --git a/state/src/utilities/mod.rs b/state/src/utilities/mod.rs new file mode 100644 index 0000000000..5166631852 --- /dev/null +++ b/state/src/utilities/mod.rs @@ -0,0 +1,2 @@ +pub mod input_value; +pub use self::input_value::*; diff --git a/state/tests/inputs/test_record.state b/state/tests/inputs/test_record.state new file mode 100644 index 0000000000..dcc712d3de --- /dev/null +++ b/state/tests/inputs/test_record.state @@ -0,0 +1,23 @@ +[[public]] +[state] +leaf_index: u32 = 0; +root: u8[32] = [0u8; 32]; + +[[private]] +[record] +serial_number: u8[32] = [0u8; 32]; +commitment: u8[32] = [24, 156, 6, 189, 180, 191, 65, 243, 196, 227, 127, 239, 207, 46, 119, 151, 6, 98, 159, 197, 6, 239, 1, 149, 94, 119, 37, 190, 168, 146, 198, 6]; +owner: address = aleo1daxej63vwrmn2zhl4dymygagh89k5d2vaw6rjauueme7le6k2q8sjn0ng9; +is_dummy: bool = false; +value: u64 = 13895627391323573723; +payload: u8[32] = [235, 120, 28, 41, 42, 46, 237, 32, 50, 70, 185, 70, 180, 174, 198, 169, 149, 118, 227, 124, 192, 36, 43, 2, 239, 2, 207, 166, 142, 240, 246, 39]; +birth_program_id: u8[48] = [89, 70, 116, 103, 190, 86, 194, 133, 37, 77, 113, 166, 160, 156, 98, 252, 16, 219, 94, 12, 214, 12, 151, 139, 239, 1, 79, 79, 86, 235, 209, 168, 240, 156, 224, 86, 35, 63, 185, 196, 0, 10, 148, 56, 92, 199, 45, 0]; +death_program_id: u8[48] = [89, 70, 116, 103, 190, 86, 194, 133, 37, 77, 113, 166, 160, 156, 98, 252, 16, 219, 94, 12, 214, 12, 151, 139, 239, 1, 79, 79, 86, 235, 209, 168, 240, 156, 224, 86, 35, 63, 185, 196, 0, 10, 148, 56, 92, 199, 45, 0]; +serial_number_nonce: u8[32] = [13, 163, 44, 146, 30, 254, 39, 255, 41, 20, 154, 181, 69, 34, 205, 82, 37, 173, 21, 57, 100, 180, 40, 7, 57, 209, 107, 89, 142, 91, 122, 16]; +commitment_randomness: u8[32] = [55, 189, 0, 182, 148, 137, 120, 223, 121, 7, 140, 240, 100, 202, 196, 41, 236, 128, 38, 189, 60, 59, 23, 210, 25, 102, 65, 117, 38, 140, 136, 1]; + +[state_leaf] +path: u8[128] = [0u8; 128]; +memo: u8[32] = [0u8; 32]; +network_id: u8 = 0; +leaf_randomness: u8[32] = [0u8; 32]; \ No newline at end of file diff --git a/state/tests/inputs/test_state.state b/state/tests/inputs/test_state.state new file mode 100644 index 0000000000..4336865638 --- /dev/null +++ b/state/tests/inputs/test_state.state @@ -0,0 +1,23 @@ +[[public]] +[state] +leaf_index: u32 = 0; +root: u8[32] = [70, 27, 231, 101, 102, 20, 37, 118, 77, 38, 56, 106, 49, 17, 135, 81, 134, 61, 255, 147, 230, 94, 218, 157, 98, 31, 132, 10, 116, 201, 78, 15]; + +[[private]] +[record] +serial_number: u8[64] = [98, 228, 41, 139, 42, 245, 63, 73, 45, 255, 134, 169, 18, 86, 90, 209, 31, 207, 161, 83, 183, 126, 53, 86, 142, 64, 59, 29, 185, 204, 179, 8, 43, 199, 156, 127, 124, 7, 91, 56, 27, 101, 57, 52, 166, 145, 153, 137, 17, 1, 140, 42, 205, 91, 32, 99, 22, 119, 141, 253, 235, 213, 112, 14]; +commitment: u8[32] = [233, 180, 207, 91, 31, 4, 177, 7, 21, 177, 170, 63, 134, 227, 249, 217, 193, 113, 220, 188, 97, 228, 70, 43, 160, 112, 228, 151, 110, 58, 85, 7]; +owner: address = aleo1daxej63vwrmn2zhl4dymygagh89k5d2vaw6rjauueme7le6k2q8sjn0ng9; +is_dummy: bool = false; +value: u64 = 18186969098991041491; +payload: u8[32] = [192, 118, 4, 191, 56, 79, 165, 142, 20, 92, 140, 207, 81, 125, 226, 247, 184, 40, 101, 235, 205, 174, 175, 180, 18, 104, 251, 132, 117, 163, 219, 125]; +birth_program_id: u8[48] = [89, 70, 116, 103, 190, 86, 194, 133, 37, 77, 113, 166, 160, 156, 98, 252, 16, 219, 94, 12, 214, 12, 151, 139, 239, 1, 79, 79, 86, 235, 209, 168, 240, 156, 224, 86, 35, 63, 185, 196, 0, 10, 148, 56, 92, 199, 45, 0]; +death_program_id: u8[48] = [89, 70, 116, 103, 190, 86, 194, 133, 37, 77, 113, 166, 160, 156, 98, 252, 16, 219, 94, 12, 214, 12, 151, 139, 239, 1, 79, 79, 86, 235, 209, 168, 240, 156, 224, 86, 35, 63, 185, 196, 0, 10, 148, 56, 92, 199, 45, 0]; +serial_number_nonce: u8[32] = [74, 128, 103, 188, 105, 165, 185, 183, 83, 178, 164, 202, 65, 224, 154, 216, 132, 146, 103, 158, 153, 229, 73, 162, 0, 182, 176, 162, 17, 201, 27, 6]; +commitment_randomness: u8[32] = [129, 174, 175, 20, 1, 168, 248, 69, 51, 186, 30, 34, 82, 6, 148, 174, 128, 163, 156, 197, 94, 129, 117, 226, 240, 95, 203, 196, 65, 222, 96, 4]; + +[state_leaf] +path: u8[128] = [144, 36, 140, 16, 110, 109, 215, 172, 251, 234, 246, 145, 192, 60, 79, 255, 58, 199, 52, 107, 224, 235, 152, 27, 232, 42, 96, 225, 170, 62, 118, 12, 8, 205, 94, 96, 200, 133, 229, 122, 179, 198, 124, 104, 197, 86, 67, 1, 52, 61, 168, 92, 201, 240, 61, 116, 221, 76, 172, 83, 174, 194, 118, 5, 221, 106, 153, 186, 50, 200, 155, 245, 255, 253, 169, 40, 236, 88, 58, 147, 46, 160, 55, 132, 157, 0, 134, 15, 40, 223, 53, 175, 220, 13, 222, 15, 143, 179, 79, 184, 75, 238, 87, 199, 102, 168, 167, 60, 232, 62, 64, 107, 12, 182, 200, 155, 107, 138, 224, 193, 233, 221, 54, 96, 206, 191, 83, 9]; +memo: u8[32] = [0u8; 32]; +network_id: u8 = 0; +leaf_randomness: u8[32] = [102, 202, 135, 202, 235, 133, 143, 160, 137, 212, 216, 158, 17, 44, 104, 126, 157, 109, 93, 213, 174, 57, 194, 113, 75, 184, 26, 204, 143, 131, 95, 1]; \ No newline at end of file diff --git a/state/tests/mod.rs b/state/tests/mod.rs new file mode 100644 index 0000000000..3f3fd2339d --- /dev/null +++ b/state/tests/mod.rs @@ -0,0 +1,3 @@ +mod test_verify_local_data_commitment; + +mod test_verify_record_commitment; diff --git a/state/tests/test_verify_local_data_commitment.rs b/state/tests/test_verify_local_data_commitment.rs new file mode 100644 index 0000000000..5a42e77865 --- /dev/null +++ b/state/tests/test_verify_local_data_commitment.rs @@ -0,0 +1,200 @@ +use leo_input::LeoInputParser; +use leo_state::verify_local_data_commitment; +use leo_typed::Input; + +use snarkos_dpc::base_dpc::{instantiated::*, record_payload::RecordPayload, DPC}; +use snarkos_models::{ + algorithms::{CommitmentScheme, CRH}, + dpc::Record, + objects::AccountScheme, +}; +use snarkos_objects::Account; +use snarkos_utilities::{bytes::ToBytes, to_bytes}; + +use rand::{Rng, SeedableRng}; +use rand_xorshift::XorShiftRng; +use snarkos_models::dpc::DPCScheme; +use snarkos_storage::Ledger; + +#[test] +fn test_verify_local_data_commitment_from_file() { + let mut rng = XorShiftRng::seed_from_u64(1231275789u64); + + // Generate parameters for the record commitment scheme + let system_parameters = InstantiatedDPC::generate_system_parameters(&mut rng).unwrap(); + + // Load test record state file from `inputs/test.state` + let file_bytes = include_bytes!("inputs/test_state.state"); + let file_string = String::from_utf8_lossy(file_bytes); + let file = LeoInputParser::parse_file(&file_string).unwrap(); + + let mut program_input = Input::new(); + program_input.parse_state(file).unwrap(); + + // check record state is correct by verifying commitment + let result = verify_local_data_commitment(&system_parameters, &program_input).unwrap(); + + assert!(result); +} + +#[test] +#[ignore] +fn test_generate_values_from_dpc() { + type L = Ledger; + + let mut rng = XorShiftRng::seed_from_u64(1231275789u64); + + // Specify network_id + let network_id: u8 = 0; + + // Generate parameters for the ledger, commitment schemes, CRH, and the + // "always-accept" program. + let system_parameters = InstantiatedDPC::generate_system_parameters(&mut rng).unwrap(); + let noop_program_snark_pp = + InstantiatedDPC::generate_noop_program_snark_parameters(&system_parameters, &mut rng).unwrap(); + + let noop_program_id = to_bytes![ + ProgramVerificationKeyHash::hash( + &system_parameters.program_verification_key_hash, + &to_bytes![noop_program_snark_pp.verification_key].unwrap() + ) + .unwrap() + ] + .unwrap(); + + let signature_parameters = &system_parameters.account_signature; + let commitment_parameters = &system_parameters.account_commitment; + let encryption_parameters = &system_parameters.account_encryption; + + // Generate metadata and an account for a dummy initial record. + let dummy_account = Account::new( + signature_parameters, + commitment_parameters, + encryption_parameters, + &mut rng, + ) + .unwrap(); + + let sn_nonce = SerialNumberNonce::hash(&system_parameters.serial_number_nonce, &[0u8; 1]).unwrap(); + let value = rng.gen(); + let payload: [u8; 32] = rng.gen(); + + let old_record = DPC::generate_record( + &system_parameters, + &sn_nonce, + &dummy_account.address, + false, + value, + &RecordPayload::from_bytes(&payload), + &noop_program_id, + &noop_program_id, + &mut rng, + ) + .unwrap(); + + // Set the input records for our transaction to be the initial dummy records. + let old_records = vec![old_record.clone(); NUM_INPUT_RECORDS]; + let old_account_private_keys = vec![dummy_account.private_key.clone(); NUM_INPUT_RECORDS]; + + // Construct new records. + + // Create an account for an actual new record. + + let new_account = Account::new( + signature_parameters, + commitment_parameters, + encryption_parameters, + &mut rng, + ) + .unwrap(); + + // Set the new record's program to be the "always-accept" program. + + let new_record_owners = vec![new_account.address.clone(); NUM_OUTPUT_RECORDS]; + let new_is_dummy_flags = vec![false; NUM_OUTPUT_RECORDS]; + let new_values = vec![10; NUM_OUTPUT_RECORDS]; + let new_payloads = vec![RecordPayload::default(); NUM_OUTPUT_RECORDS]; + let new_birth_program_ids = vec![noop_program_id.clone(); NUM_OUTPUT_RECORDS]; + let new_death_program_ids = vec![noop_program_id.clone(); NUM_OUTPUT_RECORDS]; + let memo = [0u8; 32]; + + let context = >::execute_offline( + &system_parameters, + &old_records, + &old_account_private_keys, + &new_record_owners, + &new_is_dummy_flags, + &new_values, + &new_payloads, + &new_birth_program_ids, + &new_death_program_ids, + &memo, + network_id, + &mut rng, + ) + .unwrap(); + + let local_data = context.into_local_data(); + let leaf_index = 0; + let record = &local_data.old_records[leaf_index]; + + let root = local_data.local_data_merkle_tree.root(); + + let serial_number = local_data.old_serial_numbers[0].clone(); + let serial_number_bytes = to_bytes![serial_number].unwrap(); + + let memorandum = local_data.memorandum; + let network_id = local_data.network_id; + let input_bytes = to_bytes![serial_number, record.commitment(), memorandum, network_id].unwrap(); + let leaf_randomness = local_data.local_data_commitment_randomizers[0].clone(); + + let old_record_leaf = ::commit( + &system_parameters.local_data_commitment, + &input_bytes, + &leaf_randomness, + ) + .unwrap(); + + // generate the path + + let path = local_data + .local_data_merkle_tree + .generate_proof(&old_record_leaf) + .unwrap(); + + println!("////////////////////////////////////////////////////"); + println!(); + println!("[state]"); + println!("leaf index {}", leaf_index); + println!("root {:?}", to_bytes![root].unwrap()); + println!(); + println!("[record]"); + println!( + "serial number {:?} len {}", + serial_number_bytes, + serial_number_bytes.len() + ); + println!("commitment {:?}", to_bytes![record.commitment()].unwrap()); + println!("owner {}", record.owner()); + println!("is_dummy {:?}", record.is_dummy()); + println!("value {:?}", record.value()); + println!("payload {:?}", record.payload()); + println!("birth_program_id {:?}", record.birth_program_id()); + println!("death_program_id {:?}", record.death_program_id()); + println!( + "serial number nonce {:?}", + to_bytes![record.serial_number_nonce()].unwrap() + ); + println!( + "commitment randomness {:?}", + to_bytes![record.commitment_randomness()].unwrap() + ); + println!(); + println!("[state_leaf]"); + println!("path {:?}", to_bytes![path].unwrap()); + println!("memo {:?}", memorandum); + println!("network id {:?}", network_id); + println!("leaf randomness {:?}", to_bytes![leaf_randomness].unwrap()); + println!(); + println!("////////////////////////////////////////////////////"); +} diff --git a/state/tests/test_verify_record_commitment.rs b/state/tests/test_verify_record_commitment.rs new file mode 100644 index 0000000000..e7bde411eb --- /dev/null +++ b/state/tests/test_verify_record_commitment.rs @@ -0,0 +1,29 @@ +use leo_input::LeoInputParser; +use leo_state::verify_record_commitment; +use leo_typed::Input; + +use snarkos_dpc::base_dpc::instantiated::*; + +use rand::SeedableRng; +use rand_xorshift::XorShiftRng; + +#[test] +fn test_verify_record_from_file() { + let mut rng = XorShiftRng::seed_from_u64(1231275789u64); + + // Generate parameters for the record commitment scheme + let system_parameters = InstantiatedDPC::generate_system_parameters(&mut rng).unwrap(); + + // Load test record state file from `inputs/test.state` + let file_bytes = include_bytes!("inputs/test_record.state"); + let file_string = String::from_utf8_lossy(file_bytes); + let file = LeoInputParser::parse_file(&file_string).unwrap(); + + let mut program_input = Input::new(); + program_input.parse_state(file).unwrap(); + + let typed_record = program_input.get_record(); + + // check record state is correct by verifying commitment + let _values = verify_record_commitment(&system_parameters, typed_record).unwrap(); +} diff --git a/typed/src/annotation.rs b/typed/src/annotation.rs new file mode 100644 index 0000000000..761dea19e3 --- /dev/null +++ b/typed/src/annotation.rs @@ -0,0 +1,58 @@ +use crate::{Circuit, Function, Identifier, Import, InputVariable, TestFunction}; +use leo_ast::{ + annotations::{Annotation, AnnotationArguments, AnnotationName}, + definitions::{AnnotatedDefinition, Definition}, +}; + +use std::collections::HashMap; + +pub fn load_annotation( + annotated_definition: AnnotatedDefinition, + _imports: &mut Vec, + _circuits: &mut HashMap, + _functions: &mut HashMap, + tests: &mut HashMap, + _expected: &mut Vec, +) { + let ast_annotation = annotated_definition.annotation; + let ast_definition = *annotated_definition.definition; + + match ast_definition { + Definition::Import(_) => unimplemented!("annotated imports are not supported yet"), + Definition::Circuit(_) => unimplemented!("annotated circuits are not supported yet"), + Definition::Function(_) => unimplemented!("annotated functions are not supported yet"), + Definition::TestFunction(ast_test) => { + let test = TestFunction::from(ast_test); + load_annotated_test(test, ast_annotation, tests) + } + Definition::Annotated(_) => unimplemented!("nested annotations are not supported yet"), + } +} + +pub fn load_annotated_test(test: TestFunction, annotation: Annotation, tests: &mut HashMap) { + let name = annotation.name; + let ast_arguments = annotation.arguments; + + match name { + AnnotationName::Context(_) => load_annotated_test_context(test, ast_arguments, tests), + } +} + +pub fn load_annotated_test_context( + mut test: TestFunction, + ast_arguments: AnnotationArguments, + tests: &mut HashMap, +) { + let arguments = ast_arguments.arguments; + + if arguments.len() != 1 { + panic!("text context annotation must have one argument identifier") + } + + let ast_input_file = arguments[0].to_owned(); + let input_file = Identifier::from(ast_input_file); + + test.input_file = Some(input_file); + + tests.insert(test.function.identifier.clone(), test); +} diff --git a/typed/src/common/identifier.rs b/typed/src/common/identifier.rs index 3a31e08599..777e404a4d 100644 --- a/typed/src/common/identifier.rs +++ b/typed/src/common/identifier.rs @@ -1,5 +1,9 @@ use crate::Span; -use leo_ast::{common::Identifier as AstIdentifier, imports::PackageName as AstPackageName}; +use leo_ast::{ + annotations::AnnotationArgument, + common::Identifier as AstIdentifier, + imports::PackageName as AstPackageName, +}; use leo_input::common::Identifier as InputAstIdentifier; use serde::{ @@ -55,6 +59,15 @@ impl<'ast> From> for Identifier { } } +impl<'ast> From> for Identifier { + fn from(argument: AnnotationArgument<'ast>) -> Self { + Self { + name: argument.value, + span: Span::from(argument.span), + } + } +} + impl fmt::Display for Identifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) diff --git a/typed/src/expression.rs b/typed/src/expression.rs index 27d366bb25..beed6ee8b5 100644 --- a/typed/src/expression.rs +++ b/typed/src/expression.rs @@ -1,4 +1,4 @@ -use crate::{CircuitFieldDefinition, Identifier, IntegerType, RangeOrExpression, Span, SpreadOrExpression}; +use crate::{CircuitFieldDefinition, GroupValue, Identifier, IntegerType, RangeOrExpression, Span, SpreadOrExpression}; use leo_ast::{ access::{Access, AssigneeAccess}, common::{Assignee, Identifier as AstIdentifier}, @@ -17,7 +17,7 @@ use leo_ast::{ AddressValue, BooleanValue, FieldValue, - GroupValue, + GroupValue as AstGroupValue, IntegerValue, NumberValue as AstNumber, PositiveNumber as AstPositiveNumber, @@ -40,7 +40,7 @@ pub enum Expression { Address(String, Span), Boolean(String, Span), Field(String, Span), - Group(String, Span), + Group(GroupValue), Implicit(String, Span), Integer(IntegerType, String, Span), @@ -86,7 +86,7 @@ impl Expression { pub fn set_span(&mut self, new_span: &Span) { match self { Expression::Field(_, old_span) => *old_span = new_span.clone(), - Expression::Group(_, old_span) => *old_span = new_span.clone(), + Expression::Group(value) => value.span = new_span.clone(), Expression::Add(_, _, old_span) => *old_span = new_span.clone(), Expression::Sub(_, _, old_span) => *old_span = new_span.clone(), @@ -146,7 +146,7 @@ impl<'ast> fmt::Display for Expression { Expression::Address(ref address, ref _span) => write!(f, "{}", address), Expression::Boolean(ref bool, ref _span) => write!(f, "{}", bool), Expression::Field(ref field, ref _span) => write!(f, "{}", field), - Expression::Group(ref group, ref _span) => write!(f, "{}", group), + Expression::Group(ref group) => write!(f, "{}", group), Expression::Implicit(ref value, ref _span) => write!(f, "{}", value), Expression::Integer(ref type_, ref integer, ref _span) => write!(f, "{}{}", integer, type_), @@ -509,9 +509,9 @@ impl<'ast> From> for Expression { } } -impl<'ast> From> for Expression { - fn from(group: GroupValue<'ast>) -> Self { - Expression::Group(group.to_string(), Span::from(group.span)) +impl<'ast> From> for Expression { + fn from(ast_group: AstGroupValue<'ast>) -> Self { + Expression::Group(GroupValue::from(ast_group)) } } diff --git a/typed/src/functions/test_function.rs b/typed/src/functions/test_function.rs index cc20c13ad7..f82344afc2 100644 --- a/typed/src/functions/test_function.rs +++ b/typed/src/functions/test_function.rs @@ -1,13 +1,19 @@ -use crate::Function; +use crate::{Function, Identifier}; use leo_ast::functions::TestFunction as AstTestFunction; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TestFunction(pub Function); +pub struct TestFunction { + pub function: Function, + pub input_file: Option, +} impl<'ast> From> for TestFunction { fn from(test: AstTestFunction) -> Self { - TestFunction(Function::from(test.function)) + TestFunction { + function: Function::from(test.function), + input_file: None, // pass custom input file with `@context` annotation + } } } diff --git a/typed/src/groups/group_coordinate.rs b/typed/src/groups/group_coordinate.rs new file mode 100644 index 0000000000..9475a7c4eb --- /dev/null +++ b/typed/src/groups/group_coordinate.rs @@ -0,0 +1,113 @@ +use crate::common::span::Span; +use leo_ast::values::{ + GroupCoordinate as AstGroupCoordinate, + Inferred as AstInferred, + NumberValue as AstNumberValue, + SignHigh as AstSignHigh, + SignLow as AstSignLow, +}; +use leo_input::values::{ + GroupCoordinate as InputGroupCoordinate, + Inferred as InputInferred, + NumberValue as InputNumberValue, + SignHigh as InputSignHigh, + SignLow as InputSignLow, +}; + +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum GroupCoordinate { + Number(String, Span), + SignHigh, + SignLow, + Inferred, +} + +impl<'ast> From> for GroupCoordinate { + fn from(coordinate: AstGroupCoordinate<'ast>) -> Self { + match coordinate { + AstGroupCoordinate::Number(number) => GroupCoordinate::from(number), + AstGroupCoordinate::SignHigh(sign_high) => GroupCoordinate::from(sign_high), + AstGroupCoordinate::SignLow(sign_low) => GroupCoordinate::from(sign_low), + AstGroupCoordinate::Inferred(inferred) => GroupCoordinate::from(inferred), + } + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(coordinate: InputGroupCoordinate<'ast>) -> Self { + match coordinate { + InputGroupCoordinate::Number(number) => GroupCoordinate::from(number), + InputGroupCoordinate::SignHigh(sign_high) => GroupCoordinate::from(sign_high), + InputGroupCoordinate::SignLow(sign_low) => GroupCoordinate::from(sign_low), + InputGroupCoordinate::Inferred(inferred) => GroupCoordinate::from(inferred), + } + } +} + +impl fmt::Display for GroupCoordinate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GroupCoordinate::Number(number, _) => write!(f, "{}", number), + GroupCoordinate::SignHigh => write!(f, "+"), + GroupCoordinate::SignLow => write!(f, "-"), + GroupCoordinate::Inferred => write!(f, "_"), + } + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(number: AstNumberValue<'ast>) -> Self { + let value = number.to_string(); + let span = Span::from(number.span().clone()); + + GroupCoordinate::Number(value, span) + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(_sign: AstSignHigh<'ast>) -> Self { + GroupCoordinate::SignHigh + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(_sign: AstSignLow<'ast>) -> Self { + GroupCoordinate::SignLow + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(_sign: AstInferred<'ast>) -> Self { + GroupCoordinate::Inferred + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(number: InputNumberValue<'ast>) -> Self { + let value = number.to_string(); + let span = Span::from(number.span().clone()); + + GroupCoordinate::Number(value, span) + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(_sign: InputSignHigh<'ast>) -> Self { + GroupCoordinate::SignHigh + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(_sign: InputSignLow<'ast>) -> Self { + GroupCoordinate::SignLow + } +} + +impl<'ast> From> for GroupCoordinate { + fn from(_sign: InputInferred<'ast>) -> Self { + GroupCoordinate::Inferred + } +} diff --git a/typed/src/groups/group_value.rs b/typed/src/groups/group_value.rs new file mode 100644 index 0000000000..8c54ef4939 --- /dev/null +++ b/typed/src/groups/group_value.rs @@ -0,0 +1,45 @@ +use crate::{common::span::Span, groups::GroupCoordinate}; +use leo_ast::values::GroupValue as AstGroupValue; +use leo_input::values::GroupValue as InputGroupValue; + +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GroupValue { + pub x: GroupCoordinate, + pub y: GroupCoordinate, + pub span: Span, +} + +impl<'ast> From> for GroupValue { + fn from(ast_group: AstGroupValue<'ast>) -> Self { + let ast_x = ast_group.value.x; + let ast_y = ast_group.value.y; + + Self { + x: GroupCoordinate::from(ast_x), + y: GroupCoordinate::from(ast_y), + span: Span::from(ast_group.span), + } + } +} + +impl<'ast> From> for GroupValue { + fn from(ast_group: InputGroupValue<'ast>) -> Self { + let ast_x = ast_group.value.x; + let ast_y = ast_group.value.y; + + Self { + x: GroupCoordinate::from(ast_x), + y: GroupCoordinate::from(ast_y), + span: Span::from(ast_group.span), + } + } +} + +impl fmt::Display for GroupValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } +} diff --git a/typed/src/groups/mod.rs b/typed/src/groups/mod.rs new file mode 100644 index 0000000000..98533d2893 --- /dev/null +++ b/typed/src/groups/mod.rs @@ -0,0 +1,5 @@ +pub mod group_coordinate; +pub use self::group_coordinate::*; + +pub mod group_value; +pub use self::group_value::*; diff --git a/typed/src/input/input.rs b/typed/src/input/input.rs index cacc2d9d8f..8f1dd54bfc 100644 --- a/typed/src/input/input.rs +++ b/typed/src/input/input.rs @@ -6,6 +6,7 @@ use leo_input::{ #[derive(Clone, PartialEq, Eq)] pub struct Input { + name: String, program_input: ProgramInput, program_state: ProgramState, } @@ -13,6 +14,7 @@ pub struct Input { impl Input { pub fn new() -> Self { Self { + name: "default".to_owned(), program_input: ProgramInput::new(), program_state: ProgramState::new(), } @@ -25,6 +27,7 @@ impl Input { let state = self.program_state.empty(); Self { + name: self.name.clone(), program_input: input, program_state: state, } diff --git a/typed/src/input/input_value.rs b/typed/src/input/input_value.rs index a17f0fd5e0..4d064e728c 100644 --- a/typed/src/input/input_value.rs +++ b/typed/src/input/input_value.rs @@ -1,8 +1,9 @@ +use crate::GroupValue; use leo_input::{ errors::InputParserError, expressions::{ArrayInitializerExpression, ArrayInlineExpression, Expression}, types::{ArrayType, DataType, IntegerType, Type}, - values::{BooleanValue, FieldValue, GroupValue, NumberValue, Value}, + values::{BooleanValue, FieldValue, GroupValue as InputGroupValue, NumberValue, Value}, }; use leo_input::{ @@ -17,7 +18,7 @@ pub enum InputValue { Address(String), Boolean(bool), Field(String), - Group(String), + Group(GroupValue), Integer(IntegerType, String), Array(Vec), Tuple(Vec), @@ -44,8 +45,8 @@ impl InputValue { Ok(InputValue::Integer(integer_type, number)) } - fn from_group(group: GroupValue) -> Self { - InputValue::Group(group.to_string()) + fn from_group(group: InputGroupValue) -> Self { + InputValue::Group(GroupValue::from(group)) } fn from_field(field: FieldValue) -> Self { @@ -57,7 +58,7 @@ impl InputValue { DataType::Address(_) => Err(InputParserError::implicit_type(data_type, implicit)), DataType::Boolean(_) => Err(InputParserError::implicit_type(data_type, implicit)), DataType::Integer(integer_type) => InputValue::from_number(integer_type, implicit.to_string()), - DataType::Group(_) => Ok(InputValue::Group(implicit.to_string())), + DataType::Group(_) => Err(InputParserError::implicit_group(implicit)), DataType::Field(_) => Ok(InputValue::Field(implicit.to_string())), } } diff --git a/typed/src/lib.rs b/typed/src/lib.rs index c1d90de64c..290550c657 100644 --- a/typed/src/lib.rs +++ b/typed/src/lib.rs @@ -1,6 +1,9 @@ //! A typed syntax tree is represented as a `Program` and consists of import, circuit, and function definitions. //! Each defined type consists of typed statements and expressions. +pub mod annotation; +pub use self::annotation::*; + pub mod circuits; pub use self::circuits::*; @@ -16,6 +19,9 @@ pub use self::expression::*; pub mod functions; pub use self::functions::*; +pub mod groups; +pub use self::groups::*; + pub mod imports; pub use self::imports::*; diff --git a/typed/src/program.rs b/typed/src/program.rs index c4964ffd10..abe5cb8bf0 100644 --- a/typed/src/program.rs +++ b/typed/src/program.rs @@ -1,7 +1,7 @@ //! A typed Leo program consists of import, circuit, and function definitions. //! Each defined type consists of typed statements and expressions. -use crate::{Circuit, Function, Identifier, Import, InputVariable, TestFunction}; +use crate::{load_annotation, Circuit, Function, Identifier, Import, InputVariable, TestFunction}; use leo_ast::{definitions::Definition, files::File}; use serde::{Deserialize, Serialize}; @@ -47,7 +47,17 @@ impl<'ast> Program { } Definition::TestFunction(test_def) => { let test = TestFunction::from(test_def); - tests.insert(test.0.identifier.clone(), test); + tests.insert(test.function.identifier.clone(), test); + } + Definition::Annotated(annotated_definition) => { + load_annotation( + annotated_definition, + &mut imports, + &mut circuits, + &mut functions, + &mut tests, + &mut expected_input, + ); } });