From fbca76444b134f59c080bf78b69bb0186c33fa60 Mon Sep 17 00:00:00 2001 From: vms Date: Fri, 30 Oct 2020 20:29:05 +0300 Subject: [PATCH] New data format (#12) --- .circleci/config.yml | 6 +- CHANGELOG.md | 12 + Cargo.lock | 190 ++++--- Cargo.toml | 1 + crates/test-utils/src/lib.rs | 51 ++ stepper-lib/.rustfmt.toml | 1 + stepper-lib/Cargo.toml | 37 ++ {stepper => stepper-lib}/src/air/call.rs | 113 ++-- stepper-lib/src/air/call/parsed_call.rs | 290 ++++++++++ stepper-lib/src/air/call/utils.rs | 87 +++ .../src/air/execution_context.rs | 30 +- stepper-lib/src/air/fold.rs | 346 ++++++++++++ {stepper => stepper-lib}/src/air/mod.rs | 53 +- {stepper => stepper-lib}/src/air/null.rs | 3 +- {stepper => stepper-lib}/src/air/par.rs | 39 +- {stepper => stepper-lib}/src/air/seq.rs | 28 +- {stepper => stepper-lib}/src/air/xor.rs | 41 +- stepper-lib/src/build_targets/fce_target.rs | 30 ++ .../src/build_targets/mod.rs | 27 +- .../src/build_targets/wasm_bindgen_target.rs | 40 ++ .../src/call_evidence/context.rs | 14 + .../src/call_evidence/mod.rs | 6 +- .../src/call_evidence/state.rs | 144 ++--- {stepper => stepper-lib}/src/errors.rs | 79 ++- {stepper => stepper-lib}/src/execution.rs | 30 +- stepper-lib/src/execution/prolog.rs | 69 +++ .../src/execution/utils.rs | 0 stepper-lib/src/lib.rs | 81 +++ stepper-lib/src/log_targets.rs | 59 ++ .../src/stepper_outcome.rs | 2 +- stepper-lib/tests/air_basic.rs | 196 +++++++ stepper-lib/tests/call_evidence_basic.rs | 442 +++++++++++++++ stepper-lib/tests/data_merge.rs | 211 ++++++++ {stepper => stepper-lib}/tests/join.rs | 227 +++----- stepper/Cargo.toml | 26 +- stepper/src/air/call/parsed_call.rs | 207 ------- stepper/src/air/call/utils.rs | 132 ----- stepper/src/air/fold.rs | 300 ----------- stepper/src/execution/epilog.rs | 33 -- stepper/src/execution/prolog.rs | 99 ---- stepper/src/fce.rs | 36 +- stepper/src/wasm_bindgen.rs | 50 +- stepper/tests/air_basic.rs | 180 ------- stepper/tests/call_evidence_basic.rs | 509 ------------------ stepper/tests/data_merge.rs | 186 ------- 45 files changed, 2521 insertions(+), 2222 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 stepper-lib/.rustfmt.toml create mode 100644 stepper-lib/Cargo.toml rename {stepper => stepper-lib}/src/air/call.rs (54%) create mode 100644 stepper-lib/src/air/call/parsed_call.rs create mode 100644 stepper-lib/src/air/call/utils.rs rename {stepper => stepper-lib}/src/air/execution_context.rs (67%) create mode 100644 stepper-lib/src/air/fold.rs rename {stepper => stepper-lib}/src/air/mod.rs (58%) rename {stepper => stepper-lib}/src/air/null.rs (92%) rename {stepper => stepper-lib}/src/air/par.rs (83%) rename {stepper => stepper-lib}/src/air/seq.rs (78%) rename {stepper => stepper-lib}/src/air/xor.rs (72%) create mode 100644 stepper-lib/src/build_targets/fce_target.rs rename stepper/src/defines.rs => stepper-lib/src/build_targets/mod.rs (63%) create mode 100644 stepper-lib/src/build_targets/wasm_bindgen_target.rs rename {stepper => stepper-lib}/src/call_evidence/context.rs (75%) rename {stepper => stepper-lib}/src/call_evidence/mod.rs (86%) rename {stepper => stepper-lib}/src/call_evidence/state.rs (58%) rename {stepper => stepper-lib}/src/errors.rs (76%) rename {stepper => stepper-lib}/src/execution.rs (55%) create mode 100644 stepper-lib/src/execution/prolog.rs rename {stepper => stepper-lib}/src/execution/utils.rs (100%) create mode 100644 stepper-lib/src/lib.rs create mode 100644 stepper-lib/src/log_targets.rs rename {stepper => stepper-lib}/src/stepper_outcome.rs (97%) create mode 100644 stepper-lib/tests/air_basic.rs create mode 100644 stepper-lib/tests/call_evidence_basic.rs create mode 100644 stepper-lib/tests/data_merge.rs rename {stepper => stepper-lib}/tests/join.rs (53%) delete mode 100644 stepper/src/air/call/parsed_call.rs delete mode 100644 stepper/src/air/call/utils.rs delete mode 100644 stepper/src/air/fold.rs delete mode 100644 stepper/src/execution/epilog.rs delete mode 100644 stepper/src/execution/prolog.rs delete mode 100644 stepper/tests/air_basic.rs delete mode 100644 stepper/tests/call_evidence_basic.rs delete mode 100644 stepper/tests/data_merge.rs diff --git a/.circleci/config.yml b/.circleci/config.yml index 66133643..4042c7ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,12 +22,12 @@ jobs: cargo install fcli cd stepper - fce build + fce build --features fce cd .. cargo fmt --all -- --check --color always - cargo build --release --all-features - cargo test --release --all-features + cargo check + cargo test --release cargo clippy -v - save_cache: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..1aa78c7f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +## Version 0.1.2 (2020-10-29) + +- Added new data format ([PR 12] (https://github.com/fluencelabs/aquamarine/pull/12)): + - previously data was a hashmap with variable names to values, and now it is call evidence path that contains call and par evidence states + - logger is refactored and supports now several log targets + - stepper decoupled into two crates: `stepper-lib` and `stepper`. To build it for the FCE target the `fce` feature should be specified (`fce build --features fce`) + +## Version 0.1.1 (2020-10-23) + +- Added join behaviour ([PR 11](https://github.com/fluencelabs/aquamarine/pull/11)): + - if `call` uses non existing variable, it is just being passed and isn't executed without any error + - `par` becomes completed when at least one of its subtree is completed diff --git a/Cargo.lock b/Cargo.lock index 620b1df7..9f18e5d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,31 +42,22 @@ dependencies = [ [[package]] name = "aquamarine" -version = "0.1.1" +version = "0.1.2" dependencies = [ - "aqua-test-utils", - "aquamarine-vm", - "boolinator", - "env_logger", "fluence", - "jsonpath_lib", "log", - "maplit", - "once_cell", - "pretty_assertions", - "serde", - "serde_derive", "serde_json", - "serde_sexpr", + "stepper-lib", "wasm-bindgen", ] [[package]] name = "aquamarine-vm" -version = "0.1.0" -source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed" +version = "0.1.1" +source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457" dependencies = [ "fluence-faas", + "maplit", "serde", "serde_json", ] @@ -85,9 +76,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "atty" @@ -131,7 +122,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if", + "cfg-if 0.1.10", "constant_time_eq", "crypto-mac", "digest 0.9.0", @@ -167,6 +158,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "cloudabi" version = "0.0.3" @@ -182,6 +179,12 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "475bd7aa7680b4ed8f6bb59745e882bcbaeb39326532bb79ffb1716480d9a274" +[[package]] +name = "const_fn" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -250,48 +253,48 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ + "cfg-if 1.0.0", "crossbeam-utils", - "maybe-uninit", ] [[package]] name = "crossbeam-deque" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", - "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.8.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f" dependencies = [ - "autocfg", - "cfg-if", + "cfg-if 1.0.0", + "const_fn", "crossbeam-utils", "lazy_static", - "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", + "const_fn", "lazy_static", ] @@ -369,9 +372,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" +checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe" dependencies = [ "errno-dragonfly", "libc", @@ -390,8 +393,8 @@ dependencies = [ [[package]] name = "fce" -version = "0.1.8" -source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed" +version = "0.1.9" +source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457" dependencies = [ "boolinator", "fce-wit-interfaces", @@ -402,6 +405,7 @@ dependencies = [ "parity-wasm", "pwasm-utils", "safe-transmute", + "serde", "wasmer-interface-types-fl", "wasmer-runtime-core-fl", "wasmer-runtime-fl", @@ -410,8 +414,8 @@ dependencies = [ [[package]] name = "fce-wit-interfaces" -version = "0.1.5" -source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed" +version = "0.1.6" +source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457" dependencies = [ "multimap", "wasmer-interface-types-fl", @@ -419,8 +423,8 @@ dependencies = [ [[package]] name = "fce-wit-parser" -version = "0.1.7" -source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed" +version = "0.1.8" +source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457" dependencies = [ "anyhow", "fce-wit-interfaces", @@ -432,7 +436,7 @@ dependencies = [ [[package]] name = "fluence" version = "0.2.8" -source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" +source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30" dependencies = [ "fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", "fluence-sdk-main 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", @@ -440,8 +444,8 @@ dependencies = [ [[package]] name = "fluence-faas" -version = "0.1.9" -source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed" +version = "0.1.10" +source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457" dependencies = [ "cmd_lib", "fce", @@ -459,6 +463,14 @@ dependencies = [ "wasmer-wasi-fl", ] +[[package]] +name = "fluence-sdk-macro" +version = "0.2.8" +source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30" +dependencies = [ + "fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", +] + [[package]] name = "fluence-sdk-macro" version = "0.2.8" @@ -469,11 +481,13 @@ dependencies = [ ] [[package]] -name = "fluence-sdk-macro" +name = "fluence-sdk-main" version = "0.2.8" -source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" +source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30" dependencies = [ - "fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", + "fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", + "log", + "serde", ] [[package]] @@ -487,21 +501,10 @@ dependencies = [ "serde", ] -[[package]] -name = "fluence-sdk-main" -version = "0.2.8" -source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" -dependencies = [ - "fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", - "log", - "serde", -] - [[package]] name = "fluence-sdk-wit" version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17" +source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30" dependencies = [ "proc-macro2", "quote", @@ -514,7 +517,8 @@ dependencies = [ [[package]] name = "fluence-sdk-wit" version = "0.2.8" -source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17" dependencies = [ "proc-macro2", "quote", @@ -536,7 +540,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "serde", ] @@ -565,7 +569,7 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -717,16 +721,16 @@ checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" dependencies = [ "arrayvec", "bitflags", - "cfg-if", + "cfg-if 0.1.10", "ryu", "static_assertions", ] [[package]] name = "libc" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "lock_api" @@ -743,7 +747,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] @@ -752,17 +756,11 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memmap" @@ -806,7 +804,7 @@ checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", "void", ] @@ -842,12 +840,6 @@ dependencies = [ "libc", ] -[[package]] -name = "once_cell" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" - [[package]] name = "output_vt100" version = "0.1.2" @@ -889,7 +881,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "cloudabi", "libc", "redox_syscall", @@ -1004,9 +996,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" dependencies = [ "autocfg", "crossbeam-deque", @@ -1016,9 +1008,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1166,6 +1158,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stepper-lib" +version = "0.1.2" +dependencies = [ + "aqua-test-utils", + "aquamarine-vm", + "boolinator", + "env_logger", + "fluence", + "jsonpath_lib", + "log", + "maplit", + "pretty_assertions", + "serde", + "serde_derive", + "serde_json", + "serde_sexpr", + "wasm-bindgen", +] + [[package]] name = "subtle" version = "2.3.0" @@ -1174,9 +1186,9 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" [[package]] name = "syn" -version = "1.0.44" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" dependencies = [ "proc-macro2", "quote", @@ -1360,7 +1372,7 @@ version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "wasm-bindgen-macro", ] @@ -1462,9 +1474,9 @@ dependencies = [ [[package]] name = "wasmer-interface-types-fl" -version = "0.17.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475cafb8bf9763895f6fde0d7417b9090145fc461ad7aef21b1a866b8357c091" +checksum = "1de496e366bd1c198942248fc1de4b94e4647b263dd60099d5f7776f0d621656" dependencies = [ "log", "nom 5.1.2", diff --git a/Cargo.toml b/Cargo.toml index d3bb1a6f..5a0ba7c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/test-module", "crates/test-utils", "stepper", + "stepper-lib", ] [profile.release] diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 6b0bbeef..f591694b 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -33,8 +33,11 @@ use aquamarine_vm::HostImportDescriptor; use aquamarine_vm::IType; use aquamarine_vm::IValue; +use std::collections::HashMap; use std::path::PathBuf; +type JValue = serde_json::Value; + pub fn create_aqua_vm( call_service: HostExportedFunc, current_peer_id: impl Into, @@ -46,10 +49,14 @@ pub fn create_aqua_vm( error_handler: None, }; + let tmp_dir = std::env::temp_dir(); + let config = AquamarineVMConfig { aquamarine_wasm_path: PathBuf::from("../target/wasm32-wasi/debug/aquamarine.wasm"), call_service: call_service_descriptor, current_peer_id: current_peer_id.into(), + particle_data_store: tmp_dir, + logging_mask: i64::max_value(), }; AquamarineVM::new(config).expect("vm should be created") @@ -100,3 +107,47 @@ pub fn echo_number_call_service() -> HostExportedFunc { )) }) } + +pub fn set_variable_call_service(json: impl Into) -> HostExportedFunc { + let json = json.into(); + Box::new(move |_, _| -> Option { + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(json.clone())]).unwrap(), + )) + }) +} + +pub fn set_variables_call_service(ret_mapping: HashMap) -> HostExportedFunc { + Box::new(move |_, args| -> Option { + let arg_name = match &args[2] { + IValue::String(json_str) => { + let json = serde_json::from_str(json_str).expect("a valid json"); + match json { + JValue::Array(array) => match array.first() { + Some(JValue::String(str)) => str.to_string(), + _ => String::from("default"), + }, + _ => String::from("default"), + } + } + _ => String::from("default"), + }; + + let result = ret_mapping + .get(&arg_name) + .cloned() + .unwrap_or(String::from(r#""test""#)); + + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(result.clone())]).unwrap(), + )) + }) +} + +#[macro_export] +macro_rules! call_vm { + ($vm:expr, $init_user_id:expr, $script:expr, $prev_data:expr, $data:expr) => { + $vm.call_with_prev_data($init_user_id, $script, $prev_data, $data) + .expect("call should be successful"); + }; +} diff --git a/stepper-lib/.rustfmt.toml b/stepper-lib/.rustfmt.toml new file mode 100644 index 00000000..75306517 --- /dev/null +++ b/stepper-lib/.rustfmt.toml @@ -0,0 +1 @@ +max_width = 120 diff --git a/stepper-lib/Cargo.toml b/stepper-lib/Cargo.toml new file mode 100644 index 00000000..3b539554 --- /dev/null +++ b/stepper-lib/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "stepper-lib" +version = "0.1.2" +authors = ["Fluence Labs"] +edition = "2018" + +[lib] +name = "stepper_lib" +path = "src/lib.rs" + +[dependencies] +fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } + +serde = { version = "1.0.116", features = [ "derive", "rc" ] } +serde_derive = "1.0.116" +serde_sexpr = "0.1.0" + +jsonpath_lib = "0.2.5" + +boolinator = "2.4.0" +log = "0.4.11" +serde_json = "1.0" +wasm-bindgen = "0.2.68" + +[dev_dependencies] +aqua-test-utils = { path = "../crates/test-utils" } +aquamarine-vm = { git = "https://github.com/fluencelabs/fce", features = ["raw-aquamarine-vm-api"] } + +env_logger = "0.7.1" +maplit = "1.0.2" +pretty_assertions = "0.6.1" +serde_json = "1.0.56" + +[features] +# indicates that this library should be compiled for the wasm bindgen target +# otherwise it will be compiled to the FCE target +fce = [] diff --git a/stepper/src/air/call.rs b/stepper-lib/src/air/call.rs similarity index 54% rename from stepper/src/air/call.rs rename to stepper-lib/src/air/call.rs index 71b23330..4609c935 100644 --- a/stepper/src/air/call.rs +++ b/stepper-lib/src/air/call.rs @@ -21,6 +21,7 @@ use parsed_call::ParsedCall; use super::CallEvidenceCtx; use super::ExecutionCtx; +use crate::log_instruction; use crate::AquamarineError::VariableNotFound; use crate::AquamarineError::VariableNotInJsonPath; use crate::Result; @@ -60,7 +61,7 @@ pub(crate) struct Call(PeerPart, FunctionPart, Vec, String); impl super::ExecutableInstruction for Call { fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!("call {:?} is called with contexts: {:?} {:?}", self, exec_ctx, call_ctx); + log_instruction!(call, exec_ctx, call_ctx); let parsed_call = match ParsedCall::new(self, exec_ctx) { Ok(parsed_call) => parsed_call, @@ -89,81 +90,107 @@ impl super::ExecutableInstruction for Call { #[cfg(test)] mod tests { + use crate::call_evidence::CallEvidencePath; use crate::JValue; + use aqua_test_utils::call_vm; use aqua_test_utils::create_aqua_vm; use aqua_test_utils::echo_string_call_service; + use aqua_test_utils::set_variable_call_service; + use aqua_test_utils::unit_call_service; use aquamarine_vm::vec1::Vec1; use aquamarine_vm::HostExportedFunc; use aquamarine_vm::IValue; - use serde_json::json; + use std::rc::Rc; #[test] fn current_peer_id_call() { - let mut vm = create_aqua_vm(echo_string_call_service(), "test_peer_id"); + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + + let mut vm = create_aqua_vm(unit_call_service(), "test_peer_id"); let script = String::from( r#" - (call (%current_peer_id% ("local_service_id" "local_fn_name") (value) result_name)) + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_name)) "#, ); - let res = vm - .call(json!(["asd", script, "{}", "{\"value\": \"test\"}",])) - .expect("call should be successful"); + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json"); - let res: JValue = serde_json::from_str(&res.data).unwrap(); - - assert_eq!(res.get("result_name").unwrap(), &json!("test")); + assert_eq!(call_path.len(), 1); + assert_eq!( + call_path[0], + Call(Executed(Rc::new(JValue::String(String::from("test"))))) + ); + assert!(res.next_peer_pks.is_empty()); let script = String::from( r#" - (call ("test_peer_id" ("local_service_id" "local_fn_name") (value) result_name)) + (call ("test_peer_id" ("local_service_id" "local_fn_name") () result_name)) "#, ); - let res = vm - .call(json!(["asd", script, "{}", "{\"value\": \"test\"}",])) - .expect("call should be successful"); + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json"); - let res: JValue = serde_json::from_str(&res.data).unwrap(); - - assert_eq!(res.get("result_name").unwrap(), &json!("test")); + assert_eq!(call_path.len(), 1); + assert_eq!( + call_path[0], + Call(Executed(Rc::new(JValue::String(String::from("test"))))) + ); + assert!(res.next_peer_pks.is_empty()); } #[test] fn remote_peer_id_call() { - let mut vm = create_aqua_vm(echo_string_call_service(), ""); - let remote_peer_id = String::from("some_remote_peer_id"); + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + let some_local_peer_id = String::from("some_local_peer_id"); + let mut vm = create_aqua_vm(echo_string_call_service(), some_local_peer_id.clone()); + + let remote_peer_id = String::from("some_remote_peer_id"); let script = format!( r#"(call ("{}" ("local_service_id" "local_fn_name") (value) result_name))"#, remote_peer_id ); - let res = vm - .call(json!(["asd", script, "{}", "{\"value\": \"test\"}",])) - .expect("call should be successful"); + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json"); + assert_eq!(call_path.len(), 1); + assert_eq!(call_path[0], Call(RequestSent(some_local_peer_id))); assert_eq!(res.next_peer_pks, vec![remote_peer_id]); } #[test] fn variables() { - let mut vm = create_aqua_vm(echo_string_call_service(), ""); + let mut vm = create_aqua_vm(unit_call_service(), "remote_peer_id"); + let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#""remote_peer_id""#), "set_variable"); - let script = format!(r#"(call (remote_peer_id ("some_service_id" "local_fn_name") ("param") result_name))"#,); + let script = format!( + r#" + (seq ( + (call ("set_variable" ("some_service_id" "local_fn_name") () remote_peer_id)) + (call (remote_peer_id ("some_service_id" "local_fn_name") () result_name)) + )) + "#, + ); - let res = vm - .call(json!(["asd", script, "{}", "{\"remote_peer_id\": \"some_peer_id\"}",])) - .expect("call should be successful"); + let res = call_vm!(set_variable_vm, "asd", script.clone(), "[]", "[]"); + let res = call_vm!(vm, "asd", script, "[]", res.data); - assert_eq!(res.next_peer_pks, vec![String::from("some_peer_id")]); + assert!(res.next_peer_pks.is_empty()); } #[test] fn string_parameters() { + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + let call_service: HostExportedFunc = Box::new(|_, args| -> Option { let arg = match &args[2] { IValue::String(str) => str, @@ -175,18 +202,30 @@ mod tests { )) }); - let mut vm = create_aqua_vm(call_service, ""); + let mut vm = create_aqua_vm(call_service, "A"); + let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#""arg3_value""#), "set_variable"); - let script = format!( - r#"(call (%current_peer_id% ("some_service_id" "local_fn_name") ("arg1" "arg2" arg3) result_name))"#, + let script = String::from( + r#" + (seq ( + (call ("set_variable" ("some_service_id" "local_fn_name") () arg3)) + (call ("A" ("some_service_id" "local_fn_name") ("arg1" "arg2" arg3) result)) + )) + "#, ); - let res = vm - .call(json!(["asd", script, "{}", json!({"arg3": "arg3_value"}).to_string(),])) - .expect("call should be successful"); + let res = call_vm!(set_variable_vm, "asd", script.clone(), "[]", "[]"); + let res = call_vm!(vm, "asd", script, "[]", res.data); + let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json"); - let jdata: JValue = serde_json::from_str(&res.data).expect("should be valid json"); - - assert_eq!(jdata["result_name"], json!(["arg1", "arg2", "arg3_value"])); + assert_eq!(call_path.len(), 2); + assert_eq!( + call_path[1], + Call(Executed(Rc::new(JValue::Array(vec![ + JValue::String(String::from("arg1")), + JValue::String(String::from("arg2")), + JValue::String(String::from("arg3_value")), + ])))) + ); } } diff --git a/stepper-lib/src/air/call/parsed_call.rs b/stepper-lib/src/air/call/parsed_call.rs new file mode 100644 index 00000000..9b241d6f --- /dev/null +++ b/stepper-lib/src/air/call/parsed_call.rs @@ -0,0 +1,290 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#![allow(unused_unsafe)] // for wasm_bindgen target where calling FFI is safe + +use super::utils::find_by_json_path; +use super::utils::is_string_literal; +use super::utils::set_local_call_result; +use super::Call; +use super::CURRENT_PEER_ALIAS; +use crate::air::ExecutionCtx; +use crate::build_targets::CALL_SERVICE_SUCCESS; +use crate::call_evidence::CallEvidenceCtx; +use crate::call_evidence::CallResult; +use crate::call_evidence::EvidenceState; +use crate::log_targets::EVIDENCE_CHANGING; +use crate::AValue; +use crate::AquamarineError; +use crate::JValue; +use crate::Result; + +use std::borrow::Cow; +use std::rc::Rc; + +#[derive(Debug, PartialEq, Eq)] +pub(super) struct ParsedCall { + peer_pk: String, + service_id: String, + function_name: String, + function_arg_paths: Vec, + result_variable_name: String, +} + +impl ParsedCall { + pub(super) fn new(raw_call: &Call, exec_ctx: &ExecutionCtx) -> Result { + let (peer_pk, service_id, function_name) = prepare_peer_fn_parts(raw_call, exec_ctx)?; + let result_variable_name = parse_result_variable_name(raw_call)?; + + Ok(Self { + peer_pk, + service_id, + function_name, + function_arg_paths: raw_call.2.clone(), + result_variable_name: result_variable_name.to_string(), + }) + } + + pub(super) fn execute(self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { + let should_executed = self.prepare_evidence_state(exec_ctx, call_ctx)?; + if !should_executed { + return Ok(()); + } + + if self.peer_pk != exec_ctx.current_peer_id && self.peer_pk != CURRENT_PEER_ALIAS { + super::utils::set_remote_call_result(self.peer_pk, exec_ctx, call_ctx); + + return Ok(()); + } + + let function_args = extract_args_by_paths(&self.function_arg_paths, exec_ctx)?; + let function_args = serde_json::to_string(&function_args) + .map_err(|e| AquamarineError::FuncArgsSerializationError(function_args, e))?; + + let result = unsafe { crate::call_service(self.service_id, self.function_name, function_args) }; + + if result.ret_code != CALL_SERVICE_SUCCESS { + call_ctx + .new_path + .push_back(EvidenceState::Call(CallResult::CallServiceFailed( + result.result.clone(), + ))); + return Err(AquamarineError::LocalServiceError(result.result)); + } + + let result: JValue = serde_json::from_str(&result.result) + .map_err(|e| AquamarineError::CallServiceResultDeserializationError(result, e))?; + let result = Rc::new(result); + super::utils::set_local_call_result(self.result_variable_name, exec_ctx, result.clone())?; + + let new_evidence_state = EvidenceState::Call(CallResult::Executed(result)); + log::info!( + target: EVIDENCE_CHANGING, + " adding new call evidence state {:?}", + new_evidence_state + ); + call_ctx.new_path.push_back(new_evidence_state); + + Ok(()) + } + + pub(super) fn prepare_evidence_state( + &self, + exec_ctx: &mut ExecutionCtx, + call_ctx: &mut CallEvidenceCtx, + ) -> Result { + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + + if call_ctx.current_subtree_elements_count == 0 { + log::info!(target: EVIDENCE_CHANGING, " previous call evidence state wasn't found"); + return Ok(true); + } + + call_ctx.current_subtree_elements_count -= 1; + // unwrap is safe here, because current_subtree_elements_count depends on current_path len, + // and it's been checked previously + let prev_state = call_ctx.current_path.pop_front().unwrap(); + + log::info!( + target: EVIDENCE_CHANGING, + " previous call evidence state found {:?}", + prev_state + ); + + match &prev_state { + // this call was failed on one of the previous executions, + // here it's needed to bubble this special error up + Call(CallServiceFailed(err_msg)) => { + let err_msg = err_msg.clone(); + call_ctx.new_path.push_back(prev_state); + exec_ctx.subtree_complete = false; + Err(AquamarineError::LocalServiceError(err_msg)) + } + Call(RequestSent(..)) => { + // check whether current node can execute this call + let is_current_peer = self.peer_pk == exec_ctx.current_peer_id; + if is_current_peer { + Ok(true) + } else { + exec_ctx.subtree_complete = false; + call_ctx.new_path.push_back(prev_state); + Ok(false) + } + } + // this instruction's been already executed + Call(Executed(result)) => { + set_local_call_result(self.result_variable_name.clone(), exec_ctx, result.clone())?; + call_ctx.new_path.push_back(prev_state); + Ok(false) + } + // state has inconsistent order - return a error, call shouldn't be executed + par_state @ Par(..) => Err(AquamarineError::InvalidEvidenceState( + par_state.clone(), + String::from("call"), + )), + } + } +} + +fn prepare_peer_fn_parts<'a>(raw_call: &'a Call, exec_ctx: &'a ExecutionCtx) -> Result<(String, String, String)> { + use super::FunctionPart::*; + use super::PeerPart::*; + + let (peer_pk, service_id, func_name) = match (&raw_call.0, &raw_call.1) { + (PeerPkWithServiceId(peer_pk, peer_service_id), ServiceIdWithFuncName(_service_id, func_name)) => { + Ok((peer_pk, peer_service_id, func_name)) + } + (PeerPkWithServiceId(peer_pk, peer_service_id), FuncName(func_name)) => { + Ok((peer_pk, peer_service_id, func_name)) + } + (PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => Ok((peer_pk, service_id, func_name)), + (PeerPk(_), FuncName(_)) => Err(AquamarineError::InstructionError(String::from( + "call should have service id specified by peer part or function part", + ))), + }?; + + let peer_pk = if peer_pk != CURRENT_PEER_ALIAS { + prepare_call_arg(peer_pk, exec_ctx)? + } else { + peer_pk.to_string() + }; + + let service_id = prepare_call_arg(service_id, exec_ctx)?; + let func_name = prepare_call_arg(func_name, exec_ctx)?; + + Ok((peer_pk, service_id, func_name)) +} + +fn extract_args_by_paths(function_arg_paths: &[String], ctx: &ExecutionCtx) -> Result { + let mut result = Vec::with_capacity(function_arg_paths.len()); + let owned_maybe_json_path = |jvalue: Cow<'_, JValue>, json_path: Option<&str>| -> Result> { + if json_path.is_none() { + return Ok(vec![jvalue.into_owned()]); + } + + let json_path = json_path.unwrap(); + let values = find_by_json_path(jvalue.as_ref(), json_path)?; + Ok(values.into_iter().cloned().collect()) + }; + + for arg_path in function_arg_paths.iter() { + if is_string_literal(arg_path) { + result.push(JValue::String(arg_path[1..arg_path.len() - 1].to_string())); + } else { + let arg = get_args_by_path(arg_path, ctx, owned_maybe_json_path)?; + result.extend(arg); + } + } + + Ok(JValue::Array(result)) +} + +fn parse_result_variable_name(call: &Call) -> Result<&str> { + let result_variable_name = &call.3; + + if result_variable_name.is_empty() { + return Err(AquamarineError::InstructionError(String::from( + "result name of a call instruction must be non empty", + ))); + } + + if is_string_literal(result_variable_name) { + return Err(AquamarineError::InstructionError(String::from( + "result name of a call instruction must be non string literal", + ))); + } + + Ok(result_variable_name) +} + +fn get_args_by_path<'args_path, 'exec_ctx, T: 'exec_ctx>( + args_path: &'args_path str, + ctx: &'exec_ctx ExecutionCtx, + maybe_json_path: impl FnOnce(Cow<'exec_ctx, JValue>, Option<&str>) -> Result, +) -> Result { + let mut split_arg: Vec<&str> = args_path.splitn(2, '.').collect(); + let arg_path_head = split_arg.remove(0); + + match ctx.data_cache.get(arg_path_head) { + Some(AValue::JValueFoldCursor(fold_state)) => match fold_state.iterable.as_ref() { + JValue::Array(array) => { + let jvalue = &array[fold_state.cursor]; + maybe_json_path(Cow::Borrowed(jvalue), split_arg.pop()) + } + _ => unreachable!("fold state must be well-formed because it is changed only by stepper"), + }, + Some(AValue::JValueRef(value)) => maybe_json_path(Cow::Borrowed(value.as_ref()), split_arg.pop()), + Some(AValue::JValueAccumulatorRef(acc)) => { + let owned_acc = acc.borrow().iter().map(|v| v.as_ref()).cloned().collect::>(); + let jvalue = JValue::Array(owned_acc); + maybe_json_path(Cow::Owned(jvalue), split_arg.pop()) + } + None => Err(AquamarineError::VariableNotFound(arg_path_head.to_string())), + } +} + +// Prepare arguments of call +fn prepare_call_arg<'a>(arg_path: &'a str, ctx: &'a ExecutionCtx) -> Result { + fn borrowed_maybe_json_path(jvalue: Cow<'_, JValue>, json_path: Option<&str>) -> Result { + if json_path.is_none() { + return Ok(jvalue.into_owned()); + } + + let json_path = json_path.unwrap(); + let values = find_by_json_path(jvalue.as_ref(), json_path)?; + if values.is_empty() { + return Err(AquamarineError::VariableNotFound(json_path.to_string())); + } + + if values.len() != 1 { + return Err(AquamarineError::MultipleValuesInJsonPath(json_path.to_string())); + } + + Ok(values[0].clone()) + } + + if is_string_literal(arg_path) { + return Ok(arg_path[1..arg_path.len() - 1].to_string()); + } + + let arg = get_args_by_path(arg_path, ctx, borrowed_maybe_json_path)?; + + match arg { + JValue::String(str) => Ok(str), + v => Err(AquamarineError::IncompatibleJValueType(v, String::from("string"))), + } +} diff --git a/stepper-lib/src/air/call/utils.rs b/stepper-lib/src/air/call/utils.rs new file mode 100644 index 00000000..01b14d60 --- /dev/null +++ b/stepper-lib/src/air/call/utils.rs @@ -0,0 +1,87 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionCtx; +use crate::call_evidence::CallEvidenceCtx; +use crate::call_evidence::CallResult; +use crate::call_evidence::EvidenceState; +use crate::log_targets::EVIDENCE_CHANGING; +use crate::AValue; +use crate::AquamarineError; +use crate::JValue; +use crate::Result; + +use std::cell::RefCell; +use std::rc::Rc; + +pub(super) fn set_local_call_result( + result_variable_name: String, + exec_ctx: &mut ExecutionCtx, + result: Rc, +) -> Result<()> { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + use AquamarineError::*; + + let stripped_result_name = result_variable_name.strip_suffix("[]"); + if stripped_result_name.is_none() { + // if result is not an array, simply insert it into data + match exec_ctx.data_cache.entry(result_variable_name) { + Vacant(entry) => entry.insert(AValue::JValueRef(result)), + Occupied(entry) => return Err(MultipleVariablesFound(entry.key().clone())), + }; + return Ok(()); + } + + // unwrap is safe because it's been checked for [] + match exec_ctx.data_cache.entry(stripped_result_name.unwrap().to_string()) { + Occupied(mut entry) => match entry.get_mut() { + // if result is an array, insert result to the end of the array + AValue::JValueAccumulatorRef(values) => values.borrow_mut().push(result), + v => return Err(IncompatibleAValueType(format!("{:?}", v), String::from("Array"))), + }, + Vacant(entry) => { + entry.insert(AValue::JValueAccumulatorRef(RefCell::new(vec![result]))); + } + } + + Ok(()) +} + +pub(super) fn set_remote_call_result(peer_pk: String, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) { + exec_ctx.next_peer_pks.push(peer_pk); + exec_ctx.subtree_complete = false; + + let new_evidence_state = EvidenceState::Call(CallResult::RequestSent(exec_ctx.current_peer_id.clone())); + log::info!( + target: EVIDENCE_CHANGING, + " adding new call evidence state {:?}", + new_evidence_state + ); + call_ctx.new_path.push_back(new_evidence_state); +} + +pub(super) fn find_by_json_path<'jvalue, 'json_path>( + jvalue: &'jvalue JValue, + json_path: &'json_path str, +) -> Result> { + use AquamarineError::VariableNotInJsonPath as JsonPathError; + + jsonpath_lib::select(jvalue, json_path).map_err(|e| JsonPathError(jvalue.clone(), String::from(json_path), e)) +} + +pub(super) fn is_string_literal(value: &str) -> bool { + value.starts_with('"') && value.ends_with('"') +} diff --git a/stepper/src/air/execution_context.rs b/stepper-lib/src/air/execution_context.rs similarity index 67% rename from stepper/src/air/execution_context.rs rename to stepper-lib/src/air/execution_context.rs index 87c35390..36793bb6 100644 --- a/stepper/src/air/execution_context.rs +++ b/stepper-lib/src/air/execution_context.rs @@ -14,16 +14,17 @@ * limitations under the License. */ -use super::fold::FoldState; -use crate::AquaData; +use crate::AValue; use std::collections::HashMap; +use std::fmt::Display; +use std::fmt::Formatter; /// Execution context contains all necessary information needed to execute aqua script. #[derive(Clone, Default, Debug)] pub(crate) struct ExecutionCtx { /// Contains all set variables. - pub data: AquaData, + pub data_cache: HashMap, /// Set of peer public keys that should receive resulted data. pub next_peer_pks: Vec, @@ -31,25 +32,36 @@ pub(crate) struct ExecutionCtx { /// PeerId of a peer executing this aqua script. pub current_peer_id: String, - /// Describes all met folds on the current execution step. - pub folds: HashMap, - /// Indicates that previous executed subtree is complete. /// A subtree treats as a complete if all subtree elements satisfy the following rules: /// - at least one of par subtrees is complete + /// - non-thrown subtree of xor is complete /// - all of seq subtrees are complete /// - call executes successfully (call evidence equals to Executed) pub subtree_complete: bool, } impl ExecutionCtx { - pub(crate) fn new(data: AquaData, current_peer_id: String) -> Self { + pub(crate) fn new(current_peer_id: String) -> Self { Self { - data, + data_cache: HashMap::new(), next_peer_pks: vec![], current_peer_id, - folds: HashMap::new(), subtree_complete: true, } } } + +impl Display for ExecutionCtx { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "data cache:")?; + for (key, value) in self.data_cache.iter() { + writeln!(f, " {} => {}", key, value)?; + } + writeln!(f, "current peer id: {}", self.current_peer_id)?; + writeln!(f, "subtree complete: {}", self.subtree_complete)?; + writeln!(f, "next peer public keys: {:?}", self.next_peer_pks)?; + + Ok(()) + } +} diff --git a/stepper-lib/src/air/fold.rs b/stepper-lib/src/air/fold.rs new file mode 100644 index 00000000..062c88dc --- /dev/null +++ b/stepper-lib/src/air/fold.rs @@ -0,0 +1,346 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::CallEvidenceCtx; +use super::ExecutionCtx; +use super::Instruction; +use crate::log_instruction; +use crate::AValue; +use crate::AquamarineError; +use crate::JValue; +use crate::Result; + +use serde_derive::Deserialize; +use serde_derive::Serialize; +use std::rc::Rc; + +/* + (fold Iterable i + (par + (call fn [i] acc[]) + (next i) + ) + ) +*/ + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub(crate) struct Fold(String, String, Rc); + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub(crate) struct Next(String); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct FoldState { + // TODO: maybe change to bidirectional iterator + pub(crate) cursor: usize, + pub(crate) iterable: Rc, + pub(crate) instr_head: Rc, +} + +impl super::ExecutableInstruction for Fold { + fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { + use AquamarineError::*; + + log_instruction!(fold, exec_ctx, call_ctx); + + let iterable_name = &self.0; + let iterator_name = &self.1; + let instr_head = self.2.clone(); + + // check that value exists and has array type + let iterable = match exec_ctx.data_cache.get(iterable_name) { + Some(AValue::JValueRef(jvalue_rc)) => { + match jvalue_rc.as_ref() { + JValue::Array(array) => { + if array.is_empty() { + // skip fold if array is empty + return Ok(()); + } + + jvalue_rc + } + v => return Err(IncompatibleJValueType(v.clone(), String::from("Array"))), + } + } + Some(v) => return Err(IncompatibleAValueType(format!("{:?}", v), String::from("JValueRef"))), + None => return Err(VariableNotFound(String::from(iterable_name))), + }; + + let fold_state = FoldState { + cursor: 0, + iterable: iterable.clone(), + instr_head: instr_head.clone(), + }; + + let previous_value = exec_ctx + .data_cache + .insert(iterator_name.clone(), AValue::JValueFoldCursor(fold_state)); + + if previous_value.is_some() { + return Err(MultipleFoldStates(iterable_name.clone())); + } + + instr_head.execute(exec_ctx, call_ctx)?; + exec_ctx.data_cache.remove(iterator_name); + + Ok(()) + } +} + +impl super::ExecutableInstruction for Next { + fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { + use AquamarineError::IncompatibleAValueType; + + log_instruction!(next, exec_ctx, call_ctx); + + let iterator_name = &self.0; + let avalue = exec_ctx + .data_cache + .get_mut(iterator_name) + .ok_or_else(|| AquamarineError::FoldStateNotFound(iterator_name.clone()))?; + let fold_state = match avalue { + AValue::JValueFoldCursor(state) => state, + v => { + return Err(IncompatibleAValueType( + format!("{:?}", v), + String::from("JValueFoldCursor"), + )) + } + }; + let value_len = match fold_state.iterable.as_ref() { + JValue::Array(array) => array.len(), + _ => unreachable!("iterable value shouldn't changed inside fold"), + }; + + fold_state.cursor += 1; + if value_len == 0 || value_len <= fold_state.cursor { + fold_state.cursor -= 1; + // just do nothing to exit + return Ok(()); + } + + let next_instr = fold_state.instr_head.clone(); + next_instr.execute(exec_ctx, call_ctx)?; + + // get the same fold state again because of borrow checker + match exec_ctx.data_cache.get_mut(iterator_name) { + Some(AValue::JValueFoldCursor(fold_state)) => fold_state.cursor -= 1, + _ => unreachable!("iterator value shouldn't changed inside fold"), + }; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::call_evidence::CallEvidencePath; + use crate::JValue; + + use aqua_test_utils::call_vm; + use aqua_test_utils::create_aqua_vm; + use aqua_test_utils::echo_number_call_service; + use aqua_test_utils::set_variable_call_service; + use aquamarine_vm::AquamarineVMError; + use aquamarine_vm::StepperError; + + use serde_json::json; + use std::rc::Rc; + + #[test] + fn lfold() { + env_logger::init(); + + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + + let mut vm = create_aqua_vm(echo_number_call_service(), "A"); + let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); + + let lfold = String::from( + r#" + (seq ( + (call ("set_variable" ("" "") () Iterable)) + (fold (Iterable i + (seq ( + (call ("A" ("" "") (i) acc[])) + (next i) + )) + )) + ))"#, + ); + + let res = call_vm!(set_variable_vm, "", lfold.clone(), "[]", "[]"); + let res = call_vm!(vm, "", lfold, "[]", res.data); + let res: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid call evidence path"); + + assert_eq!(res.len(), 6); + assert_eq!(res[0], Call(Executed(Rc::new(json!(["1", "2", "3", "4", "5"]))))); + + for i in 1..=5 { + assert_eq!(res[i], Call(Executed(Rc::new(JValue::Number(i.into()))))); + } + } + + #[test] + fn rfold() { + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + + let mut vm = create_aqua_vm(echo_number_call_service(), "A"); + let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); + + let rfold = String::from( + r#" + (seq ( + (call ("set_variable" ("" "") () Iterable)) + (fold (Iterable i + (seq ( + (next i) + (call ("A" ("" "") (i) acc[])) + )) + )) + ))"#, + ); + + let res = call_vm!(set_variable_vm, "", rfold.clone(), "[]", "[]"); + let res = call_vm!(vm, "", rfold, "[]", res.data); + let res: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid call evidence path"); + + assert_eq!(res.len(), 6); + assert_eq!(res[0], Call(Executed(Rc::new(json!(["1", "2", "3", "4", "5"]))))); + + for i in 1..=5 { + assert_eq!(res[i], Call(Executed(Rc::new(JValue::Number((6 - i).into()))))); + } + } + + #[test] + fn inner_fold() { + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + + let mut vm = create_aqua_vm(echo_number_call_service(), "A"); + let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); + + let script = String::from( + r#" + (seq ( + (seq ( + (call ("set_variable" ("" "") () Iterable1)) + (call ("set_variable" ("" "") () Iterable2)) + )) + (fold (Iterable1 i + (seq ( + (fold (Iterable2 j + (seq ( + (call ("A" ("" "") (i) acc[])) + (next j) + )) + )) + (next i) + )) + )) + ))"#, + ); + + let res = call_vm!(set_variable_vm, "", script.clone(), "[]", "[]"); + let res = call_vm!(vm, "", script, "[]", res.data); + let res: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid call evidence path"); + + assert_eq!(res.len(), 27); + assert_eq!(res[0], Call(Executed(Rc::new(json!(["1", "2", "3", "4", "5"]))))); + assert_eq!(res[1], Call(Executed(Rc::new(json!(["1", "2", "3", "4", "5"]))))); + + for i in 1..=5 { + for j in 1..=5 { + assert_eq!( + res[1 + 5 * (i - 1) + j], + Call(Executed(Rc::new(JValue::Number(i.into())))) + ); + } + } + } + + #[test] + fn inner_fold_with_same_iterator() { + let mut vm = create_aqua_vm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); + + let script = String::from( + r#" + (seq ( + (seq ( + (call ("set_variable" ("" "") () Iterable1)) + (call ("set_variable" ("" "") () Iterable2)) + )) + (fold (Iterable1 i + (seq ( + (fold (Iterable2 i + (seq ( + (call ("A" ("" "") (i) acc[])) + (next i) + )) + )) + (next i) + )) + )) + ))"#, + ); + + let res = vm.call_with_prev_data("", script, "[]", "[]"); + + assert!(res.is_err()); + let error = res.err().unwrap(); + let error = match error { + AquamarineVMError::StepperError(error) => error, + _ => unreachable!(), + }; + + assert_eq!( + error, + StepperError::FoldStateNotFound(String::from("multiple fold states found for iterable Iterable2")) + ); + } + + #[test] + fn empty_fold() { + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + + let mut vm = create_aqua_vm(echo_number_call_service(), "A"); + let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#"[]"#), "set_variable"); + + let empty_fold = String::from( + r#" + (seq ( + (call ("set_variable" ("" "") () Iterable)) + (fold (Iterable i + (seq ( + (call ("A" ("" "") (i) acc[])) + (next i) + )) + )) + ))"#, + ); + + let res = call_vm!(set_variable_vm, "", empty_fold.clone(), "[]", "[]"); + let res = call_vm!(vm, "", empty_fold, "[]", res.data); + let res: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid call evidence path"); + + assert_eq!(res.len(), 1); + assert_eq!(res[0], Call(Executed(Rc::new(json!([]))))); + } +} diff --git a/stepper/src/air/mod.rs b/stepper-lib/src/air/mod.rs similarity index 58% rename from stepper/src/air/mod.rs rename to stepper-lib/src/air/mod.rs index 286f622e..922864ce 100644 --- a/stepper/src/air/mod.rs +++ b/stepper-lib/src/air/mod.rs @@ -23,6 +23,7 @@ mod seq; mod xor; pub(crate) use execution_context::ExecutionCtx; +pub(crate) use fold::FoldState; pub(self) use crate::call_evidence::CallEvidenceCtx; pub(self) use crate::call_evidence::EvidenceState; @@ -39,15 +40,6 @@ use xor::Xor; use serde_derive::Deserialize; use serde_derive::Serialize; -use once_cell::sync::Lazy; -use std::collections::HashSet; - -pub(self) static RESERVED_KEYWORDS: Lazy> = Lazy::new(|| { - let mut set = HashSet::new(); - set.insert("__call"); - set -}); - #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub(crate) enum Instruction { @@ -77,3 +69,46 @@ impl ExecutableInstruction for Instruction { } } } + +#[macro_export] +macro_rules! log_instruction { + ($instr_name:expr, $exec_ctx:expr, $call_ctx:expr) => { + log::info!(target: crate::log_targets::INSTRUCTION, "> {}", stringify!($instr_name)); + + let mut data_cache_log = String::from(" data cache:"); + if $exec_ctx.data_cache.is_empty() { + data_cache_log.push_str(" empty"); + } + for (key, value) in $exec_ctx.data_cache.iter() { + data_cache_log.push_str(&format!("\n {} => {}", key, value)); + } + + log::info!(target: crate::log_targets::DATA_CACHE, "{}", data_cache_log); + log::info!( + target: crate::log_targets::NEXT_PEER_PKS, + " next peers pk: {:?}", + $exec_ctx.next_peer_pks + ); + log::info!( + target: crate::log_targets::SUBTREE_COMPLETE, + " subtree complete: {}", + $exec_ctx.subtree_complete + ); + + log::info!( + target: crate::log_targets::CALL_EVIDENCE_PATH, + " current call evidence path: {:?}", + $call_ctx.current_path + ); + log::info!( + target: crate::log_targets::SUBTREE_ELEMENTS, + " subtree elements count: {:?}", + $call_ctx.current_subtree_elements_count + ); + log::info!( + target: crate::log_targets::NEW_CALL_EVIDENCE_PATH, + " new call evidence path: {:?}", + $call_ctx.new_path + ); + }; +} diff --git a/stepper/src/air/null.rs b/stepper-lib/src/air/null.rs similarity index 92% rename from stepper/src/air/null.rs rename to stepper-lib/src/air/null.rs index e255116b..5b8c920d 100644 --- a/stepper/src/air/null.rs +++ b/stepper-lib/src/air/null.rs @@ -16,6 +16,7 @@ use super::CallEvidenceCtx; use super::ExecutionCtx; +use crate::log_instruction; use crate::Result; use serde_derive::Deserialize; @@ -26,7 +27,7 @@ pub(crate) struct Null {} impl super::ExecutableInstruction for Null { fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!("null is called with contexts: {:?} {:?}", exec_ctx, call_ctx); + log_instruction!(null, exec_ctx, call_ctx); Ok(()) } diff --git a/stepper/src/air/par.rs b/stepper-lib/src/air/par.rs similarity index 83% rename from stepper/src/air/par.rs rename to stepper-lib/src/air/par.rs index ca9b0f21..3d119bb8 100644 --- a/stepper/src/air/par.rs +++ b/stepper-lib/src/air/par.rs @@ -19,6 +19,8 @@ use super::EvidenceState; use super::ExecutableInstruction; use super::ExecutionCtx; use super::Instruction; +use crate::log_instruction; +use crate::log_targets::EVIDENCE_CHANGING; use crate::Result; use serde_derive::Deserialize; @@ -29,7 +31,7 @@ pub(crate) struct Par(Box, Box); impl ExecutableInstruction for Par { fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!("par is called with context: {:?} {:?}", exec_ctx, call_ctx); + log_instruction!(par, exec_ctx, call_ctx); let (left_subtree_size, right_subtree_size) = extract_subtree_sizes(call_ctx)?; @@ -39,11 +41,9 @@ impl ExecutableInstruction for Par { let pre_new_states_count = call_ctx.new_path.len(); call_ctx.new_path.push_back(EvidenceState::Par(0, 0)); - exec_ctx.subtree_complete = determine_subtree_complete(&self.0); let new_left_subtree_size = execute_subtree(&self.0, left_subtree_size, exec_ctx, call_ctx)?; let left_subtree_complete = exec_ctx.subtree_complete; - exec_ctx.subtree_complete = determine_subtree_complete(&self.1); let new_right_subtree_size = execute_subtree(&self.1, right_subtree_size, exec_ctx, call_ctx)?; let right_subtree_complete = exec_ctx.subtree_complete; @@ -51,7 +51,11 @@ impl ExecutableInstruction for Par { exec_ctx.subtree_complete = left_subtree_complete || right_subtree_complete; let new_par_evidence_state = EvidenceState::Par(new_left_subtree_size, new_right_subtree_size); - log::info!("call evidence: adding new state {:?}", new_par_evidence_state); + log::info!( + target: EVIDENCE_CHANGING, + " adding new call evidence state {:?}", + new_par_evidence_state + ); call_ctx.new_path[pre_new_states_count] = new_par_evidence_state; let post_states_count = call_ctx.current_path.len(); @@ -71,7 +75,8 @@ fn extract_subtree_sizes(call_ctx: &mut CallEvidenceCtx) -> Result<(usize, usize call_ctx.current_subtree_elements_count -= 1; log::info!( - "call evidence: the previous state was found {:?}", + target: EVIDENCE_CHANGING, + " previous call evidence state was found {:?}", call_ctx.current_path[0] ); @@ -91,6 +96,8 @@ fn execute_subtree( call_ctx.current_subtree_elements_count = subtree_size; let before_states_count = call_ctx.new_path.len(); + exec_ctx.subtree_complete = determine_subtree_complete(&subtree); + // execute subtree subtree.execute(exec_ctx, call_ctx)?; @@ -106,20 +113,15 @@ fn determine_subtree_complete(next_instruction: &Instruction) -> bool { // )) // )) // par will be executed after the last next that wouldn't change subtree_complete - if let Instruction::Next(_) = next_instruction { - false - } else { - true - } + !matches!(next_instruction, Instruction::Next(_)) } #[cfg(test)] mod tests { + use aqua_test_utils::call_vm; use aqua_test_utils::create_aqua_vm; use aqua_test_utils::unit_call_service; - use serde_json::json; - #[test] fn par_remote_remote() { use std::collections::HashSet; @@ -134,14 +136,11 @@ mod tests { ))"#, ); - let mut res = vm - .call(json!(["asd", script, "{}", "{}",])) - .expect("call should be successful"); + let mut res = call_vm!(vm, "", script, "[]", "[]"); let peers_result: HashSet<_> = res.next_peer_pks.drain(..).collect(); - let peers_right: HashSet<_> = vec![String::from("remote_peer_id_1"), String::from("remote_peer_id_2")] - .drain(..) - .collect(); + let peers_right: HashSet<_> = + maplit::hashset!(String::from("remote_peer_id_1"), String::from("remote_peer_id_2")); assert_eq!(peers_result, peers_right); } @@ -158,9 +157,7 @@ mod tests { ))"#, ); - let res = vm - .call(json!(["asd", script, "{}", "{}",])) - .expect("call should be successful"); + let res = call_vm!(vm, "", script, "[]", "[]"); assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]); } diff --git a/stepper/src/air/seq.rs b/stepper-lib/src/air/seq.rs similarity index 78% rename from stepper/src/air/seq.rs rename to stepper-lib/src/air/seq.rs index 6bbf6774..da8ad845 100644 --- a/stepper/src/air/seq.rs +++ b/stepper-lib/src/air/seq.rs @@ -17,6 +17,7 @@ use super::CallEvidenceCtx; use super::ExecutionCtx; use super::Instruction; +use crate::log_instruction; use crate::Result; use serde_derive::Deserialize; @@ -27,7 +28,7 @@ pub(crate) struct Seq(Box, Box); impl super::ExecutableInstruction for Seq { fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!("seq is called with contexts: {:?} {:?}", exec_ctx, call_ctx); + log_instruction!(seq, exec_ctx, call_ctx); exec_ctx.subtree_complete = true; self.0.execute(exec_ctx, call_ctx)?; @@ -42,6 +43,7 @@ impl super::ExecutableInstruction for Seq { #[cfg(test)] mod tests { + use aqua_test_utils::call_vm; use aqua_test_utils::create_aqua_vm; use aqua_test_utils::unit_call_service; @@ -59,25 +61,10 @@ mod tests { ))"#, ); - let res = vm - .call(json!(["asd", script, "{}", "{}",])) - .expect("call should be successful"); - + let res = call_vm!(vm, "asd", script.clone(), "[]", "[]"); assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_1")]); - let res = vm - .call(json!([ - "asd", - script, - "{}", - json!({ - "__call": [{"call": "executed"}] - } - ) - .to_string(), - ])) - .expect("call should be successful"); - + let res = call_vm!(vm, "asd", script, "[]", json!([{"call": {"executed": ""}}]).to_string()); assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]); } @@ -93,10 +80,7 @@ mod tests { ))"#, ); - let res = vm - .call(json!(["asd", script, "{}", "{}",])) - .expect("call should be successful"); - + let res = call_vm!(vm, "asd", script, "[]", "[]"); assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]); } } diff --git a/stepper/src/air/xor.rs b/stepper-lib/src/air/xor.rs similarity index 72% rename from stepper/src/air/xor.rs rename to stepper-lib/src/air/xor.rs index 492d53e9..4cbc0efd 100644 --- a/stepper/src/air/xor.rs +++ b/stepper-lib/src/air/xor.rs @@ -17,6 +17,7 @@ use super::CallEvidenceCtx; use super::ExecutionCtx; use super::Instruction; +use crate::log_instruction; use crate::AquamarineError::LocalServiceError; use crate::Result; @@ -28,7 +29,7 @@ pub(crate) struct Xor(Box, Box); impl super::ExecutableInstruction for Xor { fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!("xor is called with contexts: {:?} {:?}", exec_ctx, call_ctx); + log_instruction!(xor, exec_ctx, call_ctx); exec_ctx.subtree_complete = true; match self.0.execute(exec_ctx, call_ctx) { @@ -43,17 +44,22 @@ impl super::ExecutableInstruction for Xor { #[cfg(test)] mod tests { + use crate::call_evidence::CallEvidencePath; use crate::JValue; + use aqua_test_utils::call_vm; use aqua_test_utils::create_aqua_vm; use aquamarine_vm::vec1::Vec1; use aquamarine_vm::HostExportedFunc; use aquamarine_vm::IValue; - use serde_json::json; + use std::rc::Rc; #[test] fn xor() { + use crate::call_evidence::CallResult::*; + use crate::call_evidence::EvidenceState::*; + let call_service: HostExportedFunc = Box::new(|_, args| -> Option { let builtin_service = match &args[0] { IValue::String(str) => str, @@ -63,12 +69,12 @@ mod tests { if builtin_service == "service_id_1" { // return a error for service with id service_id_1 Some(IValue::Record( - Vec1::new(vec![IValue::S32(1), IValue::String(String::from("{}"))]).unwrap(), + Vec1::new(vec![IValue::S32(1), IValue::String(String::from(r#""error""#))]).unwrap(), )) } else { // return success for services with other ids Some(IValue::Record( - Vec1::new(vec![IValue::S32(0), IValue::String(String::from("\"res\""))]).unwrap(), + Vec1::new(vec![IValue::S32(0), IValue::String(String::from(r#""res""#))]).unwrap(), )) } }); @@ -83,13 +89,15 @@ mod tests { ))"#, ); - let res = vm - .call(json!(["asd", script, "{}", json!({"arg3": "arg3_value"}).to_string(),])) - .expect("call should be successful"); + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid json"); - let jdata: JValue = serde_json::from_str(&res.data).expect("should be valid json"); - - assert_eq!(jdata["result_2"], json!("res")); + assert_eq!(call_path.len(), 2); + assert_eq!(call_path[0], Call(CallServiceFailed(String::from(r#""error""#)))); + assert_eq!( + call_path[1], + Call(Executed(Rc::new(JValue::String(String::from("res"))))) + ); let script = String::from( r#" @@ -99,12 +107,13 @@ mod tests { ))"#, ); - let res = vm - .call(json!(["asd", script, "{}", json!({"arg3": "arg3_value"}).to_string(),])) - .expect("call should be successful"); + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid json"); - let jdata: JValue = serde_json::from_str(&res.data).expect("should be valid json"); - - assert_eq!(jdata["result_1"], json!("res")); + assert_eq!(call_path.len(), 1); + assert_eq!( + call_path[0], + Call(Executed(Rc::new(JValue::String(String::from("res"))))) + ); } } diff --git a/stepper-lib/src/build_targets/fce_target.rs b/stepper-lib/src/build_targets/fce_target.rs new file mode 100644 index 00000000..a8fc6684 --- /dev/null +++ b/stepper-lib/src/build_targets/fce_target.rs @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::CallServiceResult; +use std::env::VarError; + +const CURRENT_PEER_ID_ENV_NAME: &str = "CURRENT_PEER_ID"; + +pub fn get_current_peer_id() -> std::result::Result { + std::env::var(CURRENT_PEER_ID_ENV_NAME) +} + +#[fluence::fce] +#[link(wasm_import_module = "host")] +extern "C" { + pub(crate) fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult; +} diff --git a/stepper/src/defines.rs b/stepper-lib/src/build_targets/mod.rs similarity index 63% rename from stepper/src/defines.rs rename to stepper-lib/src/build_targets/mod.rs index afdc597f..be5ec405 100644 --- a/stepper/src/defines.rs +++ b/stepper-lib/src/build_targets/mod.rs @@ -14,20 +14,15 @@ * limitations under the License. */ +#[cfg(feature = "fce")] +mod fce_target; +#[cfg(not(feature = "fce"))] +mod wasm_bindgen_target; + use serde_derive::Deserialize; use serde_derive::Serialize; -/// This file contains defines of some things similar both for FCE and browser targets. - -pub(crate) type Result = std::result::Result; -pub(crate) type AquaData = std::collections::HashMap; -pub(crate) type JValue = serde_json::Value; - -pub(crate) use crate::errors::AquamarineError; -pub(crate) use crate::stepper_outcome::StepperOutcome; -pub(crate) use crate::stepper_outcome::STEPPER_SUCCESS; - -pub(crate) const CALL_SERVICE_SUCCESS: i32 = 0; +pub const CALL_SERVICE_SUCCESS: i32 = 0; #[fluence::fce] #[derive(Debug, Clone, Serialize, Deserialize)] @@ -35,3 +30,13 @@ pub struct CallServiceResult { pub ret_code: i32, pub result: String, } + +#[cfg(feature = "fce")] +pub(crate) use fce_target::call_service; +#[cfg(feature = "fce")] +pub(crate) use fce_target::get_current_peer_id; + +#[cfg(not(feature = "fce"))] +pub(crate) use wasm_bindgen_target::call_service; +#[cfg(not(feature = "fce"))] +pub(crate) use wasm_bindgen_target::get_current_peer_id; diff --git a/stepper-lib/src/build_targets/wasm_bindgen_target.rs b/stepper-lib/src/build_targets/wasm_bindgen_target.rs new file mode 100644 index 00000000..d349eec8 --- /dev/null +++ b/stepper-lib/src/build_targets/wasm_bindgen_target.rs @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use wasm_bindgen::__rt::std::env::VarError; +use wasm_bindgen::prelude::*; + +pub(crate) fn call_service(service_id: String, fn_name: String, args: String) -> super::CallServiceResult { + let result = call_service_impl(service_id, fn_name, args); + log::info!("result {}", result); + serde_json::from_str(&result).expect("Cannot parse CallServiceResult") +} + +pub(crate) fn get_current_peer_id() -> std::result::Result { + Ok(get_current_peer_id_impl()) +} + +#[wasm_bindgen] +extern "C" { + #[link_name = "get_current_peer_id"] + fn get_current_peer_id_impl() -> String; +} + +#[wasm_bindgen(raw_module = "../src/call_service.ts")] +extern "C" { + #[link_name = "call_service"] + fn call_service_impl(service_id: String, fn_name: String, args: String) -> String; +} diff --git a/stepper/src/call_evidence/context.rs b/stepper-lib/src/call_evidence/context.rs similarity index 75% rename from stepper/src/call_evidence/context.rs rename to stepper-lib/src/call_evidence/context.rs index 4355b4f3..9bcb2f44 100644 --- a/stepper/src/call_evidence/context.rs +++ b/stepper-lib/src/call_evidence/context.rs @@ -18,6 +18,8 @@ use super::CallEvidencePath; use serde::Deserialize; use serde::Serialize; +use std::fmt::Display; +use std::fmt::Formatter; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub(crate) struct CallEvidenceCtx { @@ -37,3 +39,15 @@ impl CallEvidenceCtx { } } } + +impl Display for CallEvidenceCtx { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "current path:\n{:?}", self.current_path)?; + writeln!( + f, + "current subtree elements count:\n{:?}", + self.current_subtree_elements_count + )?; + writeln!(f, "new path:\n{:?}", self.new_path) + } +} diff --git a/stepper/src/call_evidence/mod.rs b/stepper-lib/src/call_evidence/mod.rs similarity index 86% rename from stepper/src/call_evidence/mod.rs rename to stepper-lib/src/call_evidence/mod.rs index 6303b6f0..d444b0dc 100644 --- a/stepper/src/call_evidence/mod.rs +++ b/stepper-lib/src/call_evidence/mod.rs @@ -19,6 +19,6 @@ mod state; pub(crate) use context::CallEvidenceCtx; pub(crate) use state::merge_call_paths; -pub(crate) use state::CallEvidencePath; -pub(crate) use state::CallResult; -pub(crate) use state::EvidenceState; +pub use state::CallEvidencePath; +pub use state::CallResult; +pub use state::EvidenceState; diff --git a/stepper/src/call_evidence/state.rs b/stepper-lib/src/call_evidence/state.rs similarity index 58% rename from stepper/src/call_evidence/state.rs rename to stepper-lib/src/call_evidence/state.rs index a8bdff4c..7449a6af 100644 --- a/stepper/src/call_evidence/state.rs +++ b/stepper-lib/src/call_evidence/state.rs @@ -14,22 +14,25 @@ * limitations under the License. */ +use crate::log_targets::EVIDENCE_PATH_MERGE; +use crate::JValue; use crate::Result; use serde::Deserialize; use serde::Serialize; use std::cmp::max; +use std::rc::Rc; -pub(crate) type CallEvidencePath = std::collections::VecDeque; +pub type CallEvidencePath = std::collections::VecDeque; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] -pub(crate) enum CallResult { - /// Request was sent to a target node and it shouldn't be called again. - RequestSent, +pub enum CallResult { + /// Request was sent to a target node by node with such public key and it shouldn't be called again. + RequestSent(String), - /// A corresponding call's been already executed. - Executed, + /// A corresponding call's been already executed with such value and result. + Executed(Rc), /// call_service ended with a service error. CallServiceFailed(String), @@ -37,7 +40,7 @@ pub(crate) enum CallResult { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] -pub(crate) enum EvidenceState { +pub enum EvidenceState { Par(usize, usize), Call(CallResult), } @@ -59,7 +62,7 @@ pub(crate) fn merge_call_paths( &mut merged_path, )?; - log::info!("merged path: {:?}", merged_path); + log::info!(target: EVIDENCE_PATH_MERGE, "merged path: {:?}", merged_path); Ok(merged_path) } @@ -145,14 +148,26 @@ fn merge_call(prev_call_result: CallResult, current_call_result: CallResult) -> } Ok(current_call_result) } - (RequestSent, CallServiceFailed(_)) => Ok(current_call_result), - (CallServiceFailed(_), RequestSent) => Ok(prev_call_result), - (RequestSent, RequestSent) => Ok(prev_call_result), - (RequestSent, Executed) => Ok(current_call_result), - (Executed, RequestSent) => Ok(prev_call_result), - (Executed, Executed) => Ok(prev_call_result), - (CallServiceFailed(_), Executed) => Err(IncompatibleCallResults(prev_call_result, current_call_result)), - (Executed, CallServiceFailed(_)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)), + (RequestSent(_), CallServiceFailed(_)) => Ok(current_call_result), + (CallServiceFailed(_), RequestSent(_)) => Ok(prev_call_result), + (RequestSent(prev_sender), RequestSent(sender)) => { + if prev_sender != sender { + return Err(IncompatibleCallResults(prev_call_result, current_call_result)); + } + + Ok(prev_call_result) + } + (RequestSent(_), Executed(..)) => Ok(current_call_result), + (Executed(..), RequestSent(_)) => Ok(prev_call_result), + (Executed(prev_result), Executed(result)) => { + if prev_result != result { + return Err(IncompatibleCallResults(prev_call_result, current_call_result)); + } + + Ok(prev_call_result) + } + (CallServiceFailed(_), Executed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)), + (Executed(..), CallServiceFailed(_)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)), } } @@ -161,6 +176,9 @@ mod tests { use crate::call_evidence::CallResult; use crate::call_evidence::EvidenceState; use crate::call_evidence::{merge_call_paths, CallEvidencePath}; + use crate::JValue; + + use std::rc::Rc; #[test] fn merge_call_states_1() { @@ -169,29 +187,29 @@ mod tests { let mut prev_path = CallEvidencePath::new(); prev_path.push_back(Par(1, 1)); - prev_path.push_back(Call(RequestSent)); - prev_path.push_back(Call(Executed)); + prev_path.push_back(Call(RequestSent(String::from("peer_1")))); + prev_path.push_back(Call(Executed(Rc::new(JValue::Null)))); prev_path.push_back(Par(1, 1)); - prev_path.push_back(Call(RequestSent)); - prev_path.push_back(Call(Executed)); + prev_path.push_back(Call(RequestSent(String::from("peer_3")))); + prev_path.push_back(Call(Executed(Rc::new(JValue::Null)))); let mut current_path = CallEvidencePath::new(); current_path.push_back(Par(1, 1)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(RequestSent(String::from("peer_2")))); current_path.push_back(Par(1, 1)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(RequestSent(String::from("peer_4")))); let merged_path = merge_call_paths(prev_path, current_path).expect("merging should be successful"); let mut right_merged_path = CallEvidencePath::new(); right_merged_path.push_back(Par(1, 1)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); right_merged_path.push_back(Par(1, 1)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); assert_eq!(merged_path, right_merged_path); } @@ -203,32 +221,32 @@ mod tests { let mut prev_path = CallEvidencePath::new(); prev_path.push_back(Par(1, 0)); - prev_path.push_back(Call(RequestSent)); + prev_path.push_back(Call(RequestSent(String::from("peer_1")))); prev_path.push_back(Par(1, 1)); - prev_path.push_back(Call(RequestSent)); - prev_path.push_back(Call(Executed)); + prev_path.push_back(Call(RequestSent(String::from("peer_2")))); + prev_path.push_back(Call(Executed(Rc::new(JValue::Null)))); let mut current_path = CallEvidencePath::new(); current_path.push_back(Par(2, 2)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(RequestSent(String::from("peer_1")))); current_path.push_back(Par(1, 1)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(RequestSent(String::from("peer_2")))); let merged_path = merge_call_paths(prev_path, current_path).expect("merging should be successful"); let mut right_merged_path = CallEvidencePath::new(); right_merged_path.push_back(Par(2, 2)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(RequestSent)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(RequestSent(String::from("peer_1")))); right_merged_path.push_back(Par(1, 1)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); assert_eq!(merged_path, right_merged_path); } @@ -239,43 +257,43 @@ mod tests { use EvidenceState::*; let mut prev_path = CallEvidencePath::new(); - prev_path.push_back(Call(Executed)); + prev_path.push_back(Call(Executed(Rc::new(JValue::Null)))); prev_path.push_back(Par(2, 0)); prev_path.push_back(Par(1, 0)); - prev_path.push_back(Call(RequestSent)); + prev_path.push_back(Call(RequestSent(String::from("peer_1")))); prev_path.push_back(Par(1, 2)); - prev_path.push_back(Call(RequestSent)); - prev_path.push_back(Call(Executed)); - prev_path.push_back(Call(RequestSent)); + prev_path.push_back(Call(RequestSent(String::from("peer_1")))); + prev_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + prev_path.push_back(Call(RequestSent(String::from("peer_1")))); let mut current_path = CallEvidencePath::new(); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); current_path.push_back(Par(3, 3)); current_path.push_back(Par(1, 1)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(Executed)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); current_path.push_back(Par(1, 1)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(RequestSent(String::from("peer_1")))); current_path.push_back(Par(1, 1)); - current_path.push_back(Call(Executed)); - current_path.push_back(Call(RequestSent)); + current_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + current_path.push_back(Call(RequestSent(String::from("peer_1")))); let merged_path = merge_call_paths(prev_path, current_path).expect("merging should be successful"); let mut right_merged_path = CallEvidencePath::new(); - right_merged_path.push_back(Call(Executed)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); right_merged_path.push_back(Par(3, 3)); right_merged_path.push_back(Par(1, 1)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); right_merged_path.push_back(Par(1, 1)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(RequestSent)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(RequestSent(String::from("peer_1")))); right_merged_path.push_back(Par(1, 2)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(Executed)); - right_merged_path.push_back(Call(RequestSent)); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null)))); + right_merged_path.push_back(Call(RequestSent(String::from("peer_1")))); assert_eq!(merged_path, right_merged_path); } diff --git a/stepper/src/errors.rs b/stepper-lib/src/errors.rs similarity index 76% rename from stepper/src/errors.rs rename to stepper-lib/src/errors.rs index fd3dce4f..2c10cd63 100644 --- a/stepper/src/errors.rs +++ b/stepper-lib/src/errors.rs @@ -14,8 +14,9 @@ * limitations under the License. */ -use crate::call_evidence::{CallResult, EvidenceState}; -use crate::CallServiceResult; +use crate::build_targets::CallServiceResult; +use crate::call_evidence::CallResult; +use crate::call_evidence::EvidenceState; use crate::JValue; use crate::StepperOutcome; @@ -28,16 +29,10 @@ use std::env::VarError; use std::error::Error; #[derive(Debug)] -pub(crate) enum AquamarineError { +pub enum AquamarineError { /// Errors occurred while parsing aqua script in the form of S expressions. SExprParseError(SExprError), - /// Errors occurred on aqua data deserialization. - DataDeserializationError(SerdeJsonError), - - /// Errors occurred on aqua data serialization. - DataSerializationError(SerdeJsonError), - /// Errors occurred while parsing function arguments of an expression. FuncArgsSerializationError(JValue, SerdeJsonError), @@ -62,9 +57,12 @@ pub(crate) enum AquamarineError { /// Value with such path wasn't found in data with such error. VariableNotInJsonPath(JValue, String, JsonPathError), - /// Value for such name isn't presence in data. + /// Provided JValue has incompatible with target type. IncompatibleJValueType(JValue, String), + /// Provided AValue has incompatible with target type. + IncompatibleAValueType(String, String), + /// Multiple values found for such json path. MultipleValuesInJsonPath(String), @@ -83,9 +81,6 @@ pub(crate) enum AquamarineError { /// Errors occurred on call evidence serialization. CallEvidenceSerializationError(SerdeJsonError), - /// Errors occurred when reserved keyword is used for variable name. - ReservedKeywordError(String), - /// Errors occurred when previous and current evidence states are incompatible. IncompatibleEvidenceStates(EvidenceState, EvidenceState), @@ -102,12 +97,6 @@ impl std::fmt::Display for AquamarineError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match self { AquamarineError::SExprParseError(err) => write!(f, "aqua script can't be parsed: {:?}", err), - AquamarineError::DataDeserializationError(err) => { - write!(f, "an error occurred while data deserialization: {:?}", err) - } - AquamarineError::DataSerializationError(err) => { - write!(f, "an error occurred while data serialization: {:?}", err) - } AquamarineError::FuncArgsSerializationError(args, err) => write!( f, "function arguments {} can't be serialized or deserialized with an error: {:?}", @@ -137,7 +126,10 @@ impl std::fmt::Display for AquamarineError { json_path, value, json_path_err ), AquamarineError::IncompatibleJValueType(jvalue, desired_type) => { - write!(f, "got avalue \"{:?}\", but {} type is needed", jvalue, desired_type,) + write!(f, "got jvalue \"{:?}\", but {} type is needed", jvalue, desired_type,) + } + AquamarineError::IncompatibleAValueType(avalue, desired_type) => { + write!(f, "got avalue {}, but {} type is needed", avalue, desired_type,) } AquamarineError::MultipleValuesInJsonPath(json_path) => { write!(f, "multiple variables found for this json path {}", json_path) @@ -159,11 +151,6 @@ impl std::fmt::Display for AquamarineError { AquamarineError::CallEvidenceSerializationError(err) => { write!(f, "an error occurred while data serialization: {:?}", err) } - AquamarineError::ReservedKeywordError(variable_name) => write!( - f, - "a variable can't be named as {} because this name is reserved", - variable_name - ), AquamarineError::IncompatibleEvidenceStates(prev_state, current_state) => write!( f, "previous and current data have incompatible states: {:?} {:?}", @@ -199,32 +186,30 @@ impl Into for AquamarineError { fn into(self) -> StepperOutcome { let ret_code = match self { AquamarineError::SExprParseError(_) => 1, - AquamarineError::DataDeserializationError(..) => 2, - AquamarineError::DataSerializationError(..) => 3, - AquamarineError::FuncArgsSerializationError(..) => 4, - AquamarineError::CallServiceResultDeserializationError(..) => 5, - AquamarineError::CurrentPeerIdEnvError(..) => 6, - AquamarineError::InstructionError(..) => 7, - AquamarineError::LocalServiceError(..) => 8, - AquamarineError::VariableNotFound(..) => 9, - AquamarineError::MultipleVariablesFound(..) => 10, - AquamarineError::VariableNotInJsonPath(..) => 11, - AquamarineError::IncompatibleJValueType(..) => 12, - AquamarineError::MultipleValuesInJsonPath(..) => 13, - AquamarineError::FoldStateNotFound(..) => 14, - AquamarineError::MultipleFoldStates(..) => 15, - AquamarineError::InvalidEvidenceState(..) => 16, - AquamarineError::CallEvidenceDeserializationError(..) => 17, - AquamarineError::CallEvidenceSerializationError(..) => 18, - AquamarineError::ReservedKeywordError(..) => 19, - AquamarineError::IncompatibleEvidenceStates(..) => 20, - AquamarineError::IncompatibleCallResults(..) => 21, - AquamarineError::EvidencePathTooSmall(..) => 21, + AquamarineError::FuncArgsSerializationError(..) => 2, + AquamarineError::CallServiceResultDeserializationError(..) => 3, + AquamarineError::CurrentPeerIdEnvError(..) => 4, + AquamarineError::InstructionError(..) => 5, + AquamarineError::LocalServiceError(..) => 6, + AquamarineError::VariableNotFound(..) => 7, + AquamarineError::MultipleVariablesFound(..) => 8, + AquamarineError::VariableNotInJsonPath(..) => 9, + AquamarineError::IncompatibleJValueType(..) => 10, + AquamarineError::IncompatibleAValueType(..) => 11, + AquamarineError::MultipleValuesInJsonPath(..) => 12, + AquamarineError::FoldStateNotFound(..) => 13, + AquamarineError::MultipleFoldStates(..) => 14, + AquamarineError::InvalidEvidenceState(..) => 15, + AquamarineError::CallEvidenceDeserializationError(..) => 16, + AquamarineError::CallEvidenceSerializationError(..) => 17, + AquamarineError::IncompatibleEvidenceStates(..) => 18, + AquamarineError::IncompatibleCallResults(..) => 19, + AquamarineError::EvidencePathTooSmall(..) => 20, }; StepperOutcome { ret_code, - data: format!("{}", self), + call_path: format!("{}", self), next_peer_pks: vec![], } } diff --git a/stepper/src/execution.rs b/stepper-lib/src/execution.rs similarity index 55% rename from stepper/src/execution.rs rename to stepper-lib/src/execution.rs index da932c79..c275e01c 100644 --- a/stepper/src/execution.rs +++ b/stepper-lib/src/execution.rs @@ -14,47 +14,41 @@ * limitations under the License. */ -mod epilog; mod prolog; mod utils; -use epilog::make_result_data; use prolog::make_contexts; use prolog::prepare; use utils::dedup; use crate::air::ExecutableInstruction; +use crate::AquamarineError::CallEvidenceSerializationError as CallSeError; use crate::Result; use crate::StepperOutcome; use crate::STEPPER_SUCCESS; -pub(self) const CALL_EVIDENCE_CTX_KEY: &str = "__call"; - -pub(crate) fn execute_aqua(init_user_id: String, aqua: String, prev_data: String, data: String) -> StepperOutcome { - log::info!("aquamarine version is {}", env!("CARGO_PKG_VERSION")); - +pub fn execute_aqua(init_user_id: String, aqua: String, prev_data: String, data: String) -> StepperOutcome { log::info!( - "stepper invoked with user_id = {}, aqua = {:?}, prev_data = {:?}, data = {:?}", - init_user_id, - aqua, - prev_data, - data + "aquamarine version is {}, init user id is {}", + env!("CARGO_PKG_VERSION"), + init_user_id ); execute_aqua_impl(init_user_id, aqua, prev_data, data).unwrap_or_else(Into::into) } -fn execute_aqua_impl(_init_user_id: String, aqua: String, prev_data: String, data: String) -> Result { - let (prev_data, data, aqua) = prepare(prev_data, data, aqua)?; - let (mut exec_ctx, mut call_ctx) = make_contexts(prev_data, data)?; +fn execute_aqua_impl(_init_user_id: String, aqua: String, prev_path: String, path: String) -> Result { + let (prev_path, path, aqua) = prepare(prev_path, path, aqua)?; + let (mut exec_ctx, mut call_ctx) = make_contexts(prev_path, path)?; aqua.execute(&mut exec_ctx, &mut call_ctx)?; - let data = make_result_data(exec_ctx.data, call_ctx)?; + let next_peer_pks = dedup(exec_ctx.next_peer_pks); + let serialized_call_path = serde_json::to_string(&call_ctx.new_path).map_err(CallSeError)?; Ok(StepperOutcome { ret_code: STEPPER_SUCCESS, - data, - next_peer_pks: dedup(exec_ctx.next_peer_pks), + call_path: serialized_call_path, + next_peer_pks, }) } diff --git a/stepper-lib/src/execution/prolog.rs b/stepper-lib/src/execution/prolog.rs new file mode 100644 index 00000000..6af2935d --- /dev/null +++ b/stepper-lib/src/execution/prolog.rs @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::utils::format_aqua; +use crate::air::ExecutionCtx; +use crate::air::Instruction; +use crate::call_evidence::merge_call_paths; +use crate::call_evidence::CallEvidenceCtx; +use crate::get_current_peer_id; +use crate::log_targets::RUN_PARAMS; +use crate::AquamarineError; +use crate::CallEvidencePath; +use crate::Result; + +/// Parse and prepare supplied data and aqua script. +pub(super) fn prepare( + raw_prev_path: String, + raw_path: String, + raw_aqua: String, +) -> Result<(CallEvidencePath, CallEvidencePath, Instruction)> { + use AquamarineError::CallEvidenceDeserializationError as CallDeError; + + let prev_path: CallEvidencePath = serde_json::from_str(&raw_prev_path).map_err(CallDeError)?; + let path: CallEvidencePath = serde_json::from_str(&raw_path).map_err(CallDeError)?; + + let formatted_aqua = format_aqua(raw_aqua); + let aqua: Instruction = serde_sexpr::from_str(&formatted_aqua)?; + + log::info!( + target: RUN_PARAMS, + "aqua: {:?}\nprev_path: {:?}\ncurrent_path: {:?}", + aqua, + prev_path, + path + ); + + Ok((prev_path, path, aqua)) +} + +/// Make execution and call evidence contexts from supplied data. +/// Internally, it unites variable from previous and current data and merges call evidence paths. +pub(super) fn make_contexts( + prev_path: CallEvidencePath, + path: CallEvidencePath, +) -> Result<(ExecutionCtx, CallEvidenceCtx)> { + use AquamarineError::CurrentPeerIdEnvError as EnvError; + + let current_peer_id = get_current_peer_id().map_err(|e| EnvError(e, String::from("CURRENT_PEER_ID")))?; + log::info!(target: RUN_PARAMS, "current peer id {}", current_peer_id); + + let exec_ctx = ExecutionCtx::new(current_peer_id); + let current_path = merge_call_paths(prev_path, path)?; + let call_evidence_ctx = CallEvidenceCtx::new(current_path); + + Ok((exec_ctx, call_evidence_ctx)) +} diff --git a/stepper/src/execution/utils.rs b/stepper-lib/src/execution/utils.rs similarity index 100% rename from stepper/src/execution/utils.rs rename to stepper-lib/src/execution/utils.rs diff --git a/stepper-lib/src/lib.rs b/stepper-lib/src/lib.rs new file mode 100644 index 00000000..451c6f05 --- /dev/null +++ b/stepper-lib/src/lib.rs @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#![allow(improper_ctypes)] +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +mod air; +mod build_targets; +mod call_evidence; +mod errors; +mod execution; +pub mod log_targets; +mod stepper_outcome; + +pub use crate::call_evidence::CallEvidencePath; +pub use crate::call_evidence::CallResult; +pub use crate::call_evidence::EvidenceState; +pub use crate::errors::AquamarineError; +pub use crate::stepper_outcome::StepperOutcome; +pub use crate::stepper_outcome::STEPPER_SUCCESS; +pub use execution::execute_aqua; + +pub(crate) type Result = std::result::Result; +pub(crate) type JValue = serde_json::Value; + +pub(crate) use build_targets::call_service; +pub(crate) use build_targets::get_current_peer_id; + +use std::cell::RefCell; +use std::fmt::Display; +use std::fmt::Formatter; +use std::rc::Rc; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub(crate) enum AValue { + JValueRef(Rc), + JValueAccumulatorRef(RefCell>>), + JValueFoldCursor(crate::air::FoldState), +} + +impl Display for AValue { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AValue::JValueRef(value) => write!(f, "{:?}", value)?, + AValue::JValueAccumulatorRef(acc) => { + write!(f, "[ ")?; + for value in acc.borrow().iter() { + write!(f, "{:?} ", value)?; + } + write!(f, "]")?; + } + AValue::JValueFoldCursor(fold_state) => { + write!(f, "cursor: {}, iterable: {}", fold_state.cursor, fold_state.iterable)?; + } + } + + Ok(()) + } +} diff --git a/stepper-lib/src/log_targets.rs b/stepper-lib/src/log_targets.rs new file mode 100644 index 00000000..078d8c4f --- /dev/null +++ b/stepper-lib/src/log_targets.rs @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Print out each instruction name at the beginning of its execution. +pub const INSTRUCTION: &str = "instruction"; + +/// Print out data cache at the beginning of each instruction execution. +pub const DATA_CACHE: &str = "data_cache"; + +/// Print out next_peer_pks at the beginning of each instruction execution. +pub const NEXT_PEER_PKS: &str = "next_peer_pks"; + +/// Print out subtree_complete value at the beginning of each instruction execution. +pub const SUBTREE_COMPLETE: &str = "subtree_complete"; + +/// Print out current call_evidence path at the beginning of each instruction execution. +pub const CALL_EVIDENCE_PATH: &str = "call_evidence_path"; + +/// Print out count of element in the current subtree at the beginning of each instruction execution. +pub const SUBTREE_ELEMENTS: &str = "subtree_elements_count"; + +/// Print out state of data cache at the beginning of each instruction execution. +pub const NEW_CALL_EVIDENCE_PATH: &str = "new_call_evidence_path"; + +/// Print out logs at the evidence merging stage. +pub const EVIDENCE_PATH_MERGE: &str = "evidence_merge"; + +/// Print out running arguments and params of a script. +pub const RUN_PARAMS: &str = "initial_params"; + +/// Print out state of data cache at the beginning of each instruction execution. +pub const EVIDENCE_CHANGING: &str = "evidence_changing"; + +/// This map should be used by rust-sdk logger that allows print only necessary targets by id. +pub const TARGET_MAP: [(&str, i64); 10] = [ + (INSTRUCTION, 1 << 1), + (DATA_CACHE, 1 << 2), + (NEXT_PEER_PKS, 1 << 3), + (SUBTREE_COMPLETE, 1 << 4), + (CALL_EVIDENCE_PATH, 1 << 5), + (SUBTREE_ELEMENTS, 1 << 6), + (NEW_CALL_EVIDENCE_PATH, 1 << 7), + (EVIDENCE_PATH_MERGE, 1 << 8), + (RUN_PARAMS, 1 << 9), + (EVIDENCE_CHANGING, 1 << 9), +]; diff --git a/stepper/src/stepper_outcome.rs b/stepper-lib/src/stepper_outcome.rs similarity index 97% rename from stepper/src/stepper_outcome.rs rename to stepper-lib/src/stepper_outcome.rs index f3c1460f..b836b53c 100644 --- a/stepper/src/stepper_outcome.rs +++ b/stepper-lib/src/stepper_outcome.rs @@ -26,7 +26,7 @@ pub struct StepperOutcome { pub ret_code: i32, /// Contains data if ret_code == 0, otherwise error message (that could be empty string). - pub data: String, + pub call_path: String, /// Public keys of peers that should receive data. pub next_peer_pks: Vec, diff --git a/stepper-lib/tests/air_basic.rs b/stepper-lib/tests/air_basic.rs new file mode 100644 index 00000000..68fcc948 --- /dev/null +++ b/stepper-lib/tests/air_basic.rs @@ -0,0 +1,196 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use aqua_test_utils::call_vm; +use aqua_test_utils::create_aqua_vm; +use aqua_test_utils::set_variables_call_service; +use aqua_test_utils::unit_call_service; +use aquamarine_vm::vec1::Vec1; +use aquamarine_vm::HostExportedFunc; +use aquamarine_vm::IValue; + +use serde_json::json; + +use std::rc::Rc; + +type JValue = serde_json::Value; + +#[test] +fn seq_par_call() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let vm_peer_id = String::from("some_peer_id"); + let mut vm = create_aqua_vm(unit_call_service(), vm_peer_id.clone()); + + let script = String::from( + r#" + (seq ( + (par ( + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_1)) + (call ("remote_peer_id" ("service_id" "fn_name") () g)) + )) + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) + ))"#, + ); + + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let resulted_path: Vec = serde_json::from_str(&res.data).expect("stepper should return valid json"); + + let test_string = String::from("test"); + let right_path = vec![ + Par(1, 1), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(RequestSent(vm_peer_id)), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + ]; + + assert_eq!(resulted_path, right_path); + assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); +} + +#[test] +fn par_par_call() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let vm_peer_id = String::from("some_peer_id"); + let mut vm = create_aqua_vm(unit_call_service(), vm_peer_id.clone()); + + let script = String::from( + r#" + (par ( + (par ( + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_1)) + (call ("remote_peer_id" ("service_id" "fn_name") () g)) + )) + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) + ))"#, + ); + + let res = call_vm!(vm, "asd", script, "[]", "[]"); + let resulted_path: Vec = serde_json::from_str(&res.data).expect("stepper should return valid json"); + + let test_string = String::from("test"); + let right_path = vec![ + Par(3, 1), + Par(1, 1), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(RequestSent(vm_peer_id)), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + ]; + + assert_eq!(resulted_path, right_path); + assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); +} + +#[test] +fn create_service() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let module = "greeting"; + let module_config = json!( + { + "name": module, + "mem_pages_count": 100, + "logger_enabled": true, + "wasi": { + "envs": json!({}), + "preopened_files": vec!["/tmp"], + "mapped_dirs": json!({}), + } + } + ); + + let module_bytes = json!([1, 2]); + let blueprint = json!({ "name": "blueprint", "dependencies": [module]}); + + let variables_mapping = maplit::hashmap!( + String::from("module_bytes") => module_bytes.to_string(), + String::from("module_config") => module_config.to_string(), + String::from("blueprint") => blueprint.to_string(), + ); + + let mut set_variables_vm = create_aqua_vm(set_variables_call_service(variables_mapping), "set_variables"); + + let add_module_response = String::from("add_module response"); + let add_blueprint_response = String::from("add_blueprint response"); + let create_response = String::from("create response"); + + let call_service: HostExportedFunc = Box::new(move |_, args| -> Option { + let builtin_service = match &args[0] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let response = match builtin_service.as_str() { + "add_module" => add_module_response.clone(), + "add_blueprint" => add_blueprint_response.clone(), + "create" => create_response.clone(), + _ => String::from("unknown response"), + }; + + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(format!("\"{}\"", response))]).unwrap(), + )) + }); + + let mut vm = create_aqua_vm(call_service, "A"); + + let script = String::from( + r#" + (seq ( + (seq ( + (seq ( + (call ("set_variables" ("" "") ("module_bytes") module_bytes)) + (call ("set_variables" ("" "") ("module_config") module_config)) + )) + (call ("set_variables" ("" "") ("blueprint") blueprint)) + )) + (seq ( + (call ("A" ("add_module" "") (module_bytes module_config) module)) + (seq ( + (call ("A" ("add_blueprint" "") (blueprint) blueprint_id)) + (seq ( + (call ("A" ("create" "") (blueprint_id) service_id)) + (call ("remote_peer_id" ("" "") (service_id) client_result)) + )) + )) + )) + ))"#, + ); + + let res = call_vm!(set_variables_vm, "init_user_id", script.clone(), "[]", "[]"); + let res = call_vm!(vm, "init_user_id", script, "[]", res.data); + + let add_module_response = String::from("add_module response"); + let add_blueprint_response = String::from("add_blueprint response"); + let create_response = String::from("create response"); + let resulted_path: Vec = serde_json::from_str(&res.data).expect("should be a correct json"); + let right_path = vec![ + Call(Executed(Rc::new(module_bytes))), + Call(Executed(Rc::new(module_config))), + Call(Executed(Rc::new(blueprint))), + Call(Executed(Rc::new(JValue::String(add_module_response)))), + Call(Executed(Rc::new(JValue::String(add_blueprint_response)))), + Call(Executed(Rc::new(JValue::String(create_response)))), + Call(RequestSent(String::from("A"))), + ]; + + assert_eq!(resulted_path, right_path); + assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); +} diff --git a/stepper-lib/tests/call_evidence_basic.rs b/stepper-lib/tests/call_evidence_basic.rs new file mode 100644 index 00000000..1c30fa11 --- /dev/null +++ b/stepper-lib/tests/call_evidence_basic.rs @@ -0,0 +1,442 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use aqua_test_utils::call_vm; +use aqua_test_utils::create_aqua_vm; +use aqua_test_utils::echo_number_call_service; +use aqua_test_utils::unit_call_service; +use aquamarine_vm::vec1::Vec1; +use aquamarine_vm::HostExportedFunc; +use aquamarine_vm::IValue; + +use serde_json::json; + +use std::rc::Rc; + +type JValue = serde_json::Value; + +#[test] +fn evidence_seq_par_call() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let mut vm = create_aqua_vm(unit_call_service(), ""); + + let script = String::from( + r#" + (seq ( + (par ( + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_1)) + (call ("remote_peer_id" ("service_id" "fn_name") () g)) + )) + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) + ))"#, + ); + + let initial_state = json!([ + { "par": [1,1] }, + { "call": {"executed": "test"} }, + { "call": {"executed": "test"} }, + ]) + .to_string(); + + let res = call_vm!(vm, "asd", script, "[]", initial_state); + let resulted_path: Vec = serde_json::from_str(&res.data).expect("stepper should return valid json"); + + let test_string = String::from("test"); + let right_path = vec![ + Par(1, 1), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(Executed(Rc::new(JValue::String(test_string)))), + ]; + + assert_eq!(resulted_path, right_path); + assert!(res.next_peer_pks.is_empty()); +} + +#[test] +fn evidence_par_par_call() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let mut vm = create_aqua_vm(unit_call_service(), "some_peer_id"); + + let script = String::from( + r#" + (par ( + (par ( + (call ("some_peer_id" ("local_service_id" "local_fn_name") () result_1)) + (call ("remote_peer_id" ("service_id" "fn_name") () g)) + )) + (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) + ))"#, + ); + + let initial_state = json!([ + { "par": [3,0] }, + { "par": [1,0] }, + { "call": {"request_sent": "peer_id_1"} }, + { "call": {"executed": "test"} }, + ]) + .to_string(); + + let res = call_vm!(vm, "asd", script, "[]", initial_state); + let resulted_path: Vec = serde_json::from_str(&res.data).expect("stepper should return valid json"); + + let test_string = String::from("test"); + let right_path = vec![ + Par(3, 1), + Par(1, 1), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(RequestSent(String::from("some_peer_id"))), + Call(Executed(Rc::new(JValue::String(test_string)))), + ]; + + assert_eq!(resulted_path, right_path); + assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); +} + +#[test] +fn evidence_seq_seq() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let peer_id_1 = String::from("12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er"); + let peer_id_2 = String::from("12D3KooWAzJcYitiZrerycVB4Wryrx22CFKdDGx7c4u31PFdfTbR"); + let mut vm1 = create_aqua_vm(unit_call_service(), peer_id_1.clone()); + let mut vm2 = create_aqua_vm(unit_call_service(), peer_id_2.clone()); + + let script = format!( + r#" + (seq ( + (call ("{}" ("identity" "") () void0)) + (seq ( + (call ("{}" ("add_blueprint" "") () blueprint_id)) + (call ("{}" ("addBlueprint-14d8488e-d10d-474d-96b2-878f6a7d74c8" "") () void1)) + )) + )) + "#, + peer_id_1, peer_id_1, peer_id_2 + ); + + let res = call_vm!(vm2, "asd", script.clone(), "[]", "[]"); + assert_eq!(res.next_peer_pks, vec![peer_id_1.clone()]); + + let res = call_vm!(vm1, "asd", script.clone(), "[]", res.data); + assert_eq!(res.next_peer_pks, vec![peer_id_2.clone()]); + + let res = call_vm!(vm2, "asd", script, "[]", res.data); + + let resulted_path: Vec = serde_json::from_str(&res.data).expect("stepper should return valid json"); + + let test_string = String::from("test"); + let right_path = vec![ + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(Executed(Rc::new(JValue::String(test_string.clone())))), + Call(Executed(Rc::new(JValue::String(test_string)))), + ]; + + assert_eq!(resulted_path, right_path); +} + +#[test] +fn evidence_create_service() { + use stepper_lib::CallResult::*; + use stepper_lib::EvidenceState::{self, *}; + + let module = "greeting"; + let module_config = json!( + { + "name": module, + "mem_pages_count": 100, + "logger_enabled": true, + "wasi": { + "envs": json!({}), + "preopened_files": vec!["/tmp"], + "mapped_dirs": json!({}), + } + } + ); + + let module_bytes = json!([1, 2]); + let blueprint = json!({ "name": "blueprint", "dependencies": [module]}); + + let add_module_response = String::from("add_module response"); + let add_blueprint_response = String::from("add_blueprint response"); + let create_response = String::from("create response"); + + let call_service: HostExportedFunc = Box::new(move |_, args| -> Option { + let builtin_service = match &args[0] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let response = match builtin_service.as_str() { + "add_module" => add_module_response.clone(), + "add_blueprint" => add_blueprint_response.clone(), + "create" => create_response.clone(), + _ => String::from("unknown response"), + }; + + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(format!("\"{}\"", response))]).unwrap(), + )) + }); + + let mut vm = create_aqua_vm(call_service, "A"); + + let script = String::from( + r#" + (seq ( + (seq ( + (seq ( + (call ("set_variables" ("add_module" "") ("module_bytes") module_bytes)) + (call ("set_variables" ("add_module" "") ("module_config") module_config)) + )) + (call ("set_variables" ("add_module" "") ("blueprint") blueprint)) + )) + (seq ( + (call ("A" ("add_module" "") (module_bytes module_config) module)) + (seq ( + (call ("A" ("add_blueprint" "") (blueprint) blueprint_id)) + (seq ( + (call ("A" ("create" "") (blueprint_id) service_id)) + (call ("remote_peer_id" ("" "") (service_id) client_result)) + )) + )) + )) + ))"#, + ); + + let add_module_response = String::from("add_module response"); + let add_blueprint_response = String::from("add_blueprint response"); + let create_response = String::from("create response"); + let path = vec![ + Call(Executed(Rc::new(module_bytes))), + Call(Executed(Rc::new(module_config))), + Call(Executed(Rc::new(blueprint))), + Call(Executed(Rc::new(JValue::String(add_module_response)))), + Call(Executed(Rc::new(JValue::String(add_blueprint_response)))), + Call(Executed(Rc::new(JValue::String(create_response)))), + Call(Executed(Rc::new(JValue::String(String::from("test"))))), + ]; + + let res = call_vm!(vm, "init_user_id", script, "[]", json!(path).to_string()); + + let resulted_path: Vec = serde_json::from_str(&res.data).expect("should be a correct json"); + + assert_eq!(resulted_path, path); + assert!(res.next_peer_pks.is_empty()); +} + +#[test] +fn evidence_par_seq_fold_call() { + let return_numbers_call_service: HostExportedFunc = Box::new(|_, _| -> Option { + Some(IValue::Record( + Vec1::new(vec![ + IValue::S32(0), + IValue::String(String::from( + "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", + )), + ]) + .unwrap(), + )) + }); + + let mut vm1 = create_aqua_vm(return_numbers_call_service, "some_peer_id_1"); + let mut vm2 = create_aqua_vm(echo_number_call_service(), "some_peer_id_2"); + let mut vm3 = create_aqua_vm(unit_call_service(), "some_peer_id_3"); + + let script = String::from( + r#" + (par ( + (seq ( + (call ("some_peer_id_1" ("local_service_id" "local_fn_name") () IterableResultPeer1)) + (fold (IterableResultPeer1 i + (par ( + (call ("some_peer_id_2" ("local_service_id" "local_fn_name") (i) acc[])) + (next i) + )) + )) + )) + (call ("some_peer_id_3" ("local_service_id" "local_fn_name") () result_2)) + ))"#, + ); + + let res = call_vm!(vm2, "asd", script.clone(), "[]", "[]"); + let res = call_vm!(vm1, "asd", script.clone(), "[]", res.data); + let mut data = res.data; + + for _ in 0..100 { + let res = call_vm!(vm2, "asd", script.clone(), "[]", data); + data = res.data; + } + + let res = call_vm!(vm3, "asd", script, "[]", data); + let resulted_path: JValue = serde_json::from_str(&res.data).expect("a valid json"); + + let right_json = json!( [ + { "par": [21,1] }, + { "call": { "executed": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] } }, + { "par": [1,18] }, + { "call": { "executed": 1 } }, + { "par": [1,16] }, + { "call": { "executed": 2 } }, + { "par": [1,14] }, + { "call": { "executed": 3 } }, + { "par": [1,12] }, + { "call": { "executed": 4 } }, + { "par": [1,10] }, + { "call": { "executed": 5 } }, + { "par": [1,8] }, + { "call": { "executed": 6 } }, + { "par": [1,6] }, + { "call": { "executed": 7 } }, + { "par": [1,4] }, + { "call": { "executed": 8 } }, + { "par": [1,2] }, + { "call": { "executed": 9 } }, + { "par": [1,0] }, + { "call": { "executed": 10 } }, + { "call": { "executed": "test" } }, + ]); + + assert_eq!(resulted_path, right_json); + assert!(res.next_peer_pks.is_empty()); +} + +#[test] +fn evidence_par_seq_fold_in_cycle_call() { + let return_numbers_call_service: HostExportedFunc = Box::new(|_, _| -> Option { + Some(IValue::Record( + Vec1::new(vec![ + IValue::S32(0), + IValue::String(String::from( + "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", + )), + ]) + .unwrap(), + )) + }); + + let mut vm1 = create_aqua_vm(return_numbers_call_service, "some_peer_id_1"); + let mut vm2 = create_aqua_vm(echo_number_call_service(), "some_peer_id_2"); + let mut vm3 = create_aqua_vm(unit_call_service(), "some_peer_id_3"); + + let script = String::from( + r#" + (par ( + (seq ( + (call ("some_peer_id_1" ("local_service_id" "local_fn_name") () IterableResultPeer1)) + (fold (IterableResultPeer1 i + (par ( + (call ("some_peer_id_2" ("local_service_id" "local_fn_name") (i) acc[])) + (next i) + )) + )) + )) + (call ("some_peer_id_3" ("local_service_id" "local_fn_name") () result_2)) + ))"#, + ); + + let mut data = String::from("[]"); + + for _ in 0..100 { + let res = call_vm!(vm1, "asd", script.clone(), "[]", data); + let res = call_vm!(vm2, "asd", script.clone(), "[]", res.data); + let res = call_vm!(vm3, "asd", script.clone(), "[]", res.data); + data = res.data; + } + + let resulted_json: JValue = serde_json::from_str(&data).expect("stepper should return valid json"); + + let right_json = json!( [ + { "par": [21,1] }, + { "call": { "executed": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] } }, + { "par": [1,18] }, + { "call": { "executed": 1 } }, + { "par": [1,16] }, + { "call": { "executed": 2 } }, + { "par": [1,14] }, + { "call": { "executed": 3 } }, + { "par": [1,12] }, + { "call": { "executed": 4 } }, + { "par": [1,10] }, + { "call": { "executed": 5 } }, + { "par": [1,8] }, + { "call": { "executed": 6 } }, + { "par": [1,6] }, + { "call": { "executed": 7 } }, + { "par": [1,4] }, + { "call": { "executed": 8 } }, + { "par": [1,2] }, + { "call": { "executed": 9 } }, + { "par": [1,0] }, + { "call": { "executed": 10 } }, + { "call": { "executed": "test" } }, + ]); + + assert_eq!(resulted_json, right_json); +} + +#[test] +fn evidence_seq_par_seq_seq() { + let peer_id_1 = String::from("12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er"); + let peer_id_2 = String::from("12D3KooWAzJcYitiZrerycVB4Wryrx22CFKdDGx7c4u31PFdfTbR"); + let mut vm1 = create_aqua_vm(unit_call_service(), peer_id_1.clone()); + let mut vm2 = create_aqua_vm(unit_call_service(), peer_id_2.clone()); + let script = format!( + r#" + (seq ( + (par ( + (seq ( + (call ("{}" ("" "") () result_1)) + (call ("{}" ("" "") () result_2)) + )) + (seq ( + (call ("{}" ("" "") () result_3)) + (call ("{}" ("" "") () result_4)) + )) + )) + (call ("{}" ("" "") () result_5)) + )) + "#, + peer_id_1, peer_id_2, peer_id_2, peer_id_1, peer_id_2 + ); + + let res = call_vm!(vm2, "asd", script.clone(), "[]", "[]"); + assert_eq!(res.next_peer_pks, vec![peer_id_1.clone()]); + + let res = call_vm!(vm1, "asd", script.clone(), "[]", res.data); + assert_eq!(res.next_peer_pks, vec![peer_id_2.clone()]); + + let res = call_vm!(vm2, "asd", script, "[]", res.data); + + let resulted_json: JValue = serde_json::from_str(&res.data).expect("stepper should return valid json"); + + let right_json = json!( [ + { "par": [2,2] }, + { "call": {"executed" : "test" } }, + { "call": {"executed" : "test" } }, + { "call": {"executed" : "test" } }, + { "call": {"executed" : "test" } }, + { "call": {"executed" : "test" } }, + ]); + + assert_eq!(resulted_json, right_json); + assert!(res.next_peer_pks.is_empty()); +} diff --git a/stepper-lib/tests/data_merge.rs b/stepper-lib/tests/data_merge.rs new file mode 100644 index 00000000..ac9590b5 --- /dev/null +++ b/stepper-lib/tests/data_merge.rs @@ -0,0 +1,211 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use aqua_test_utils::call_vm; +use aqua_test_utils::create_aqua_vm; +use aqua_test_utils::set_variable_call_service; +use aquamarine_vm::vec1::Vec1; +use aquamarine_vm::HostExportedFunc; +use aquamarine_vm::IValue; + +use pretty_assertions::assert_eq; +use serde_json::json; + +type JValue = serde_json::Value; + +#[test] +fn data_merge() { + let neighborhood_call_service1: HostExportedFunc = Box::new(|_, _| -> Option { + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(String::from("[\"A\", \"B\"]"))]).unwrap(), + )) + }); + + let neighborhood_call_service2: HostExportedFunc = Box::new(|_, _| -> Option { + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(String::from("[\"A\", \"B\"]"))]).unwrap(), + )) + }); + + let mut vm1 = create_aqua_vm(neighborhood_call_service1, "A"); + let mut vm2 = create_aqua_vm(neighborhood_call_service2, "B"); + + let script = String::from( + r#" + (seq ( + (call (%current_peer_id% ("neighborhood" "") () neighborhood)) + (seq ( + (seq ( + (fold (neighborhood i + (par ( + (call (i ("add_provider" "") () void[])) + (next i) + )) + )) + (fold (neighborhood i + (par ( + (call (i ("get_providers" "") () providers[])) + (next i) + )) + )) + )) + (seq ( + (call ("A" ("identity" "") () void[])) + (call ("B" ("" "") () none)) + )) + )) + )) + "#, + ); + + let res1 = call_vm!(vm1, "asd", script.clone(), "[]", "[]"); + let res2 = call_vm!(vm2, "asd", script.clone(), "[]", "[]"); + let res3 = call_vm!(vm1, "asd", script.clone(), res1.data.clone(), res2.data.clone()); + let res4 = call_vm!(vm2, "asd", script, res1.data.clone(), res2.data.clone()); + + let resulted_json1: JValue = serde_json::from_str(&res1.data).expect("stepper should return valid json"); + + let right_json1 = json!( [ + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,0] }, + { "call": { "request_sent": "A" } }, + { "par": [1,2] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,0] }, + { "call": { "request_sent": "A" } }, + { "call": { "executed": ["A", "B"] } }, + { "call": { "request_sent": "A" } }, + ]); + + assert_eq!(resulted_json1, right_json1); + assert_eq!(res1.next_peer_pks, vec![String::from("B")]); + + let resulted_json2: JValue = serde_json::from_str(&res2.data).expect("stepper should return valid json"); + + let right_json2 = json!( [ + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "request_sent": "B" } }, + { "par": [1,0] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "request_sent": "B" } }, + { "par": [1,0] }, + { "call": { "executed": ["A", "B"] } }, + { "call": { "request_sent": "B" } }, + ]); + + assert_eq!(resulted_json2, right_json2); + assert_eq!(res2.next_peer_pks, vec![String::from("A")]); + + let resulted_json3: JValue = serde_json::from_str(&res3.data).expect("stepper should return valid json"); + + let right_json3 = json!( [ + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,0] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,0] }, + { "call": { "executed": ["A", "B"] } }, + { "call": { "executed": ["A", "B"] } }, + { "call": { "request_sent": "A" } }, + ]); + + assert_eq!(resulted_json3, right_json3); + assert!(res3.next_peer_pks.is_empty()); + + let resulted_json4: JValue = serde_json::from_str(&res4.data).expect("stepper should return valid json"); + + let right_json4 = json!( [ + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,0] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,2] }, + { "call": { "executed": ["A", "B"] } }, + { "par": [1,0] }, + { "call": { "executed": ["A", "B"] } }, + { "call": { "executed": ["A", "B"] } }, + { "call": { "executed": ["A", "B"] } }, + ]); + + assert_eq!(resulted_json4, right_json4); + assert!(res4.next_peer_pks.is_empty()); +} + +#[test] +fn acc_merge() { + env_logger::init(); + + let neighborhood_call_service: HostExportedFunc = Box::new(|_, args| -> Option { + let args_count = match &args[1] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let args_count = (args_count.as_bytes()[0] - b'0') as usize; + + let args_json = match &args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let args: Vec = serde_json::from_str(args_json).expect("valid json"); + let args = match &args[0] { + JValue::Array(args) => args, + _ => unreachable!(), + }; + + assert_eq!(args.len(), args_count); + + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(json!(args).to_string())]).unwrap(), + )) + }); + + let mut vm1 = create_aqua_vm(set_variable_call_service(r#""peer_id""#), "A"); + let mut vm2 = create_aqua_vm(neighborhood_call_service, "B"); + + let script = String::from( + r#" + (seq ( + (call ("A" ("add_provider" "") () void[])) + (seq ( + (call ("A" ("add_provider" "") () void[])) + (seq ( + (call ("A" ("get_providers" "") () providers[])) + (seq ( + (call ("A" ("get_providers" "") () providers[])) + (seq ( + (call ("B" ("" "2") (providers) void[])) + (call ("B" ("" "3") (void) void[])) + )) + )) + )) + )) + )) + "#, + ); + + let res = call_vm!(vm1, "asd", script.clone(), "[]", "[]"); + call_vm!(vm2, "asd", script, "[]", res.data); +} diff --git a/stepper/tests/join.rs b/stepper-lib/tests/join.rs similarity index 53% rename from stepper/tests/join.rs rename to stepper-lib/tests/join.rs index 871d1570..20c5438c 100644 --- a/stepper/tests/join.rs +++ b/stepper-lib/tests/join.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +use aqua_test_utils::call_vm; use aqua_test_utils::create_aqua_vm; use aqua_test_utils::unit_call_service; use aquamarine_vm::vec1::Vec1; @@ -68,58 +69,42 @@ fn join_chat() { "#, ); - let client_1_res = client_1 - .call(json!(["asd", script, "{}", "{}"])) - .expect("should be successful"); + let client_1_res = call_vm!(client_1, "asd", script.clone(), "[]", "[]"); let client_1_res_json: JValue = serde_json::from_str(&client_1_res.data).expect("stepper should return valid json"); - let client_1_right_json = json!( { - "__call": [ - { "call": "request_sent" }, - ] - }); + let client_1_right_json = json!([ + { "call": {"request_sent": "A" } }, + ]); assert_eq!(client_1_res_json, client_1_right_json); assert_eq!(client_1_res.next_peer_pks, vec![String::from("Relay1")]); - let relay_1_res = relay_1 - .call(json!(["asd", script, client_1_res.data, "{}"])) - .expect("should be successful"); + let relay_1_res = call_vm!(relay_1, "asd", script.clone(), client_1_res.data, "[]"); let relay_1_res_json: JValue = serde_json::from_str(&relay_1_res.data).expect("stepper should return valid json"); - let relay_1_right_json = json!( { - "void1": ["test"], - "__call": [ - { "call": "executed" }, - { "call": "request_sent" }, - ] - }); + let relay_1_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "request_sent": "Relay1" } }, + ]); assert_eq!(relay_1_res_json, relay_1_right_json); assert_eq!(relay_1_res.next_peer_pks, vec![String::from("Remote")]); - let remote_res = remote - .call(json!(["asd", script, relay_1_res.data, "{}"])) - .expect("should be successful"); + let remote_res = call_vm!(remote, "asd", script.clone(), relay_1_res.data, "[]"); let remote_res_json: JValue = serde_json::from_str(&remote_res.data).expect("stepper should return valid json"); - let remote_right_json = json!( { - "void1": ["test"], - "void2": [[["A", "Relay1"], ["B", "Relay2"]]], - "members": [["A", "Relay1"], ["B", "Relay2"]], - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [1, 2] }, - { "call": "request_sent" }, - { "par": [1, 0] }, - { "call": "request_sent" }, - ] - }); + let remote_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "par": [1, 2] }, + { "call": { "request_sent" : "Remote" } }, + { "par": [1, 0] }, + { "call": { "request_sent" : "Remote" } }, + ]); let remote_res_next_peer_pks: HashSet<_> = remote_res.next_peer_pks.iter().map(|s| s.as_str()).collect(); let next_peer_pks_right = maplit::hashset! { @@ -130,108 +115,74 @@ fn join_chat() { assert_eq!(remote_res_json, remote_right_json); assert_eq!(remote_res_next_peer_pks, next_peer_pks_right); - let relay_1_res = relay_1 - .call(json!(["asd", script, remote_res.data, "{}"])) - .expect("should be successful"); + let relay_1_res = call_vm!(relay_1, "asd", script.clone(), remote_res.data.clone(), "[]"); let relay_1_res_json: JValue = serde_json::from_str(&relay_1_res.data).expect("stepper should return valid json"); - let relay_1_right_json = json!( { - "void1": ["test"], - "void2": [[["A", "Relay1"], ["B", "Relay2"]]], - "void": ["test"], - "members": [["A", "Relay1"], ["B", "Relay2"]], - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [2, 2] }, - { "call": "executed" }, - { "call": "request_sent" }, - { "par": [1, 0] }, - { "call": "request_sent" }, - ] - }); + let relay_1_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "par": [2, 2] }, + { "call": { "executed" : "test" } }, + { "call": { "request_sent" : "Relay1" } }, + { "par": [1, 0] }, + { "call": { "request_sent" : "Remote" } }, + ]); assert_eq!(relay_1_res_json, relay_1_right_json); assert_eq!(relay_1_res.next_peer_pks, vec![String::from("A")]); - let client_1_res = client_1 - .call(json!(["asd", script, relay_1_res.data, "{}"])) - .expect("should be successful"); + let client_1_res = call_vm!(client_1, "asd", script.clone(), relay_1_res.data, "[]"); let client_1_res_json: JValue = serde_json::from_str(&client_1_res.data).expect("stepper should return valid json"); - let client_1_right_json = json!( { - "void1": ["test"], - "void2": [[["A", "Relay1"], ["B", "Relay2"]]], - "void": ["test"], - "void3": ["test"], - "members": [["A", "Relay1"], ["B", "Relay2"]], - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [2, 2] }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [1, 0] }, - { "call": "request_sent" }, - ] - }); + let client_1_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "par": [2, 2] }, + { "call": { "executed" : "test" } }, + { "call": { "executed" : "test" } }, + { "par": [1, 0] }, + { "call": { "request_sent" : "Remote" } }, + ]); assert_eq!(client_1_res_json, client_1_right_json); assert_eq!(client_1_res.next_peer_pks, Vec::::new()); - let relay_2_res = relay_2 - .call(json!(["asd", script, remote_res.data, "{}"])) - .expect("should be successful"); + let relay_2_res = call_vm!(relay_2, "asd", script.clone(), remote_res.data, "[]"); let relay_2_res_json: JValue = serde_json::from_str(&relay_2_res.data).expect("stepper should return valid json"); - let relay_2_right_json = json!( { - "void1": ["test"], - "void2": [[["A", "Relay1"], ["B", "Relay2"]]], - "void": ["test"], - "members": [["A", "Relay1"], ["B", "Relay2"]], - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [1, 3] }, - { "call": "request_sent" }, - { "par": [2, 0] }, - { "call": "executed" }, - { "call": "request_sent" }, - ] - }); + let relay_2_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "par": [1, 3] }, + { "call": { "request_sent" : "Remote" } }, + { "par": [2, 0] }, + { "call": { "executed" : "test" } }, + { "call": { "request_sent" : "Relay2" } }, + ]); assert_eq!(relay_2_res_json, relay_2_right_json); assert_eq!(relay_2_res.next_peer_pks, vec![String::from("B")]); - let client_2_res = client_2 - .call(json!(["asd", script, relay_2_res.data, "{}"])) - .expect("should be successful"); + let client_2_res = call_vm!(client_2, "asd", script, relay_2_res.data, "[]"); let client_2_res_json: JValue = serde_json::from_str(&client_2_res.data).expect("stepper should return valid json"); - let client_2_right_json = json!( { - "void1": ["test"], - "void2": [[["A", "Relay1"], ["B", "Relay2"]]], - "void": ["test"], - "void3": ["test"], - "members": [["A", "Relay1"], ["B", "Relay2"]], - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [1, 3] }, - { "call": "request_sent" }, - { "par": [2, 0] }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); + let client_2_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} }, + { "par": [1, 3] }, + { "call": { "request_sent" : "Remote" } }, + { "par": [2, 0] }, + { "call": { "executed" : "test" } }, + { "call": { "executed" : "test" } }, + ]); assert_eq!(client_2_res_json, client_2_right_json); assert_eq!(client_2_res.next_peer_pks, Vec::::new()); @@ -271,44 +222,24 @@ fn join() { "#, ); - let client_1_res = client_1 - .call(json!(["asd", script, "{}", "{}"])) - .expect("should be successful"); - - let relay_1_res = relay_1 - .call(json!(["asd", script, client_1_res.data, "{}"])) - .expect("should be successful"); - - let remote_res = remote - .call(json!(["asd", script, relay_1_res.data, "{}"])) - .expect("should be successful"); - - let relay_1_res = relay_1 - .call(json!(["asd", script, remote_res.data, "{}"])) - .expect("should be successful"); - - let client_1_res = client_1 - .call(json!(["asd", script, relay_1_res.data, "{}"])) - .expect("should be successful"); + let client_1_res = call_vm!(client_1, "asd", script.clone(), "[]", "[]"); + let relay_1_res = call_vm!(relay_1, "asd", script.clone(), client_1_res.data, "[]"); + let remote_res = call_vm!(remote, "asd", script.clone(), relay_1_res.data, "[]"); + let relay_1_res = call_vm!(relay_1, "asd", script.clone(), remote_res.data, "[]"); + let client_1_res = call_vm!(client_1, "asd", script, relay_1_res.data, "[]"); let client_1_res_json: JValue = serde_json::from_str(&client_1_res.data).expect("stepper should return valid json"); - let client_1_right_json = json!( { - "void1": ["test"], - "void": ["test", "test"], - "void3": ["test", "test"], - "members": [["A"], ["B"]], - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "par": [2, 3] }, - { "call": "executed" }, - { "call": "executed" }, - { "par": [2, 0] }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); + let client_1_right_json = json!( [ + { "call": { "executed" : "test" } }, + { "call": { "executed" : [["A"], ["B"]]} }, + { "par": [2, 3] }, + { "call": { "executed" : "test" } }, + { "call": { "executed" : "test" } }, + { "par": [2, 0] }, + { "call": { "executed" : "test" } }, + { "call": { "executed" : "test" } }, + ]); assert_eq!(client_1_res_json, client_1_right_json); assert_eq!(client_1_res.next_peer_pks, Vec::::new()); diff --git a/stepper/Cargo.toml b/stepper/Cargo.toml index 4587adb0..1c199b1b 100644 --- a/stepper/Cargo.toml +++ b/stepper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aquamarine" -version = "0.1.1" +version = "0.1.2" authors = ["Fluence Labs"] edition = "2018" @@ -14,25 +14,13 @@ name = "aquamarine" path = "src/fce.rs" [dependencies] +stepper-lib = { path = "../stepper-lib" } + fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } - -serde = { version = "1.0.116", features = [ "derive", "rc" ] } -serde_derive = "1.0.116" -serde_sexpr = "0.1.0" - -jsonpath_lib = "0.2.5" - -boolinator = "2.4.0" -log = "0.4.11" -once_cell = "1.4.1" -serde_json = "1.0" wasm-bindgen = "0.2.68" -[dev_dependencies] -aqua-test-utils = { path = "../crates/test-utils" } -aquamarine-vm = { git = "https://github.com/fluencelabs/fce" } +log = "0.4.11" +serde_json = "1.0" -env_logger = "0.7.1" -maplit = "1.0.2" -pretty_assertions = "0.6.1" -serde_json = "1.0.56" +[features] +fce = ["stepper-lib/fce"] diff --git a/stepper/src/air/call/parsed_call.rs b/stepper/src/air/call/parsed_call.rs deleted file mode 100644 index 62c73fde..00000000 --- a/stepper/src/air/call/parsed_call.rs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::utils::is_string_literal; -use super::utils::prepare_evidence_state; -use super::Call; -use super::CURRENT_PEER_ALIAS; -use crate::air::ExecutionCtx; -use crate::air::RESERVED_KEYWORDS; -use crate::call_evidence::CallEvidenceCtx; -use crate::AquamarineError; -use crate::JValue; -use crate::Result; - -#[derive(Debug, PartialEq, Eq)] -pub(super) struct ParsedCall { - peer_pk: String, - service_id: String, - function_name: String, - function_arg_paths: Vec, - result_variable_name: String, -} - -impl ParsedCall { - pub(super) fn new(raw_call: &Call, exec_ctx: &ExecutionCtx) -> Result { - let (peer_pk, service_id, func_name) = Self::prepare_peer_fn_parts(raw_call, exec_ctx)?; - let result_variable_name = Self::parse_result_variable_name(raw_call)?; - - Ok(Self { - peer_pk: peer_pk.to_string(), - service_id: service_id.to_string(), - function_name: func_name.to_string(), - function_arg_paths: raw_call.2.clone(), - result_variable_name: result_variable_name.to_string(), - }) - } - - pub(super) fn execute(self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - let is_current_peer = self.peer_pk == exec_ctx.current_peer_id; - let should_executed = prepare_evidence_state(is_current_peer, exec_ctx, call_ctx)?; - if !should_executed { - return Ok(()); - } - - if self.peer_pk != exec_ctx.current_peer_id && self.peer_pk != CURRENT_PEER_ALIAS { - super::utils::set_remote_call_result(self.peer_pk, exec_ctx, call_ctx); - - return Ok(()); - } - - let function_args = self.extract_args_by_paths(exec_ctx)?; - let function_args = serde_json::to_string(&function_args) - .map_err(|e| AquamarineError::FuncArgsSerializationError(function_args, e))?; - - let result = unsafe { crate::call_service(self.service_id, self.function_name, function_args) }; - - if result.ret_code != crate::CALL_SERVICE_SUCCESS { - return Err(AquamarineError::LocalServiceError(result.result)); - } - - let result: JValue = serde_json::from_str(&result.result) - .map_err(|e| AquamarineError::CallServiceResultDeserializationError(result, e))?; - super::utils::set_local_call_result(self.result_variable_name, exec_ctx, call_ctx, result) - } - - fn prepare_peer_fn_parts<'a>( - raw_call: &'a Call, - exec_ctx: &'a ExecutionCtx, - ) -> Result<(&'a str, &'a str, &'a str)> { - use super::FunctionPart::*; - use super::PeerPart::*; - - let (peer_pk, service_id, func_name) = match (&raw_call.0, &raw_call.1) { - (PeerPkWithServiceId(peer_pk, peer_service_id), ServiceIdWithFuncName(_service_id, func_name)) => { - Ok((peer_pk, peer_service_id, func_name)) - } - (PeerPkWithServiceId(peer_pk, peer_service_id), FuncName(func_name)) => { - Ok((peer_pk, peer_service_id, func_name)) - } - (PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => Ok((peer_pk, service_id, func_name)), - (PeerPk(_), FuncName(_)) => Err(AquamarineError::InstructionError(String::from( - "call should have service id specified by peer part or function part", - ))), - }?; - - let peer_pk = if peer_pk != CURRENT_PEER_ALIAS { - Self::prepare_call_arg(peer_pk, exec_ctx)? - } else { - peer_pk - }; - - let service_id = Self::prepare_call_arg(service_id, exec_ctx)?; - let func_name = Self::prepare_call_arg(func_name, exec_ctx)?; - - Ok((peer_pk, service_id, func_name)) - } - - fn extract_args_by_paths(&self, ctx: &ExecutionCtx) -> Result { - let mut result = Vec::with_capacity(self.function_arg_paths.len()); - - for arg_path in self.function_arg_paths.iter() { - if is_string_literal(arg_path) { - result.push(JValue::String(arg_path[1..arg_path.len() - 1].to_string())); - } else { - let arg = Self::get_args_by_path(arg_path, ctx)?; - result.extend(arg.into_iter().cloned()); - } - } - - Ok(JValue::Array(result)) - } - - fn parse_result_variable_name(call: &Call) -> Result<&str> { - let result_variable_name = &call.3; - - if result_variable_name.is_empty() { - return Err(AquamarineError::InstructionError(String::from( - "result name of a call instruction must be non empty", - ))); - } - - if RESERVED_KEYWORDS.contains(result_variable_name.as_str()) { - return Err(AquamarineError::ReservedKeywordError(result_variable_name.to_string())); - } - - if is_string_literal(result_variable_name) { - return Err(AquamarineError::InstructionError(String::from( - "result name of a call instruction must be non string literal", - ))); - } - - Ok(result_variable_name) - } - - fn get_args_by_path<'args_path, 'ctx>( - args_path: &'args_path str, - ctx: &'ctx ExecutionCtx, - ) -> Result> { - let mut split_arg: Vec<&str> = args_path.splitn(2, '.').collect(); - let arg_path_head = split_arg.remove(0); - - let value_by_head = match (ctx.data.get(arg_path_head), ctx.folds.get(arg_path_head)) { - (_, Some(fold_state)) => match ctx.data.get(&fold_state.iterable_name) { - Some(JValue::Array(values)) => &values[fold_state.cursor], - Some(v) => { - return Err(AquamarineError::IncompatibleJValueType( - v.clone(), - String::from("array"), - )) - } - None => return Err(AquamarineError::VariableNotFound(fold_state.iterable_name.clone())), - }, - (Some(value), None) => value, - (None, None) => return Err(AquamarineError::VariableNotFound(arg_path_head.to_string())), - }; - - if split_arg.is_empty() { - return Ok(vec![value_by_head]); - } - - let json_path = split_arg.remove(0); - let values = jsonpath_lib::select(value_by_head, json_path) - .map_err(|e| AquamarineError::VariableNotInJsonPath(value_by_head.clone(), String::from(json_path), e))?; - - Ok(values) - } - - fn prepare_call_arg<'a>(arg_path: &'a str, ctx: &'a ExecutionCtx) -> Result<&'a str> { - if RESERVED_KEYWORDS.contains(arg_path) { - return Err(AquamarineError::ReservedKeywordError(arg_path.to_string())); - } - - if is_string_literal(arg_path) { - return Ok(&arg_path[1..arg_path.len() - 1]); - } - - let args = Self::get_args_by_path(arg_path, ctx)?; - if args.is_empty() { - return Err(AquamarineError::VariableNotFound(arg_path.to_string())); - } - - if args.len() != 1 { - return Err(AquamarineError::MultipleValuesInJsonPath(arg_path.to_string())); - } - - match args[0] { - JValue::String(str) => Ok(str), - v => Err(AquamarineError::IncompatibleJValueType( - v.clone(), - String::from("string"), - )), - } - } -} diff --git a/stepper/src/air/call/utils.rs b/stepper/src/air/call/utils.rs deleted file mode 100644 index cc8c0a7d..00000000 --- a/stepper/src/air/call/utils.rs +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::ExecutionCtx; -use crate::call_evidence::CallEvidenceCtx; -use crate::call_evidence::CallResult; -use crate::call_evidence::EvidenceState; -use crate::AquamarineError; -use crate::JValue; -use crate::Result; - -pub(super) fn prepare_evidence_state( - is_current_peer: bool, - exec_ctx: &mut ExecutionCtx, - call_ctx: &mut CallEvidenceCtx, -) -> Result { - if call_ctx.current_subtree_elements_count == 0 { - log::info!("call evidence: previous state wasn't found"); - return Ok(true); - } - - call_ctx.current_subtree_elements_count -= 1; - // unwrap is safe here, because current_subtree_elements_count depends on current_path len, - // and it's been checked previously - let prev_state = call_ctx.current_path.pop_front().unwrap(); - - log::info!("call evidence: previous state found {:?}", prev_state); - - match &prev_state { - // this call was failed on one of the previous executions, - // here it's needed to bubble this special error up - EvidenceState::Call(CallResult::CallServiceFailed(err_msg)) => { - let err_msg = err_msg.clone(); - call_ctx.new_path.push_back(prev_state); - exec_ctx.subtree_complete = false; - Err(AquamarineError::LocalServiceError(err_msg)) - } - EvidenceState::Call(CallResult::RequestSent) => { - // check whether current node can execute this call - if is_current_peer { - Ok(true) - } else { - exec_ctx.subtree_complete = false; - call_ctx.new_path.push_back(prev_state); - Ok(false) - } - } - // this instruction's been already executed - EvidenceState::Call(CallResult::Executed) => { - call_ctx.new_path.push_back(prev_state); - Ok(false) - } - // state has inconsistent order - return a error, call shouldn't be executed - par_state @ EvidenceState::Par(..) => Err(AquamarineError::InvalidEvidenceState( - par_state.clone(), - String::from("call"), - )), - } -} - -pub(super) fn set_local_call_result( - result_variable_name: String, - exec_ctx: &mut ExecutionCtx, - call_ctx: &mut CallEvidenceCtx, - result: JValue, -) -> Result<()> { - use std::collections::hash_map::Entry::{Occupied, Vacant}; - - let new_evidence_state = EvidenceState::Call(CallResult::Executed); - let is_array = result_variable_name.ends_with("[]"); - - if !is_array { - // if result is not an array, simply insert it into data - if exec_ctx.data.insert(result_variable_name.clone(), result).is_some() { - return Err(AquamarineError::MultipleVariablesFound(result_variable_name)); - } - - log::info!("call evidence: adding new state {:?}", new_evidence_state); - call_ctx.new_path.push_back(new_evidence_state); - - return Ok(()); - } - - // unwrap is safe because it's been checked for [] - let result_variable_name = result_variable_name.strip_suffix("[]").unwrap().to_string(); - // if result is an array, insert result to the end of the array - match exec_ctx.data.entry(result_variable_name) { - Occupied(mut entry) => match entry.get_mut() { - JValue::Array(values) => values.push(result), - v => { - return Err(AquamarineError::IncompatibleJValueType( - v.clone(), - String::from("Array"), - )) - } - }, - Vacant(entry) => { - entry.insert(JValue::Array(vec![result])); - } - } - - log::info!("call evidence: adding new state {:?}", new_evidence_state); - call_ctx.new_path.push_back(new_evidence_state); - - Ok(()) -} - -pub(super) fn set_remote_call_result(peer_pk: String, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) { - exec_ctx.next_peer_pks.push(peer_pk); - exec_ctx.subtree_complete = false; - - let new_evidence_state = EvidenceState::Call(CallResult::RequestSent); - log::info!("call evidence: adding new state {:?}", new_evidence_state); - call_ctx.new_path.push_back(new_evidence_state); -} - -pub(super) fn is_string_literal(value: &str) -> bool { - value.starts_with('"') && value.ends_with('"') -} diff --git a/stepper/src/air/fold.rs b/stepper/src/air/fold.rs deleted file mode 100644 index 592a044b..00000000 --- a/stepper/src/air/fold.rs +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::CallEvidenceCtx; -use super::ExecutionCtx; -use super::Instruction; -use crate::AquamarineError; -use crate::JValue; -use crate::Result; - -use serde_derive::Deserialize; -use serde_derive::Serialize; -use std::rc::Rc; - -/* - (fold Iterable i - (par - (call fn [i] acc[]) - (next i) - ) - ) -*/ - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -pub(crate) struct Fold(String, String, Rc); - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -pub(crate) struct Next(String); - -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct FoldState { - pub(crate) cursor: usize, - pub(crate) iterable_name: String, - pub(crate) instr_head: Rc, -} - -impl super::ExecutableInstruction for Fold { - fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!( - "fold {} {} is called with contexts {:?} {:?}", - self.0, - self.1, - exec_ctx, - call_ctx - ); - - let iterable_name = &self.0; - let iterator_name = &self.1; - let instr_head = self.2.clone(); - - // check that value exists and has array type - match exec_ctx.data.get(iterable_name) { - Some(JValue::Array(array)) => { - if array.is_empty() { - // skip fold if array is empty - return Ok(()); - } - } - Some(v) => { - return Err(AquamarineError::IncompatibleJValueType( - v.clone(), - String::from("Array"), - )) - } - None => return Err(AquamarineError::VariableNotFound(String::from(iterable_name))), - }; - - let fold_state = FoldState { - cursor: 0, - iterable_name: iterable_name.clone(), - instr_head: instr_head.clone(), - }; - - if exec_ctx.folds.insert(iterator_name.clone(), fold_state).is_some() { - return Err(AquamarineError::MultipleFoldStates(iterable_name.clone())); - } - - instr_head.execute(exec_ctx, call_ctx)?; - exec_ctx.folds.remove(iterator_name); - - Ok(()) - } -} - -impl super::ExecutableInstruction for Next { - fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> { - log::info!("next {:?} is called with contexts {:?} {:?}", self, exec_ctx, call_ctx); - - let iterator_name = &self.0; - let fold_state = exec_ctx - .folds - .get_mut(iterator_name) - .ok_or_else(|| AquamarineError::FoldStateNotFound(iterator_name.clone()))?; - let value = exec_ctx - .data - .get(&fold_state.iterable_name) - .expect("this has been checked on the fold instruction"); - let value_len = match value { - JValue::Array(array) => array.len(), - _ => unreachable!("iterable value shouldn't changed inside fold"), - }; - - fold_state.cursor += 1; - if value_len == 0 || value_len <= fold_state.cursor { - fold_state.cursor -= 1; - // just do nothing to exit - return Ok(()); - } - - let next_instr = fold_state.instr_head.clone(); - next_instr.execute(exec_ctx, call_ctx)?; - - // get the same fold state again because of borrow checker - match exec_ctx.folds.get_mut(iterator_name) { - Some(fold_state) => fold_state.cursor -= 1, - _ => unreachable!("iterator value shouldn't changed inside fold"), - }; - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use crate::JValue; - - use aqua_test_utils::create_aqua_vm; - use aqua_test_utils::echo_number_call_service; - use aquamarine_vm::AquamarineVMError; - use aquamarine_vm::StepperError; - - use serde_json::json; - - #[test] - fn lfold() { - let mut vm = create_aqua_vm(echo_number_call_service(), ""); - - let lfold = String::from( - r#" - (fold (Iterable i - (seq ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") (i) acc[])) - (next i) - ) - )))"#, - ); - - let res = vm - .call(json!([ - "asd", - lfold, - "{}", - "{\"Iterable\": [\"1\",\"2\",\"3\",\"4\",\"5\"]}", - ])) - .expect("call should be successful"); - - let res: JValue = serde_json::from_str(&res.data).unwrap(); - - assert_eq!(res.get("acc").unwrap(), &json!([1, 2, 3, 4, 5])); - } - - #[test] - fn rfold() { - let mut vm = create_aqua_vm(echo_number_call_service(), ""); - - let rfold = String::from( - r#" - (fold (Iterable i - (seq ( - (next i) - (call (%current_peer_id% ("local_service_id" "local_fn_name") (i) acc[])) - ) - )))"#, - ); - - let res = vm - .call(json!([ - "asd", - rfold, - "{}", - "{\"Iterable\": [\"1\",\"2\",\"3\",\"4\",\"5\"]}", - ])) - .expect("call should be successful"); - - let res: JValue = serde_json::from_str(&res.data).unwrap(); - - assert_eq!(res.get("acc").unwrap(), &json!([5, 4, 3, 2, 1])); - } - - #[test] - fn inner_fold() { - let mut vm = create_aqua_vm(echo_number_call_service(), ""); - - let script = String::from( - r#" - (fold (Iterable1 i - (seq ( - (fold (Iterable2 j - (seq ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") (i) acc[])) - (next j) - )) - )) - (next i) - )) - ))"#, - ); - - let res = vm - .call(json!([ - "asd", - script, - "{}", - "{\"Iterable1\": [\"1\",\"2\",\"3\",\"4\",\"5\"], \"Iterable2\": [\"1\",\"2\",\"3\",\"4\",\"5\"]}", - ])) - .expect("call should be successful"); - - let res: JValue = serde_json::from_str(&res.data).unwrap(); - - assert_eq!( - res.get("acc").unwrap(), - &json!([1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5]) - ); - } - - #[test] - fn inner_fold_with_same_iterator() { - let mut vm = create_aqua_vm(echo_number_call_service(), ""); - - let script = String::from( - r#" - (fold (Iterable1 i - (seq ( - (fold (Iterable2 i - (seq ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") (i) acc[])) - (next i) - )) - )) - (next i) - )) - ))"#, - ); - - let res = vm.call(json!([ - "asd", - script, - "{}", - "{\"Iterable1\": [\"1\",\"2\",\"3\",\"4\",\"5\"], \"Iterable2\": [\"1\",\"2\",\"3\",\"4\",\"5\"]}", - ])); - - assert!(res.is_err()); - let error = res.err().unwrap(); - let error = match error { - AquamarineVMError::StepperError(error) => error, - _ => unreachable!(), - }; - - assert_eq!( - error, - StepperError::MultipleFoldStates(String::from("multiple fold states found for iterable Iterable2")) - ); - } - - #[test] - fn empty_fold() { - let mut vm = create_aqua_vm(echo_number_call_service(), ""); - - let lfold = String::from( - r#" - (fold (Iterable i - (seq ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") (i) acc[])) - (next i) - ) - )))"#, - ); - - let res = vm - .call(json!(["asd", lfold, "{}", "{\"Iterable\": []}",])) - .expect("call should be successful"); - - let res: JValue = serde_json::from_str(&res.data).unwrap(); - - assert!(res.get("acc").is_none()); - } -} diff --git a/stepper/src/execution/epilog.rs b/stepper/src/execution/epilog.rs deleted file mode 100644 index 8b2f7005..00000000 --- a/stepper/src/execution/epilog.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::CALL_EVIDENCE_CTX_KEY; -use crate::call_evidence::CallEvidenceCtx; -use crate::AquaData; -use crate::AquamarineError::CallEvidenceSerializationError as CallSeError; -use crate::AquamarineError::DataSerializationError as DataSeError; -use crate::Result; - -pub(super) fn make_result_data(mut data: AquaData, call_ctx: CallEvidenceCtx) -> Result { - use serde_json::{to_string, to_value}; - - let serialized_call_ctx = to_value(call_ctx.new_path).map_err(CallSeError)?; - data.insert(CALL_EVIDENCE_CTX_KEY.to_string(), serialized_call_ctx); - - let data = to_string(&data).map_err(DataSeError)?; - - Ok(data) -} diff --git a/stepper/src/execution/prolog.rs b/stepper/src/execution/prolog.rs deleted file mode 100644 index a1faf265..00000000 --- a/stepper/src/execution/prolog.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::utils::format_aqua; -use super::CALL_EVIDENCE_CTX_KEY; -use crate::air::ExecutionCtx; -use crate::air::Instruction; -use crate::call_evidence::merge_call_paths; -use crate::call_evidence::CallEvidenceCtx; -use crate::call_evidence::EvidenceState; -use crate::get_current_peer_id; -use crate::AquaData; -use crate::AquamarineError; -use crate::Result; - -use std::collections::VecDeque; - -/// Parse and prepare supplied data and aqua script. -pub(super) fn prepare(prev_data: String, data: String, aqua: String) -> Result<(AquaData, AquaData, Instruction)> { - use AquamarineError::DataDeserializationError as DataDeError; - - let parsed_prev_data: AquaData = serde_json::from_str(&prev_data).map_err(DataDeError)?; - let parsed_data: AquaData = serde_json::from_str(&data).map_err(DataDeError)?; - - let formatted_aqua = format_aqua(aqua); - let parsed_aqua: Instruction = serde_sexpr::from_str(&formatted_aqua)?; - - log::info!( - "\nparsed aqua: {:?}\nparsed prev_data: {:?}\nparsed data: {:?}", - parsed_aqua, - parsed_prev_data, - parsed_data - ); - - Ok((parsed_prev_data, parsed_data, parsed_aqua)) -} - -/// Make execution and call evidence contexts from supplied data. -/// Internally, it unites variable from previous and current data and merges call evidence paths. -pub(super) fn make_contexts(mut prev_data: AquaData, mut data: AquaData) -> Result<(ExecutionCtx, CallEvidenceCtx)> { - use AquamarineError::CallEvidenceDeserializationError as CallDeError; - use AquamarineError::CurrentPeerIdEnvError as EnvError; - - let current_peer_id = get_current_peer_id().map_err(|e| EnvError(e, String::from("CURRENT_PEER_ID")))?; - - let prev_states: VecDeque = match prev_data.remove(CALL_EVIDENCE_CTX_KEY) { - Some(jvalue) => serde_json::from_value(jvalue).map_err(CallDeError)?, - None => VecDeque::new(), - }; - - let states: VecDeque = match data.remove(CALL_EVIDENCE_CTX_KEY) { - Some(jvalue) => serde_json::from_value(jvalue).map_err(CallDeError)?, - None => VecDeque::new(), - }; - - let data = merge_data(prev_data, data)?; - let current_path = merge_call_paths(prev_states, states)?; - - let execution_ctx = ExecutionCtx::new(data, current_peer_id); - let call_evidence_ctx = CallEvidenceCtx::new(current_path); - - Ok((execution_ctx, call_evidence_ctx)) -} - -fn merge_data(mut prev_data: AquaData, data: AquaData) -> Result { - use boolinator::Boolinator; - use std::collections::hash_map::Entry::{Occupied, Vacant}; - use AquamarineError::MultipleVariablesFound; - - for (key, value) in data { - match prev_data.entry(key) { - Vacant(entry) => { - entry.insert(value); - } - // check that data has equal values for the same key - Occupied(entry) => { - entry - .get() - .eq(&value) - .ok_or_else(|| MultipleVariablesFound(entry.key().clone()))?; - } - } - } - - Ok(prev_data) -} diff --git a/stepper/src/fce.rs b/stepper/src/fce.rs index 7bee635e..fe706a01 100644 --- a/stepper/src/fce.rs +++ b/stepper/src/fce.rs @@ -26,36 +26,26 @@ unreachable_patterns )] -mod air; -mod call_evidence; -mod defines; -mod errors; -mod execution; -mod stepper_outcome; - -pub(crate) use crate::defines::*; - -use crate::execution::execute_aqua; use fluence::fce; -use std::env::VarError; +use stepper_lib::execute_aqua; +use stepper_lib::log_targets::TARGET_MAP; +use stepper_lib::StepperOutcome; -const CURRENT_PEER_ID_ENV_NAME: &str = "CURRENT_PEER_ID"; +use std::collections::HashMap; pub fn main() { - fluence::WasmLogger::init_with_level(log::Level::Info).unwrap(); + use std::iter::FromIterator; + + let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned()); + + fluence::WasmLogger::new() + .with_log_level(log::Level::Info) + .with_target_map(target_map) + .build() + .unwrap(); } #[fce] pub fn invoke(init_user_id: String, aqua: String, prev_data: String, data: String) -> StepperOutcome { execute_aqua(init_user_id, aqua, prev_data, data) } - -pub fn get_current_peer_id() -> std::result::Result { - std::env::var(CURRENT_PEER_ID_ENV_NAME) -} - -#[fce] -#[link(wasm_import_module = "host")] -extern "C" { - pub fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult; -} diff --git a/stepper/src/wasm_bindgen.rs b/stepper/src/wasm_bindgen.rs index 423038b2..b5c9793d 100644 --- a/stepper/src/wasm_bindgen.rs +++ b/stepper/src/wasm_bindgen.rs @@ -22,27 +22,27 @@ unused_imports, unused_mut, unused_variables, - // unused_unsafe, + unused_unsafe, unreachable_patterns )] -mod air; -mod call_evidence; -mod defines; -mod errors; -mod execution; -mod stepper_outcome; - -pub(crate) use crate::defines::*; - -use crate::execution::execute_aqua; - -use wasm_bindgen::__rt::std::env::VarError; +use stepper_lib::execute_aqua; +use stepper_lib::log_targets::TARGET_MAP; use wasm_bindgen::prelude::*; +use std::collections::HashMap; + #[wasm_bindgen(start)] pub fn main() { - fluence::WasmLogger::init_with_level(log::Level::Info).unwrap(); + use std::iter::FromIterator; + + let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned()); + + fluence::WasmLogger::new() + .with_log_level(log::Level::Info) + .with_target_map(target_map) + .build() + .unwrap(); } #[wasm_bindgen] @@ -50,25 +50,3 @@ pub fn invoke(init_user_id: String, aqua: String, prev_data: String, data: Strin let outcome = execute_aqua(init_user_id, aqua, prev_data, data); serde_json::to_string(&outcome).expect("Cannot parse StepperOutcome") } - -pub fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult { - let result = call_service_impl(service_id, fn_name, args); - log::info!("result {}", result); - serde_json::from_str(&result).expect("Cannot parse CallServiceResult") -} - -pub fn get_current_peer_id() -> std::result::Result { - Ok(get_current_peer_id_impl()) -} - -#[wasm_bindgen] -extern "C" { - #[link_name = "get_current_peer_id"] - fn get_current_peer_id_impl() -> String; -} - -#[wasm_bindgen(raw_module = "../src/call_service.ts")] -extern "C" { - #[link_name = "call_service"] - fn call_service_impl(service_id: String, fn_name: String, args: String) -> String; -} diff --git a/stepper/tests/air_basic.rs b/stepper/tests/air_basic.rs deleted file mode 100644 index e055c024..00000000 --- a/stepper/tests/air_basic.rs +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use aqua_test_utils::create_aqua_vm; -use aqua_test_utils::unit_call_service; -use aquamarine_vm::vec1::Vec1; -use aquamarine_vm::HostExportedFunc; -use aquamarine_vm::IValue; - -use serde_json::json; - -type JValue = serde_json::Value; - -#[test] -fn seq_par_call() { - let mut vm = create_aqua_vm(unit_call_service(), ""); - - let script = String::from( - r#" - (seq ( - (par ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_1)) - (call ("remote_peer_id" ("service_id" "fn_name") () g)) - )) - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) - ))"#, - ); - - let res = vm - .call(json!(["asd", script, "{}", "{}",])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res.data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_1" : "test", - "result_2" : "test", - "__call": [ - { "par": [1,1] }, - { "call": "executed" }, - { "call": "request_sent" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); -} - -#[test] -fn par_par_call() { - let mut vm = create_aqua_vm(unit_call_service(), ""); - - let script = String::from( - r#" - (par ( - (par ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_1)) - (call ("remote_peer_id" ("service_id" "fn_name") () g)) - )) - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) - ))"#, - ); - - let res = vm - .call(json!(["asd", script, "{}", "{}",])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res.data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_1" : "test", - "result_2" : "test", - "__call": [ - { "par": [3,1] }, - { "par": [1,1] }, - { "call": "executed" }, - { "call": "request_sent" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); -} - -#[test] -fn create_service() { - let module = "greeting"; - let config = json!( - { - "name": module, - "mem_pages_count": 100, - "logger_enabled": true, - "wasi": { - "envs": json!({}), - "preopened_files": vec!["/tmp"], - "mapped_dirs": json!({}), - } - } - ); - let mut data_value = json!({ - "module_bytes": vec![1,2], - "module_config": config, - "blueprint": { "name": "blueprint", "dependencies": [module] }, - }); - let data = data_value.to_string(); - - let script = String::from( - r#"(seq ( - (call (%current_peer_id% ("add_module" "") (module_bytes module_config) module)) - (seq ( - (call (%current_peer_id% ("add_blueprint" "") (blueprint) blueprint_id)) - (seq ( - (call (%current_peer_id% ("create" "") (blueprint_id) service_id)) - (call ("remote_peer_id" ("" "") (service_id) client_result)) - )) - )) - ))"#, - ); - - let call_service: HostExportedFunc = Box::new(|_, args| -> Option { - let builtin_service = match &args[0] { - IValue::String(str) => str, - _ => unreachable!(), - }; - - let response = match builtin_service.as_str() { - "add_module" => String::from("add_module response"), - "add_blueprint" => String::from("add_blueprint response"), - "create" => String::from("create response"), - _ => String::from("unknown response"), - }; - - Some(IValue::Record( - Vec1::new(vec![IValue::S32(0), IValue::String(format!("\"{}\"", response))]).unwrap(), - )) - }); - - let mut vm = create_aqua_vm(call_service, ""); - - let res = vm - .call(json!(["init_user_pk", script, "{}", data,])) - .expect("should be successful"); - - let resulted_data: JValue = serde_json::from_str(&res.data).expect("should be correct json"); - - data_value.as_object_mut().unwrap().insert( - String::from("module"), - JValue::String(String::from("add_module response")), - ); - data_value.as_object_mut().unwrap().insert( - String::from("blueprint_id"), - JValue::String(String::from("add_blueprint response")), - ); - data_value.as_object_mut().unwrap().insert( - String::from("service_id"), - JValue::String(String::from("create response")), - ); - data_value.as_object_mut().unwrap().insert( - String::from("__call"), - json!([{"call": "executed"}, {"call": "executed"}, {"call": "executed"}, {"call": "request_sent"}]), - ); - - assert_eq!(resulted_data, data_value); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); -} diff --git a/stepper/tests/call_evidence_basic.rs b/stepper/tests/call_evidence_basic.rs deleted file mode 100644 index d5cb4d06..00000000 --- a/stepper/tests/call_evidence_basic.rs +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use aqua_test_utils::create_aqua_vm; -use aqua_test_utils::echo_number_call_service; -use aqua_test_utils::unit_call_service; -use aquamarine_vm::vec1::Vec1; -use aquamarine_vm::HostExportedFunc; -use aquamarine_vm::IValue; - -use serde_json::json; - -type JValue = serde_json::Value; - -#[test] -fn evidence_seq_par_call() { - let mut vm = create_aqua_vm(unit_call_service(), ""); - - let script = String::from( - r#" - (seq ( - (par ( - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_1)) - (call ("remote_peer_id" ("service_id" "fn_name") () g)) - )) - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) - ))"#, - ); - - let res = vm - .call(json!([ - "asd", - script, - "{}", - json!({ - "__call": [ - { "par": [1,1] }, - { "call": "executed" }, - { "call": "executed" }, - ] - }) - .to_string(), - ])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res.data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_2": "test", - "__call": [ - { "par": [1,1] }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert!(res.next_peer_pks.is_empty()); -} - -#[test] -fn evidence_par_par_call() { - let mut vm = create_aqua_vm(unit_call_service(), "some_peer_id"); - - let script = String::from( - r#" - (par ( - (par ( - (call ("some_peer_id" ("local_service_id" "local_fn_name") () result_1)) - (call ("remote_peer_id" ("service_id" "fn_name") () g)) - )) - (call (%current_peer_id% ("local_service_id" "local_fn_name") () result_2)) - ))"#, - ); - - let res = vm - .call(json!([ - "asd", - script, - "{}", - json!({ - "__call": [ - { "par": [3,0] }, - { "par": [1,1] }, - { "call": "request_sent" }, - { "call": "executed" }, - ] - }) - .to_string(), - ])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res.data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_1" : "test", - "result_2" : "test", - "__call": [ - { "par": [3,1] }, - { "par": [1,1] }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert!(res.next_peer_pks.is_empty()); -} - -#[test] -fn evidence_seq_seq() { - let peer_id_1 = String::from("12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er"); - let peer_id_2 = String::from("12D3KooWAzJcYitiZrerycVB4Wryrx22CFKdDGx7c4u31PFdfTbR"); - let mut vm1 = create_aqua_vm(unit_call_service(), peer_id_1.clone()); - let mut vm2 = create_aqua_vm(unit_call_service(), peer_id_2.clone()); - - let script = format!( - r#" - (seq ( - (call ("{}" ("identity" "") () void0)) - (seq ( - (call ("{}" ("add_blueprint" "") () blueprint_id)) - (call ("{}" ("addBlueprint-14d8488e-d10d-474d-96b2-878f6a7d74c8" "") () void1)) - )) - )) - "#, - peer_id_1, peer_id_1, peer_id_2 - ); - - let res1 = vm2 - .call(json!(["asd", script, "{}", "{}",])) - .expect("should be successful"); - - assert_eq!(res1.next_peer_pks, vec![peer_id_1.clone()]); - - let res2 = vm1 - .call(json!(["asd", script, "{}", res1.data,])) - .expect("should be successful"); - - assert_eq!(res2.next_peer_pks, vec![peer_id_2.clone()]); - - let res3 = vm2 - .call(json!(["asd", script, "{}", res2.data,])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res3.data).expect("stepper should return valid json"); - - let right_json = json!( { - "void0": "test", - "void1": "test", - "blueprint_id": "test", - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert!(res3.next_peer_pks.is_empty()); -} - -#[test] -fn evidence_create_service() { - let module = "greeting"; - let config = json!( - { - "name": module, - "mem_pages_count": 100, - "logger_enabled": true, - "wasi": { - "envs": json!({}), - "preopened_files": vec!["/tmp"], - "mapped_dirs": json!({}), - } - } - ); - let mut data_value = json!({ - "module_bytes": vec![1,2], - "module_config": config, - "blueprint": { "name": "blueprint", "dependencies": [module] }, - "__call": [ - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - let data = data_value.to_string(); - - let script = String::from( - r#" - (seq ( - (call (%current_peer_id% ("add_module" "") (module_bytes module_config) module)) - (seq ( - (call (%current_peer_id% ("add_blueprint" "") (blueprint) blueprint_id)) - (seq ( - (call (%current_peer_id% ("create" "") (blueprint_id) service_id)) - (call ("remote_peer_id" ("" "") (service_id) client_result)) - )) - )) - ))"#, - ); - - let call_service: HostExportedFunc = Box::new(|_, args| -> Option { - let builtin_service = match &args[0] { - IValue::String(str) => str, - _ => unreachable!(), - }; - - let response = match builtin_service.as_str() { - "add_module" => String::from("add_module response"), - "add_blueprint" => String::from("add_blueprint response"), - "create" => String::from("create response"), - _ => String::from("unknown response"), - }; - - Some(IValue::Record( - Vec1::new(vec![IValue::S32(0), IValue::String(format!("\"{}\"", response))]).unwrap(), - )) - }); - - let mut vm = create_aqua_vm(call_service, ""); - - let res = vm - .call(json!(["init_user_pk", script, "{}", data,])) - .expect("should be successful"); - - let resulted_data: JValue = serde_json::from_str(&res.data).expect("should be correct json"); - - data_value.as_object_mut().unwrap().insert( - String::from("__call"), - json!([{"call": "executed"}, {"call": "executed"}, {"call": "executed"}, {"call": "executed"}]), - ); - - assert_eq!(resulted_data, data_value); - assert!(res.next_peer_pks.is_empty()); -} - -#[test] -fn evidence_par_seq_fold_call() { - let return_numbers_call_service: HostExportedFunc = Box::new(|_, _| -> Option { - Some(IValue::Record( - Vec1::new(vec![ - IValue::S32(0), - IValue::String(String::from( - "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", - )), - ]) - .unwrap(), - )) - }); - - let mut vm1 = create_aqua_vm(return_numbers_call_service, "some_peer_id_1"); - let mut vm2 = create_aqua_vm(echo_number_call_service(), "some_peer_id_2"); - let mut vm3 = create_aqua_vm(unit_call_service(), "some_peer_id_3"); - - let script = String::from( - r#" - (par ( - (seq ( - (call ("some_peer_id_1" ("local_service_id" "local_fn_name") () IterableResultPeer1)) - (fold (IterableResultPeer1 i - (par ( - (call ("some_peer_id_2" ("local_service_id" "local_fn_name") (i) acc[])) - (next i) - )) - )) - )) - (call ("some_peer_id_3" ("local_service_id" "local_fn_name") () result_2)) - ))"#, - ); - - let res1 = vm2 - .call(json!([ - "asd", - script, - "{}", - json!({ - "__call": [] - }) - .to_string(), - ])) - .expect("should be successful"); - - let res2 = vm1 - .call(json!(["asd", script, "{}", res1.data,])) - .expect("should be successful"); - - let mut data = res2.data; - - for _ in 0..100 { - let res3 = vm2 - .call(json!(["asd", script, "{}", data,])) - .expect("should be successful"); - - data = res3.data; - } - - let res4 = vm3 - .call(json!(["asd", script, "{}", data,])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res4.data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_2": "test", - "IterableResultPeer1": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], - "acc": [1,2,3,4,5,6,7,8,9,10], - "__call": [ - { "par": [21,1] }, - { "call": "executed" }, - { "par": [1,18] }, - { "call": "executed" }, - { "par": [1,16] }, - { "call": "executed" }, - { "par": [1,14] }, - { "call": "executed" }, - { "par": [1,12] }, - { "call": "executed" }, - { "par": [1,10] }, - { "call": "executed" }, - { "par": [1,8] }, - { "call": "executed" }, - { "par": [1,6] }, - { "call": "executed" }, - { "par": [1,4] }, - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert!(res4.next_peer_pks.is_empty()); -} - -#[test] -fn evidence_par_seq_fold_in_cycle_call() { - let return_numbers_call_service: HostExportedFunc = Box::new(|_, _| -> Option { - Some(IValue::Record( - Vec1::new(vec![ - IValue::S32(0), - IValue::String(String::from( - "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", - )), - ]) - .unwrap(), - )) - }); - - let mut vm1 = create_aqua_vm(return_numbers_call_service, "some_peer_id_1"); - let mut vm2 = create_aqua_vm(echo_number_call_service(), "some_peer_id_2"); - let mut vm3 = create_aqua_vm(unit_call_service(), "some_peer_id_3"); - - let script = String::from( - r#" - (par ( - (seq ( - (call ("some_peer_id_1" ("local_service_id" "local_fn_name") () IterableResultPeer1)) - (fold (IterableResultPeer1 i - (par ( - (call ("some_peer_id_2" ("local_service_id" "local_fn_name") (i) acc[])) - (next i) - )) - )) - )) - (call ("some_peer_id_3" ("local_service_id" "local_fn_name") () result_2)) - ))"#, - ); - - let mut data = String::from("{}"); - - for _ in 0..100 { - let res1 = vm1 - .call(json!(["asd", script, "{}", data])) - .expect("should be successful"); - - data = res1.data; - - let res2 = vm2 - .call(json!(["asd", script, "{}", data])) - .expect("should be successful"); - - data = res2.data; - - let res3 = vm3 - .call(json!(["asd", script, "{}", data])) - .expect("should be successful"); - - data = res3.data; - } - - let resulted_json: JValue = serde_json::from_str(&data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_2": "test", - "IterableResultPeer1": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], - "acc": [1,2,3,4,5,6,7,8,9,10], - "__call": [ - { "par": [21,1] }, - { "call": "executed" }, - { "par": [1,18] }, - { "call": "executed" }, - { "par": [1,16] }, - { "call": "executed" }, - { "par": [1,14] }, - { "call": "executed" }, - { "par": [1,12] }, - { "call": "executed" }, - { "par": [1,10] }, - { "call": "executed" }, - { "par": [1,8] }, - { "call": "executed" }, - { "par": [1,6] }, - { "call": "executed" }, - { "par": [1,4] }, - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); -} - -#[test] -fn evidence_seq_par_seq_seq() { - let peer_id_1 = String::from("12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er"); - let peer_id_2 = String::from("12D3KooWAzJcYitiZrerycVB4Wryrx22CFKdDGx7c4u31PFdfTbR"); - let mut vm1 = create_aqua_vm(unit_call_service(), peer_id_1.clone()); - let mut vm2 = create_aqua_vm(unit_call_service(), peer_id_2.clone()); - let script = format!( - r#" - (seq ( - (par ( - (seq ( - (call ("{}" ("" "") () result_1)) - (call ("{}" ("" "") () result_2)) - )) - (seq ( - (call ("{}" ("" "") () result_3)) - (call ("{}" ("" "") () result_4)) - )) - )) - (call ("{}" ("" "") () result_5)) - )) - "#, - peer_id_1, peer_id_2, peer_id_2, peer_id_1, peer_id_2 - ); - - let res1 = vm2 - .call(json!(["asd", script, "{}", "{}",])) - .expect("should be successful"); - - assert_eq!(res1.next_peer_pks, vec![peer_id_1.clone()]); - - let res2 = vm1 - .call(json!(["asd", script, "{}", res1.data])) - .expect("should be successful"); - - assert_eq!(res2.next_peer_pks, vec![peer_id_2.clone()]); - - let res3 = vm2 - .call(json!(["asd", script, "{}", res2.data])) - .expect("should be successful"); - - let resulted_json: JValue = serde_json::from_str(&res3.data).expect("stepper should return valid json"); - - let right_json = json!( { - "result_1": "test", - "result_2": "test", - "result_3": "test", - "result_4": "test", - "result_5": "test", - "__call": [ - { "par": [2,2] }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json, right_json); - assert!(res3.next_peer_pks.is_empty()); -} diff --git a/stepper/tests/data_merge.rs b/stepper/tests/data_merge.rs deleted file mode 100644 index bdc6e4a7..00000000 --- a/stepper/tests/data_merge.rs +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use aqua_test_utils::create_aqua_vm; -use aquamarine_vm::vec1::Vec1; -use aquamarine_vm::HostExportedFunc; -use aquamarine_vm::IValue; - -use serde_json::json; - -type JValue = serde_json::Value; - -#[test] -#[ignore] -fn data_merge() { - let neighborhood_call_service1: HostExportedFunc = Box::new(|_, _| -> Option { - Some(IValue::Record( - Vec1::new(vec![IValue::S32(0), IValue::String(String::from("[\"A\", \"B\"]"))]).unwrap(), - )) - }); - - let neighborhood_call_service2: HostExportedFunc = Box::new(|_, _| -> Option { - Some(IValue::Record( - Vec1::new(vec![IValue::S32(0), IValue::String(String::from("[\"A\", \"B\"]"))]).unwrap(), - )) - }); - - let mut vm1 = create_aqua_vm(neighborhood_call_service1, "A"); - let mut vm2 = create_aqua_vm(neighborhood_call_service2, "B"); - - let script = String::from( - r#" - (seq ( - (call (%current_peer_id% ("neighborhood" "") () neighborhood)) - (seq ( - (seq ( - (fold (neighborhood i - (par ( - (call (i ("add_provider" "") () void[])) - (next i) - )) - )) - (fold (neighborhood i - (par ( - (call (i ("get_providers" "") () providers[])) - (next i) - )) - )) - )) - (seq ( - (call ("A" ("identity" "") () void[])) - (call ("B" ("" "") () none)) - )) - )) - )) - "#, - ); - - let res1 = vm1 - .call(json!(["asd", script, "{}", "{}"])) - .expect("should be successful"); - - let res2 = vm2 - .call(json!(["asd", script, "{}", "{}"])) - .expect("should be successful"); - - let res3 = vm2 - .call(json!(["asd", script, res1.data, res2.data])) - .expect("should be successful"); - - let res4 = vm1 - .call(json!(["asd", script, res1.data, res2.data])) - .expect("should be successful"); - - let res5 = vm2 - .call(json!(["asd", script, res3.data, res4.data])) - .expect("should be successful"); - - let res6 = vm1 - .call(json!(["asd", script, res3.data, res4.data])) - .expect("should be successful"); - - let resulted_json3: JValue = serde_json::from_str(&res3.data).expect("stepper should return valid json"); - - let right_json3 = json!( { - "void": [["A", "B"]], - "neighborhood": ["A", "B"], - "providers": [["A", "B"]], - "__call": [ - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "par": [1,2] }, - { "call": "request_sent" }, - { "par": [1,0] }, - { "call": "executed" }, - ] - }); - - assert_eq!(resulted_json3, right_json3); - assert_eq!(res3.next_peer_pks, vec![String::from("A")]); - - let resulted_json4: JValue = serde_json::from_str(&res4.data).expect("stepper should return valid json"); - - let right_json4 = json!( { - "void": [["A", "B"]], - "neighborhood": ["A", "B"], - "providers": [["A", "B"]], - "__call": [ - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "request_sent" }, - ] - }); - - assert_eq!(resulted_json4, right_json4); - assert_eq!(res4.next_peer_pks, vec![String::from("B")]); - - let resulted_json5: JValue = serde_json::from_str(&res5.data).expect("stepper should return valid json"); - - let right_json5 = json!( { - "void": [["A", "B"]], - "neighborhood": ["A", "B"], - "providers": [["A", "B"]], - "__call": [ - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "call": "request_sent" }, - ] - }); - - assert_eq!(resulted_json5, right_json5); - assert_eq!(res5.next_peer_pks, vec![String::from("A")]); - - let resulted_json6: JValue = serde_json::from_str(&res6.data).expect("stepper should return valid json"); - - let right_json6 = json!( { - "void": [["A", "B"], ["A", "B"]], - "neighborhood": ["A", "B"], - "providers": [["A", "B"]], - "__call": [ - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "par": [1,2] }, - { "call": "executed" }, - { "par": [1,0] }, - { "call": "executed" }, - { "call": "executed" }, - { "call": "request_sent" } - ] - }); - - assert_eq!(resulted_json6, right_json6); - assert_eq!(res6.next_peer_pks, vec![String::from("B")]); -}