diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..edcf814c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,43 @@ +version: 2 +jobs: + aqua: + docker: + - image: circleci/rust:latest + resource_class: medium+ + environment: + RUST_BACKTRACE: 1 + RUST_TEST_THREADS: 1 + steps: + - checkout + - restore_cache: + keys: + - aqua01-{{ checksum "Cargo.lock" }} + - run: | + rustup toolchain install nightly-2020-07-12-x86_64-unknown-linux-gnu + rustup default nightly-2020-07-12-x86_64-unknown-linux-gnu + + rustup target add wasm32-wasi + rustup component add rustfmt + rustup component add clippy + + cargo install fcli + cd stepper + fce build + + cd .. + cargo fmt --all -- --check --color always + cargo build --release --all-features + cargo test --release --all-features + cargo clippy -v + + - save_cache: + paths: + - ~/.cargo + - ~/.rustup + key: aqua01-{{ checksum "Cargo.lock" }} + +workflows: + version: 2 + arqada: + jobs: + - aqua diff --git a/.gitignore b/.gitignore index ba07b712..13445573 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ /target .DS_Store .repl_history +*.wasm + +!./artifacts/*.wasm diff --git a/Cargo.lock b/Cargo.lock index 88882086..a24912b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,10 +9,35 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" + +[[package]] +name = "aqua-test-module" +version = "0.1.0" +dependencies = [ + "fluence", +] + +[[package]] +name = "aqua-test-utils" +version = "0.1.0" +dependencies = [ + "aquamarine-vm", + "fluence", + "serde_json", +] + [[package]] name = "aquamarine" version = "0.1.0" dependencies = [ + "aqua-test-utils", + "aquamarine-vm", + "env_logger", "fluence", "jsonpath_lib", "log", @@ -23,12 +48,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "aquamarine-vm" +version = "0.1.0" +source = "git+https://github.com/fluencelabs/fce#1c2578ede462430d6d15e6c750a515600342f69d" +dependencies = [ + "fluence-faas", + "serde", + "serde_json", +] + [[package]] name = "array_tool" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "atty" version = "0.2.14" @@ -46,18 +93,239 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bincode" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake3" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9ff35b701f3914bdb8fad3368d822c766ef2858b2583198e41639b936f09d3f" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + [[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" + [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "cmd_lib" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475bd7aa7680b4ed8f6bb59745e882bcbaeb39326532bb79ffb1716480d9a274" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cranelift-bforest" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a9c21f8042b9857bda93f6c1910b9f9f24100187a3d3d52f214a34e3dc5818" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7853f77a6e4a33c67a69c40f5e1bb982bd2dc5c4a22e17e67b65bbccf9b33b2e" +dependencies = [ + "byteorder", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli", + "log", + "smallvec", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084cd6d5fb0d1da28acd72c199471bfb09acc703ec8f3bf07b1699584272a3b9" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "701b599783305a58c25027a4d73f2d6b599b2d8ef3f26677275f480b4d51e05d" + +[[package]] +name = "cranelift-entity" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88e792b28e1ebbc0187b72ba5ba880dad083abe9231a99d19604d10c9e73f38" + +[[package]] +name = "cranelift-native" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32daf082da21c0c05d93394ff4842c2ab7c4991b1f3186a1d952f8ac660edd0b" +dependencies = [ + "cranelift-codegen", + "raw-cpuid", + "target-lexicon", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "env_logger" version = "0.7.1" @@ -72,36 +340,148 @@ dependencies = [ ] [[package]] -name = "fluence" -version = "0.2.7" -source = "git+https://github.com/fluencelabs/rust-sdk#e5d564d4a61e203798383b01505ba23e54e71912" +name = "erased-serde" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca8b296792113e1500fd935ae487be6e00ce318952a6880555554824d6ebf38" dependencies = [ - "fluence-sdk-macro", - "fluence-sdk-main", + "serde", +] + +[[package]] +name = "errno" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "fce" +version = "0.1.7" +source = "git+https://github.com/fluencelabs/fce#1c2578ede462430d6d15e6c750a515600342f69d" +dependencies = [ + "boolinator", + "fce-wit-interfaces", + "fce-wit-parser", + "log", + "multi-map", + "multimap", + "parity-wasm", + "pwasm-utils", + "safe-transmute", + "wasmer-interface-types-fl", + "wasmer-runtime-core-fl", + "wasmer-runtime-fl", + "wasmer-wasi-fl", +] + +[[package]] +name = "fce-wit-interfaces" +version = "0.1.5" +source = "git+https://github.com/fluencelabs/fce#1c2578ede462430d6d15e6c750a515600342f69d" +dependencies = [ + "multimap", + "wasmer-interface-types-fl", +] + +[[package]] +name = "fce-wit-parser" +version = "0.1.7" +source = "git+https://github.com/fluencelabs/fce#1c2578ede462430d6d15e6c750a515600342f69d" +dependencies = [ + "anyhow", + "fce-wit-interfaces", + "walrus", + "wasmer-interface-types-fl", + "wasmer-runtime-core-fl", +] + +[[package]] +name = "fluence" +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)", + "fluence-sdk-main 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", +] + +[[package]] +name = "fluence-faas" +version = "0.1.8" +source = "git+https://github.com/fluencelabs/fce#1c2578ede462430d6d15e6c750a515600342f69d" +dependencies = [ + "cmd_lib", + "fce", + "fluence-sdk-main 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools", + "log", + "safe-transmute", + "serde", + "serde_derive", + "serde_json", + "toml", + "wasmer-interface-types-fl", + "wasmer-runtime-core-fl", + "wasmer-runtime-fl", + "wasmer-wasi-fl", ] [[package]] name = "fluence-sdk-macro" -version = "0.2.7" -source = "git+https://github.com/fluencelabs/rust-sdk#e5d564d4a61e203798383b01505ba23e54e71912" +version = "0.2.8" +source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" dependencies = [ - "fluence-sdk-wit", + "fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", +] + +[[package]] +name = "fluence-sdk-macro" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b1720124376ac6bb13e523e3eceeef4475041a03a6434ca7d988cbdc031a5c4" +dependencies = [ + "fluence-sdk-wit 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fluence-sdk-main" -version = "0.2.7" -source = "git+https://github.com/fluencelabs/rust-sdk#e5d564d4a61e203798383b01505ba23e54e71912" +version = "0.2.8" +source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" dependencies = [ - "fluence-sdk-macro", + "fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", + "log", + "serde", +] + +[[package]] +name = "fluence-sdk-main" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5484ff6f5e091d4904217c45ed86cd0215e496090adaa9716e510742177fc512" +dependencies = [ + "fluence-sdk-macro 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log", "serde", ] [[package]] name = "fluence-sdk-wit" -version = "0.2.7" -source = "git+https://github.com/fluencelabs/rust-sdk#e5d564d4a61e203798383b01505ba23e54e71912" +version = "0.2.8" +source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd" dependencies = [ "proc-macro2", "quote", @@ -111,6 +491,55 @@ dependencies = [ "uuid", ] +[[package]] +name = "fluence-sdk-wit" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "uuid", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generational-arena" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" +dependencies = [ + "cfg-if", + "serde", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check 0.9.2", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -119,7 +548,28 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghost" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5bcf1bbeab73aa4cf2fde60a846858dc036163c7c33bec309f8d17de785479" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gimli" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" +dependencies = [ + "byteorder", + "indexmap", ] [[package]] @@ -129,14 +579,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] -name = "hermit-abi" -version = "0.1.16" +name = "heck" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + [[package]] name = "humantime" version = "1.3.0" @@ -146,6 +611,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + [[package]] name = "indexmap" version = "1.6.0" @@ -154,6 +625,38 @@ checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ "autocfg", "hashbrown", + "serde", +] + +[[package]] +name = "inventory" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedd49de24d8c263613701406611410687148ae8c37cd6452650b250f753a0dd" +dependencies = [ + "ctor", + "ghost", + "inventory-impl", +] + +[[package]] +name = "inventory-impl" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddead8880bc50f57fcd3b5869a7f6ff92570bb4e8f6870c22e2483272f2256da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", ] [[package]] @@ -182,10 +685,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "libc" -version = "0.2.77" +name = "leb128" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" + +[[package]] +name = "lexical-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] [[package]] name = "log" @@ -196,12 +727,65 @@ dependencies = [ "cfg-if", ] +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + +[[package]] +name = "multi-map" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bba551d6d795f74a01767577ea8339560bf0a65354e0417b7e915ed608443d46" + +[[package]] +name = "multimap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" +dependencies = [ + "serde", +] + +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + [[package]] name = "nom" version = "4.2.3" @@ -209,7 +793,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" dependencies = [ "memchr", - "version_check", + "version_check 0.1.5", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check 0.9.2", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" + +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi", ] [[package]] @@ -220,13 +865,24 @@ checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "proc-macro2" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] +[[package]] +name = "pwasm-utils" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" +dependencies = [ + "byteorder", + "log", + "parity-wasm", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -283,6 +939,48 @@ dependencies = [ "rand_core", ] +[[package]] +name = "raw-cpuid" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" +dependencies = [ + "bitflags", + "cc", + "rustc_version", +] + +[[package]] +name = "rayon" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "regex" version = "1.3.9" @@ -301,12 +999,48 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safe-transmute" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b8b2cd387f744f69469aaed197954ba4c0ecdb31e02edf99b023e0df11178a" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.116" @@ -316,6 +1050,25 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-bench" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.116" @@ -329,9 +1082,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" dependencies = [ "indexmap", "itoa", @@ -345,10 +1098,28 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5318bfeed779c64075ce317c81462ed54dc00021be1c6b34957d798e11a68bdb" dependencies = [ - "nom", + "nom 4.2.3", "serde", ] +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" + [[package]] name = "syn" version = "1.0.42" @@ -360,6 +1131,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "target-lexicon" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" + [[package]] name = "termcolor" version = "1.1.0" @@ -369,6 +1146,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -378,6 +1175,62 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "typetag" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b97b107d25d29de6879ac4f676ac5bfea92bdd01f206e995794493f1fc2e32" +dependencies = [ + "erased-serde", + "inventory", + "lazy_static", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2466fc87b07b800a5060f89ba579d6882f7a03ac21363e4737764aaf9f99f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + [[package]] name = "unicode-xid" version = "0.2.1" @@ -399,12 +1252,56 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walrus" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f970863270179a4e0ca2bfb470931f883f7535ae8b9dac4271761fa1b77e253d" +dependencies = [ + "anyhow", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasmparser 0.55.0", +] + +[[package]] +name = "walrus-macro" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80927fca8665132b48c7714bcec983d6e761c60b3a9877c6cd7df86417b13573" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasm-bindgen" version = "0.2.68" @@ -459,6 +1356,197 @@ version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" +[[package]] +name = "wasmer-clif-backend-fl" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532fb6cef712d9cd1ec68f9fc561447df33313393926f9bdb91fc3d67eed918f" +dependencies = [ + "byteorder", + "cranelift-codegen", + "cranelift-entity", + "cranelift-native", + "libc", + "nix", + "rayon", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "target-lexicon", + "wasmer-clif-fork-frontend", + "wasmer-clif-fork-wasm", + "wasmer-runtime-core-fl", + "wasmer-win-exception-handler", + "wasmparser 0.51.4", + "winapi", +] + +[[package]] +name = "wasmer-clif-fork-frontend" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23f2824f354a00a77e4b040eef6e1d4c595a8a3e9013bad65199cc8dade9a5a" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "wasmer-clif-fork-wasm" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e21d3aebc51cc6ebc0e830cf8458a9891c3482fb3c65ad18d408102929ae5" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "log", + "thiserror", + "wasmer-clif-fork-frontend", + "wasmparser 0.51.4", +] + +[[package]] +name = "wasmer-interface-types-fl" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475cafb8bf9763895f6fde0d7417b9090145fc461ad7aef21b1a866b8357c091" +dependencies = [ + "log", + "nom 5.1.2", + "safe-transmute", + "serde", + "serde_json", + "wast", +] + +[[package]] +name = "wasmer-runtime-core" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740161245998752cf1a567e860fd6355df0336fedca6be1940ec7aaa59643220" +dependencies = [ + "bincode", + "blake3", + "cc", + "digest 0.8.1", + "errno", + "hex", + "indexmap", + "lazy_static", + "libc", + "nix", + "page_size", + "parking_lot", + "rustc_version", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "smallvec", + "target-lexicon", + "wasmparser 0.51.4", + "winapi", +] + +[[package]] +name = "wasmer-runtime-core-fl" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4630ff544a2d7f76938bcf82ae217e0bd5c9ee078c653459d4f117c2045d315" +dependencies = [ + "bincode", + "blake3", + "cc", + "digest 0.8.1", + "errno", + "hex", + "indexmap", + "lazy_static", + "libc", + "nix", + "page_size", + "parking_lot", + "rustc_version", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "smallvec", + "target-lexicon", + "wasmparser 0.51.4", + "winapi", +] + +[[package]] +name = "wasmer-runtime-fl" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e855c8942f998a6938e43c1d0bb11b76a60b1c8341e08db36cc36b97b58bb1e9" +dependencies = [ + "lazy_static", + "memmap", + "serde", + "serde_derive", + "wasmer-clif-backend-fl", + "wasmer-runtime-core-fl", +] + +[[package]] +name = "wasmer-wasi-fl" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2493c1ef951a7a1704ecfd39abbbbb2346bf3768d761ef64ed53249a32b181" +dependencies = [ + "bincode", + "byteorder", + "generational-arena", + "getrandom", + "libc", + "log", + "serde", + "thiserror", + "time", + "typetag", + "wasmer-runtime-core-fl", + "winapi", +] + +[[package]] +name = "wasmer-win-exception-handler" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd39f3b2bd7964b28ea6f944a7eaa445cfbc91c4f2695d188103f2689bb37d9" +dependencies = [ + "cc", + "libc", + "wasmer-runtime-core", + "winapi", +] + +[[package]] +name = "wasmparser" +version = "0.51.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" + +[[package]] +name = "wasmparser" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af931e2e1960c53f4a28b063fec4cacd036f35acbec8ff3a4739125b17382a87" + +[[package]] +name = "wast" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9df3d716118a503b2f6bbb6ff46b21997ab0cc167b01de7a188e45e4b01e8d" +dependencies = [ + "leb128", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 4054a328..d3bb1a6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,31 +1,14 @@ -[package] -name = "aquamarine" -version = "0.1.0" -authors = ["Fluence Labs"] -edition = "2018" +[workspace] +members = [ + "crates/test-module", + "crates/test-utils", + "stepper", +] -[lib] -name = "aquamarine_client" -crate-type = ["cdylib"] -path = "src/wasm_bindgen.rs" - -[[bin]] -name = "aquamarine" -path = "src/fce.rs" - -[[bin]] -name = "aqua_test_module" -path = "test_module/main.rs" - -[dependencies] -fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } - -serde = { version = "1.0.116", features = ["derive"] } -serde_derive = "1.0.116" -serde_sexpr = "0.1.0" - -jsonpath_lib = "0.2.5" - -log = "0.4.11" -serde_json = "1.0" -wasm-bindgen = "0.2.68" +[profile.release] +opt-level = 3 +debug = false +lto = true +debug-assertions = false +overflow-checks = false +panic = "abort" diff --git a/Config.toml b/Config.toml index a2ff56f3..58f44d1b 100644 --- a/Config.toml +++ b/Config.toml @@ -11,4 +11,4 @@ name = "aquamarine" logger_enabled = true [module.wasi] - envs = ["CURRENT_PEER_ID=asd"] + envs = { "CURRENT_PEER_ID" = "some_peer_id" } diff --git a/README.md b/README.md index 90d58dc5..25806795 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,36 @@ Aquamarine is a distributed choreography language & platform +## AIR + +The current version supports the following instructions: +- call +- par +- seq +- fold +- next +- null + ## Examples ```lisp -((call (%current% (local_service_id local_fn_name) () result_name)) (call (remote_peer_id (service_id fn_name) () g))) +(seq ( + (call (%current_peer_id1% (local_service_id local_fn_name) () result_name_1)) + (call (remote_peer_id (service_id fn_name) () result_name_2)) +)), ``` -This instruction sequence contains two call instructions: + +This instruction sequence contains two call instructions in the sequential order: 1. call a function with `local_fn_name` name of a local service with `local_service_id` id and bind result to `result_name` 2. call a remote peer with `remote_peer_id` id + +```lisp +(fold (Iterable i + (seq ( + (call (%current_peer_id% (local_service_id local_fn_name) (i) acc[])) + (next i) + ) +))) +``` + +This example is an analog of left fold. It iterates over `Iterable` and on each iteration calls `local_service_id` and puts result to `acc`. diff --git a/artifacts/aquamarine.wasm b/artifacts/aquamarine.wasm index fe7ff77c..e9522520 100644 Binary files a/artifacts/aquamarine.wasm and b/artifacts/aquamarine.wasm differ diff --git a/crates/test-module/Cargo.toml b/crates/test-module/Cargo.toml new file mode 100644 index 00000000..d8706af4 --- /dev/null +++ b/crates/test-module/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "aqua-test-module" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[[bin]] +name = "aqua_test_module" +path = "src/main.rs" + +[dependencies] +fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } diff --git a/crates/test-module/src/main.rs b/crates/test-module/src/main.rs new file mode 100644 index 00000000..68d58808 --- /dev/null +++ b/crates/test-module/src/main.rs @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +use fluence::fce; + +fn main() {} + +#[fce] +pub struct CallServiceResult { + pub ret_code: i32, + pub result: String, +} + +#[fce] +pub fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult { + println!( + "call service invoked with:\n service_id: {}\n fn_name: {}\n args: {:?}", + service_id, fn_name, args + ); + + CallServiceResult { + ret_code: 0, + result: String::from("[\"result string\"]"), + } +} diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml new file mode 100644 index 00000000..bea79586 --- /dev/null +++ b/crates/test-utils/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aqua-test-utils" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[lib] +name = "aqua_test_utils" +path = "src/lib.rs" + +[dependencies] +fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } +aquamarine-vm = { git = "https://github.com/fluencelabs/fce" } +serde_json = "1.0.56" diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs new file mode 100644 index 00000000..702c0265 --- /dev/null +++ b/crates/test-utils/src/lib.rs @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +use aquamarine_vm::vec1::Vec1; +use aquamarine_vm::AquamarineVM; +use aquamarine_vm::AquamarineVMConfig; +use aquamarine_vm::HostExportedFunc; +use aquamarine_vm::HostImportDescriptor; +use aquamarine_vm::IType; +use aquamarine_vm::IValue; + +use std::path::PathBuf; + +pub fn create_aqua_vm(call_service: HostExportedFunc) -> AquamarineVM { + let call_service_descriptor = HostImportDescriptor { + host_exported_func: call_service, + argument_types: vec![IType::String, IType::String, IType::String], + output_type: Some(IType::Record(0)), + error_handler: None, + }; + + let config = AquamarineVMConfig { + aquamarine_wasm_path: PathBuf::from("../target/wasm32-wasi/debug/aquamarine.wasm"), + call_service: call_service_descriptor, + current_peer_id: String::from("test_peer_id"), + }; + + AquamarineVM::new(config).expect("vm should be created") +} + +pub fn unit_call_service() -> HostExportedFunc { + Box::new(|_, _| -> Option { + Some(IValue::Record( + Vec1::new(vec![ + IValue::S32(0), + IValue::String(String::from("\"test\"")), + ]) + .unwrap(), + )) + }) +} + +pub fn echo_string_call_service() -> HostExportedFunc { + Box::new(|_, args| -> Option { + let arg = match &args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let arg: Vec = serde_json::from_str(arg).unwrap(); + + Some(IValue::Record( + Vec1::new(vec![ + IValue::S32(0), + IValue::String(format!("\"{}\"", arg[0])), + ]) + .unwrap(), + )) + }) +} + +pub fn echo_number_call_service() -> HostExportedFunc { + Box::new(|_, args| -> Option { + let arg = match &args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let arg: Vec = serde_json::from_str(arg).unwrap(); + + Some(IValue::Record( + Vec1::new(vec![IValue::S32(0), IValue::String(arg[0].clone())]).unwrap(), + )) + }) +} diff --git a/src/air.rs b/src/air.rs deleted file mode 100644 index 1975460d..00000000 --- a/src/air.rs +++ /dev/null @@ -1,170 +0,0 @@ -/* - - - -null -out: a!b . P -inp: a(b). P <- barrier -par: P | Q -new: nx . P <- restriction -rep: !P -sum: P + Q - -(seq --- arrow - (proc -> new x) - (proc that uses x) -) - -Scope: -a <- A -a -> B - -(seq - (par - x <- P - y <- Q - ) - (call fn [x y] z) -) - -(seq - (call fn1 [] x) - (call fn2 [x] y) -) - -(seq P Q) <- scope -(par P Q) <- independent -(xor P Q) <- lazy - --- fire and forget (any) -(seq - (par - x <- P - y <- Q - ) - (call fn [] z) -) - --- join -(seq - (par - x <- P - y <- Q - ) - (call noop [x y]) -) - --- any (fastest) -(seq - (par - x[] <- P - x[] <- Q - ) - (call fn [x.0]) -) - -data - HashMap -x.h.a.v -> (x - key in data, h.a.v - json path) - -0 -1 - -(call 1 (counter inc) [x.a] b) -(call 0 (response inc) [b] _) - --- any (fastest) -- does not work -(seq - (seq - (par - x <- P - y <- Q - ) - (xor - (call fn [x.h.a.v, x.a.b.n] z) - (call fn [y] z) - ) - ) - (call fn11 [z]) -) - -ITERATORS - -(seq - (fold Iterable i - (par - (call fn [i] acc[]) - (next i) - ) - ) - (match acc.length 3 - (call fnAgg [acc] y) - ) -) - - - - - (par - (call fn [i.0] acc[]) - (par - (call fn [i.1] acc[]) - (par - (call fn [i.2 acc[]) - (fold Iterable[3,..] i - (par - (call fn [i] acc[]) - (next i) - ) - ) - ) - ) - ) - - -(seq - (fold Iterable i - (seq - (call fn [i acc] acc[]) - (next i) - ) - ) - (call fnAgg [acc] y) -) - -(seq - (fold Iterable i - (xor - (call fn [i] res) - (next i) - ) - ) - (call fnAgg [res] y) -) - - */ - -/* - -Addressing: - -To address a code we need to declare: -(peer_pk, srv_id, fn_name) - -(call PEER_PART FN_PART [args] res_name) - -(current) -(pk $pk) -(pk $pk $srv_id) -PEER_PART: resolves to (peer_pk) \/ (peer_pk, pk_srv_id) - -(fn $name) -(fn $name $srv_id) -FN_PART: resolves to (fn_name) \/ (fn_srv_id, fn_name) - -Call resolves to: -(peer_pk, fn_srv_id || pk_srv_id, fn_name) -If !fn_srv_id && !pk_srv_id <- error - - -(call (current) (fn "resolve" "by_pk") [pk]) - */ diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 4fedb9d1..00000000 --- a/src/errors.rs +++ /dev/null @@ -1,120 +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 crate::StepperOutcome; - -use jsonpath_lib::JsonPathError; -use serde_json::Error as SerdeJsonError; -use serde_sexpr::Error as SExprError; - -use std::convert::Into; -use std::env::VarError; -use std::error::Error; - -#[derive(Debug)] -pub enum AquamarineError { - /// Errors occurred while parsing aqua script in the form of S expressions. - SExprParseError(SExprError), - - /// Errors occurred while parsing supplied data. - DataParseError(SerdeJsonError), - - /// Indicates that environment variable with name CURRENT_PEER_ID isn't set. - CurrentPeerIdNotSet(VarError), - - /// Semantic errors in instructions. - InstructionError(String), - - /// Semantic errors in instructions. - LocalServiceError(String), - - /// Value with such name isn't presence in data. - VariableNotFound(String), - - /// Value with such path isn't found in data with such error. - VariableNotInJsonPath(String, JsonPathError), - - /// Multiple values found for such json path. - MultipleValuesInJsonPath(String), -} - -impl Error for AquamarineError {} - -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, "{}", err), - AquamarineError::DataParseError(err) => write!(f, "{}", err), - AquamarineError::CurrentPeerIdNotSet(err) => write!(f, "{}", err), - AquamarineError::InstructionError(err_msg) => write!(f, "{}", err_msg), - AquamarineError::LocalServiceError(err_msg) => write!(f, "{}", err_msg), - AquamarineError::VariableNotFound(variable_name) => write!( - f, - "variable with name {} isn't present in data", - variable_name - ), - AquamarineError::VariableNotInJsonPath(json_path, json_path_err) => write!( - f, - "variable with path {} not found with error: {}", - json_path, json_path_err - ), - AquamarineError::MultipleValuesInJsonPath(json_path) => write!( - f, - "multiple variables found for this json path {}", - json_path - ), - } - } -} - -impl From for AquamarineError { - fn from(err: SExprError) -> Self { - AquamarineError::SExprParseError(err) - } -} - -impl From for AquamarineError { - fn from(err: SerdeJsonError) -> Self { - AquamarineError::DataParseError(err) - } -} - -impl From for AquamarineError { - fn from(_: std::convert::Infallible) -> Self { - unreachable!() - } -} - -impl Into for AquamarineError { - fn into(self) -> StepperOutcome { - let ret_code = match self { - AquamarineError::SExprParseError(_) => 1, - AquamarineError::DataParseError(..) => 2, - AquamarineError::CurrentPeerIdNotSet(..) => 3, - AquamarineError::InstructionError(..) => 4, - AquamarineError::LocalServiceError(..) => 5, - AquamarineError::VariableNotFound(..) => 6, - AquamarineError::VariableNotInJsonPath(..) => 7, - AquamarineError::MultipleValuesInJsonPath(..) => 8, - }; - - StepperOutcome { - ret_code, - data: format!("{}", self), - next_peer_pks: vec![], - } - } -} diff --git a/src/instructions/call.rs b/src/instructions/call.rs deleted file mode 100644 index 5aa46140..00000000 --- a/src/instructions/call.rs +++ /dev/null @@ -1,144 +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 crate::AquaData; -use crate::AquamarineError; -use crate::Result; - -use serde_derive::Deserialize; -use serde_derive::Serialize; - -const CURRENT_PEER_ALIAS: &str = "%current%"; -const CURRENT_PEER_ID_ENV_NAME: &str = "CURRENT_PEER_ID"; - -/* - (current) - (pk $pk) - (pk $pk $srv_id) - PEER_PART: resolves to (peer_pk) \/ (peer_pk, pk_srv_id) - - (fn $name) - (fn $name $srv_id) - FN_PART: resolves to (fn_name) \/ (fn_srv_id, fn_name) -*/ - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -#[serde(untagged)] -pub enum PeerPart { - PeerPk(String), - PeerPkWithPkServiceId(String, String), -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -#[serde(untagged)] -pub enum FunctionPart { - FuncName(String), - ServiceIdWithFuncName(String, String), -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -pub(crate) struct Call(PeerPart, FunctionPart, Vec, String); - -impl super::ExecutableInstruction for Call { - fn execute(self, data: &mut AquaData, next_peer_pks: &mut Vec) -> Result<()> { - log::info!("call called with data: {:?} and next_peer_pks: {:?}", data, next_peer_pks); - - let (peer_pk, service_id, func_name) = parse_peer_fn_parts(self.0, self.1)?; - let function_args = parse_args(self.2, data)?; - let function_args = serde_json::to_string(&function_args)?; - let result_name = parse_result_name(self.3)?; - - let current_peer_id = std::env::var(CURRENT_PEER_ID_ENV_NAME) - .map_err(|e| AquamarineError::CurrentPeerIdNotSet(e))?; - - if peer_pk == current_peer_id || peer_pk == CURRENT_PEER_ALIAS { - let result = unsafe { crate::call_service(service_id, func_name, function_args) }; - if result.ret_code != crate::CALL_SERVICE_SUCCESS { - return Err(AquamarineError::LocalServiceError(result.result)); - } - - let result: serde_json::Value = serde_json::from_str(&result.result)?; - data.insert(result_name, result); - } else { - next_peer_pks.push(peer_pk); - } - - Ok(()) - } -} - -#[rustfmt::skip] -fn parse_peer_fn_parts( - peer_part: PeerPart, - fn_part: FunctionPart, -) -> Result<(String, String, String)> { - match (peer_part, fn_part) { - (PeerPart::PeerPkWithPkServiceId(peer_pk, peer_service_id), FunctionPart::ServiceIdWithFuncName(_service_id, func_name)) => { - Ok((peer_pk, peer_service_id, func_name)) - }, - (PeerPart::PeerPkWithPkServiceId(peer_pk, peer_service_id), FunctionPart::FuncName(func_name)) => { - Ok((peer_pk, peer_service_id, func_name)) - }, - (PeerPart::PeerPk(peer_pk), FunctionPart::ServiceIdWithFuncName(service_id, func_name)) => { - Ok((peer_pk, service_id, func_name)) - } - (PeerPart::PeerPk(_), FunctionPart::FuncName(_)) => Err(AquamarineError::InstructionError( - String::from("call should have service id specified by peer part or function part"), - )), - } -} - -fn parse_args(args: Vec, data: &AquaData) -> Result { - let mut result = Vec::with_capacity(args.len()); - - for arg in args { - let mut split_arg: Vec<&str> = arg.splitn(1, '.').collect(); - let variable_name = split_arg.remove(0); - - let value_by_key = data - .get(variable_name) - .ok_or_else(|| AquamarineError::VariableNotFound(String::from(variable_name)))?; - - let value = if !split_arg.is_empty() { - let json_path = split_arg.remove(0); - let values = jsonpath_lib::select(&value_by_key, json_path) - .map_err(|e| AquamarineError::VariableNotInJsonPath(String::from(json_path), e))?; - if values.len() != 1 { - return Err(AquamarineError::MultipleValuesInJsonPath(String::from( - json_path, - ))); - } - - values[0].clone() - } else { - value_by_key.clone() - }; - - result.push(value); - } - - Ok(serde_json::Value::Array(result)) -} - -fn parse_result_name(result_name: String) -> Result { - if !result_name.is_empty() { - Ok(result_name) - } else { - Err(AquamarineError::InstructionError(String::from( - "result name of a call instruction must be non empty", - ))) - } -} diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs deleted file mode 100644 index bb44260b..00000000 --- a/src/instructions/mod.rs +++ /dev/null @@ -1,49 +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. - */ - -mod call; -mod null; - -pub(self) use crate::stepper::ExecutableInstruction; - -use crate::AquaData; -use crate::Result; -use call::Call; -use null::Null; - -use serde_derive::Deserialize; -use serde_derive::Serialize; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -#[serde(rename_all = "kebab-case")] -pub(crate) enum Instruction { - Null(Null), - Call(Call), - /* - Par(Box, Box), - Seq(Box, Box), - - */ -} - -impl ExecutableInstruction for Instruction { - fn execute(self, data: &mut AquaData, next_peer_pks: &mut Vec) -> Result<()> { - match self { - Instruction::Null(null) => null.execute(data, next_peer_pks), - Instruction::Call(call) => call.execute(data, next_peer_pks), - } - } -} diff --git a/src/stepper/execution.rs b/src/stepper/execution.rs deleted file mode 100644 index 7ab345cb..00000000 --- a/src/stepper/execution.rs +++ /dev/null @@ -1,51 +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::StepperOutcome; -use crate::instructions::Instruction; -use crate::AquaData; -use crate::Result; - -pub(crate) fn execute_aqua(init_user_id: String, aqua: String, data: String) -> StepperOutcome { - log::info!( - "stepper invoked with user_id = {}, aqua = {:?}, data = {:?}", - init_user_id, - aqua, - data - ); - - execute_aqua_impl(init_user_id, aqua, data).unwrap_or_else(Into::into) -} - -fn execute_aqua_impl(init_user_id: String, aqua: String, data: String) -> Result { - let mut parsed_data: AquaData = serde_json::from_str(&data)?; - let parsed_aqua = serde_sexpr::from_str::>(&aqua)?; - - log::info!( - "parsed_aqua: {:?}\nparsed_data: {:?}", - parsed_aqua, - parsed_data - ); - - let next_peer_pks = super::stepper::execute(parsed_aqua, &mut parsed_data)?; - let data = serde_json::to_string(&parsed_data)?; - - Ok(StepperOutcome { - ret_code: 0, - data, - next_peer_pks, - }) -} diff --git a/src/stepper/mod.rs b/src/stepper/mod.rs deleted file mode 100644 index a562f9b8..00000000 --- a/src/stepper/mod.rs +++ /dev/null @@ -1,25 +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. - */ - -mod execution; -mod stepper; -mod stepper_outcome; - -pub use stepper_outcome::StepperOutcome; -pub use stepper_outcome::SUCCESS_ERROR_CODE; - -pub(crate) use execution::execute_aqua; -pub(crate) use stepper::ExecutableInstruction; diff --git a/src/stepper/stepper.rs b/src/stepper/stepper.rs deleted file mode 100644 index 49197f73..00000000 --- a/src/stepper/stepper.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 crate::instructions::Instruction; -use crate::AquaData; -use crate::Result; - -pub(crate) trait ExecutableInstruction { - fn execute(self, data: &mut AquaData, next_peer_pks: &mut Vec) -> Result<()>; -} - -pub(crate) fn execute(instructions: Vec, data: &mut AquaData) -> Result> { - let mut next_peer_pks = Vec::new(); - - for instruction in instructions { - instruction.execute(data, &mut next_peer_pks)?; - } - - Ok(next_peer_pks) -} diff --git a/stepper/Cargo.toml b/stepper/Cargo.toml new file mode 100644 index 00000000..c7e0e97a --- /dev/null +++ b/stepper/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "aquamarine" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[lib] +name = "aquamarine_client" +crate-type = ["cdylib"] +path = "src/wasm_bindgen.rs" + +[[bin]] +name = "aquamarine" +path = "src/fce.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" + +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" } + +env_logger = "0.7.1" +serde_json = "1.0.56" diff --git a/stepper/src/air/call.rs b/stepper/src/air/call.rs new file mode 100644 index 00000000..f4e2c1a4 --- /dev/null +++ b/stepper/src/air/call.rs @@ -0,0 +1,274 @@ +/* + * 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::ExecutionContext; +use crate::AquamarineError; +use crate::JValue; +use crate::Result; + +use serde_derive::Deserialize; +use serde_derive::Serialize; + +const CURRENT_PEER_ALIAS: &str = "%current_peer_id%"; + +/* + (current) + (pk $pk) + (pk $pk $srv_id) + PEER_PART: resolves to (peer_pk) \/ (peer_pk, pk_srv_id) + + (fn $name) + (fn $name $srv_id) + FN_PART: resolves to (fn_name) \/ (fn_srv_id, fn_name) +*/ + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +#[serde(untagged)] +pub enum PeerPart { + PeerPk(String), + PeerPkWithPkServiceId(String, String), +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +#[serde(untagged)] +pub enum FunctionPart { + FuncName(String), + ServiceIdWithFuncName(String, String), +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub(crate) struct Call(PeerPart, FunctionPart, Vec, String); + +impl super::ExecutableInstruction for Call { + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()> { + log::info!("call {:?} is called with context {:?}", self, ctx); + + let peer_part = &self.0; + let function_part = &self.1; + let arguments = &self.2; + let result_variable_name = &self.3; + + let (peer_pk, service_id, func_name) = parse_peer_fn_parts(peer_part, function_part)?; + let function_args = parse_args(arguments, ctx)?; + let function_args = serde_json::to_string(&function_args) + .map_err(|e| AquamarineError::FuncArgsSerdeError(function_args, e))?; + let result_variable_name = parse_result_variable_name(result_variable_name)?; + + if peer_pk == ctx.current_peer_id || peer_pk == CURRENT_PEER_ALIAS { + let result = unsafe { + crate::call_service(service_id.to_string(), func_name.to_string(), 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::CallServiceSerdeError(result, e))?; + set_result(ctx, result_variable_name, result)?; + } else { + ctx.next_peer_pks.push(peer_pk.to_string()); + } + + Ok(()) + } +} + +#[rustfmt::skip] +fn parse_peer_fn_parts<'a>( + peer_part: &'a PeerPart, + fn_part: &'a FunctionPart, +) -> Result<(&'a str, &'a str, &'a str)> { + match (peer_part, fn_part) { + (PeerPart::PeerPkWithPkServiceId(peer_pk, peer_service_id), FunctionPart::ServiceIdWithFuncName(_service_id, func_name)) => { + Ok((peer_pk, peer_service_id, func_name)) + }, + (PeerPart::PeerPkWithPkServiceId(peer_pk, peer_service_id), FunctionPart::FuncName(func_name)) => { + Ok((peer_pk, peer_service_id, func_name)) + }, + (PeerPart::PeerPk(peer_pk), FunctionPart::ServiceIdWithFuncName(service_id, func_name)) => { + Ok((peer_pk, service_id, func_name)) + } + (PeerPart::PeerPk(_), FunctionPart::FuncName(_)) => Err(AquamarineError::InstructionError( + String::from("call should have service id specified by peer part or function part"), + )), + } +} + +#[rustfmt::skip] +fn parse_args(args: &[String], ctx: &ExecutionContext) -> Result { + let mut result = Vec::with_capacity(args.len()); + + for arg in args { + let mut split_arg: Vec<&str> = arg.splitn(2, '.').collect(); + let variable_name = split_arg.remove(0); + + let value_by_key = match (ctx.data.get(variable_name), ctx.folds.get(variable_name)) { + (_, 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(variable_name.to_string())), + }; + + let value = if !split_arg.is_empty() { + let json_path = split_arg.remove(0); + let values = jsonpath_lib::select(value_by_key, json_path) + .map_err(|e| AquamarineError::VariableNotInJsonPath(String::from(json_path), e))?; + + if values.len() != 1 { + return Err(AquamarineError::MultipleValuesInJsonPath(String::from( + json_path, + ))); + } + + values[0].clone() + } else { + value_by_key.clone() + }; + + result.push(value); + } + + Ok(JValue::Array(result)) +} + +fn parse_result_variable_name(result_name: &str) -> Result<&str> { + if !result_name.is_empty() { + Ok(result_name) + } else { + Err(AquamarineError::InstructionError(String::from( + "result name of a call instruction must be non empty", + ))) + } +} + +fn set_result( + ctx: &mut ExecutionContext, + result_variable_name: &str, + result: JValue, +) -> Result<()> { + use std::collections::hash_map::Entry; + + let is_array = result_variable_name.ends_with("[]"); + if !is_array { + if ctx + .data + .insert(result_variable_name.to_string(), result) + .is_some() + { + return Err(AquamarineError::MultipleVariablesFound( + result_variable_name.to_string(), + )); + } + + return Ok(()); + } + + match ctx + .data + // unwrap is safe because it's been checked for [] + .entry(result_variable_name.strip_suffix("[]").unwrap().to_string()) + { + Entry::Occupied(mut entry) => match entry.get_mut() { + JValue::Array(values) => values.push(result), + v => { + return Err(AquamarineError::IncompatibleJValueType( + v.clone(), + String::from("Array"), + )) + } + }, + Entry::Vacant(entry) => { + entry.insert(JValue::Array(vec![result])); + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::JValue; + + use aqua_test_utils::create_aqua_vm; + use aqua_test_utils::echo_string_call_service; + + use serde_json::json; + + #[test] + fn current_peer_id_call() { + let mut vm = create_aqua_vm(echo_string_call_service()); + + let script = String::from( + r#" + (call (%current_peer_id% (local_service_id local_fn_name) (value) result_name)) + "#, + ); + + let res = vm + .call(json!([ + String::from("asd"), + script, + String::from("{\"value\": \"test\"}"), + ])) + .expect("call should be successful"); + + let res: JValue = serde_json::from_str(&res.data).unwrap(); + + assert_eq!(res.get("result_name").unwrap(), &json!("test")); + + let script = String::from( + r#" + (call (test_peer_id (local_service_id local_fn_name) (value) result_name)) + "#, + ); + + let res = vm + .call(json!([ + String::from("asd"), + script, + String::from("{\"value\": \"test\"}"), + ])) + .expect("call should be successful"); + + let res: JValue = serde_json::from_str(&res.data).unwrap(); + + assert_eq!(res.get("result_name").unwrap(), &json!("test")); + } + + #[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"); + + let script = format!( + "(call ({} (local_service_id local_fn_name) (value) result_name))", + remote_peer_id + ); + + let res = vm + .call(json!([ + String::from("asd"), + script, + String::from("{\"value\": \"test\"}"), + ])) + .expect("call should be successful"); + + assert_eq!(res.next_peer_pks, vec![remote_peer_id]); + } +} diff --git a/stepper/src/air/fold.rs b/stepper/src/air/fold.rs new file mode 100644 index 00000000..87a26593 --- /dev/null +++ b/stepper/src/air/fold.rs @@ -0,0 +1,272 @@ +/* + * 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::ExecutionContext; +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, ctx: &mut ExecutionContext) -> Result<()> { + log::info!("fold {:?} is called with context {:?}", self, 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 ctx.data.get(iterable_name) { + Some(JValue::Array(_)) => {} + 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 ctx + .folds + .insert(iterator_name.clone(), fold_state) + .is_some() + { + return Err(AquamarineError::MultipleFoldStates(iterable_name.clone())); + } + + instr_head.execute(ctx)?; + ctx.folds.remove(iterator_name); + + Ok(()) + } +} + +impl super::ExecutableInstruction for Next { + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()> { + log::info!("next {:?} is called with context {:?}", self, ctx); + + let iterator_name = &self.0; + let fold_state = ctx + .folds + .get_mut(iterator_name) + .ok_or_else(|| AquamarineError::FoldStateNotFound(iterator_name.clone()))?; + let value = 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!(), + }; + + 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(ctx)?; + + // get the same fold state again because of borrow checker + match 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!([ + String::from("asd"), + lfold, + String::from("{\"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!([ + String::from("asd"), + rfold, + String::from("{\"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!([ + String::from("asd"), + script, + String::from("{\"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!([ + String::from("asd"), + script, + String::from("{\"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" + )) + ); + } +} diff --git a/stepper/src/air/mod.rs b/stepper/src/air/mod.rs new file mode 100644 index 00000000..14edfb62 --- /dev/null +++ b/stepper/src/air/mod.rs @@ -0,0 +1,82 @@ +/* + * 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. + */ + +mod call; +mod fold; +mod null; +mod par; +mod seq; + +use crate::AquaData; +use crate::Result; +use call::Call; +use fold::Fold; +use fold::FoldState; +use fold::Next; +use null::Null; +use par::Par; +use seq::Seq; + +use serde_derive::Deserialize; +use serde_derive::Serialize; +use std::collections::HashMap; + +#[derive(Clone, Default, Debug)] +pub(super) struct ExecutionContext { + pub data: AquaData, + pub next_peer_pks: Vec, + pub current_peer_id: String, + pub folds: HashMap, +} + +impl ExecutionContext { + pub(super) fn new(data: AquaData, current_peer_id: String) -> Self { + Self { + data, + next_peer_pks: vec![], + current_peer_id, + folds: HashMap::new(), + } + } +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub(crate) enum Instruction { + Null(Null), + Call(Call), + Fold(Fold), + Next(Next), + Par(Par), + Seq(Seq), +} + +pub(crate) trait ExecutableInstruction { + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()>; +} + +impl ExecutableInstruction for Instruction { + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()> { + match self { + Instruction::Null(null) => null.execute(ctx), + Instruction::Call(call) => call.execute(ctx), + Instruction::Fold(fold) => fold.execute(ctx), + Instruction::Next(next) => next.execute(ctx), + Instruction::Par(par) => par.execute(ctx), + Instruction::Seq(seq) => seq.execute(ctx), + } + } +} diff --git a/src/instructions/null.rs b/stepper/src/air/null.rs similarity index 80% rename from src/instructions/null.rs rename to stepper/src/air/null.rs index c26f48e8..a418efc6 100644 --- a/src/instructions/null.rs +++ b/stepper/src/air/null.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::AquaData; +use super::ExecutionContext; use crate::Result; use serde_derive::Deserialize; @@ -24,8 +24,8 @@ use serde_derive::Serialize; pub(crate) struct Null {} impl super::ExecutableInstruction for Null { - fn execute(self, data: &mut AquaData, next_peer_pks: &mut Vec) -> Result<()> { - log::info!("null called with data: {:?} and next_peer_pks: {:?}", data, next_peer_pks); + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()> { + log::info!("null is called with context: {:?}", ctx); Ok(()) } diff --git a/stepper/src/air/par.rs b/stepper/src/air/par.rs new file mode 100644 index 00000000..d65dd3c9 --- /dev/null +++ b/stepper/src/air/par.rs @@ -0,0 +1,73 @@ +/* + * 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::ExecutionContext; +use super::Instruction; +use crate::Result; + +use serde_derive::Deserialize; +use serde_derive::Serialize; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub(crate) struct Par(Box, Box); + +impl super::ExecutableInstruction for Par { + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()> { + log::info!("par is called with context: {:?}", ctx); + + self.0.execute(ctx)?; + self.1.execute(ctx)?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use aqua_test_utils::create_aqua_vm; + use aqua_test_utils::unit_call_service; + use aquamarine_vm::StepperOutcome; + + use serde_json::json; + + #[test] + fn par() { + let mut vm = create_aqua_vm(unit_call_service()); + + let script = String::from( + r#" + (par ( + (call (remote_peer_id_1 (local_service_id local_fn_name) () result_name)) + (call (remote_peer_id_2 (service_id fn_name) () g)) + ))"#, + ); + + let res = vm + .call(json!([String::from("asd"), script, String::from("{}"),])) + .expect("call should be successful"); + + assert_eq!( + res, + StepperOutcome { + data: String::from("{}"), + next_peer_pks: vec![ + String::from("remote_peer_id_1"), + String::from("remote_peer_id_2") + ] + } + ); + } +} diff --git a/stepper/src/air/seq.rs b/stepper/src/air/seq.rs new file mode 100644 index 00000000..fd695e7b --- /dev/null +++ b/stepper/src/air/seq.rs @@ -0,0 +1,74 @@ +/* + * 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::ExecutionContext; +use super::Instruction; +use crate::Result; + +use serde_derive::Deserialize; +use serde_derive::Serialize; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub(crate) struct Seq(Box, Box); + +impl super::ExecutableInstruction for Seq { + fn execute(&self, ctx: &mut ExecutionContext) -> Result<()> { + log::info!("seq is called with context: {:?}", ctx); + + let pks_count_before_call = ctx.next_peer_pks.len(); + self.0.execute(ctx)?; + + if pks_count_before_call == ctx.next_peer_pks.len() { + self.1.execute(ctx)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use aqua_test_utils::create_aqua_vm; + use aqua_test_utils::unit_call_service; + use aquamarine_vm::StepperOutcome; + + use serde_json::json; + + #[test] + fn par() { + let mut vm = create_aqua_vm(unit_call_service()); + + let script = String::from( + r#" + (seq ( + (call (remote_peer_id_1 (local_service_id local_fn_name) () result_name)) + (call (remote_peer_id_2 (service_id fn_name) () g)) + ))"#, + ); + + let res = vm + .call(json!([String::from("asd"), script, String::from("{}"),])) + .expect("call should be successful"); + + assert_eq!( + res, + StepperOutcome { + data: String::from("{}"), + next_peer_pks: vec![String::from("remote_peer_id_1")] + } + ); + } +} diff --git a/src/defines.rs b/stepper/src/defines.rs similarity index 75% rename from src/defines.rs rename to stepper/src/defines.rs index b02e2146..8de6e60b 100644 --- a/src/defines.rs +++ b/stepper/src/defines.rs @@ -14,20 +14,21 @@ * limitations under the License. */ -/// This file contains defines similar for both FCE and browser targets. - -pub(crate) type Result = std::result::Result; -pub(crate) type AquaData = std::collections::HashMap; -pub(crate) use crate::errors::AquamarineError; -pub(crate) use crate::stepper::StepperOutcome; - -pub(crate) const CALL_SERVICE_SUCCESS: i32 = 0; - 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) const CALL_SERVICE_SUCCESS: i32 = 0; + #[fluence::fce] -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct CallServiceResult { pub ret_code: i32, pub result: String, diff --git a/stepper/src/errors.rs b/stepper/src/errors.rs new file mode 100644 index 00000000..b1b1a9de --- /dev/null +++ b/stepper/src/errors.rs @@ -0,0 +1,182 @@ +/* + * 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 crate::CallServiceResult; +use crate::JValue; +use crate::StepperOutcome; + +use jsonpath_lib::JsonPathError; +use serde_json::Error as SerdeJsonError; +use serde_sexpr::Error as SExprError; + +use std::convert::Into; +use std::env::VarError; +use std::error::Error; + +#[derive(Debug)] +pub(crate) enum AquamarineError { + /// Errors occurred while parsing aqua script in the form of S expressions. + SExprParseError(SExprError), + + /// Errors occurred while parsing aqua data. + DataSerdeError(SerdeJsonError), + + /// Errors occurred while parsing function arguments of an expression. + FuncArgsSerdeError(JValue, SerdeJsonError), + + /// Errors occurred while parsing returned by call_service value. + CallServiceSerdeError(CallServiceResult, SerdeJsonError), + + /// Indicates that environment variable with name CURRENT_PEER_ID isn't set. + CurrentPeerIdEnvError(VarError, String), + + /// Semantic errors in instructions. + InstructionError(String), + + /// An error is occurred while calling local service via call_service. + LocalServiceError(String), + + /// Value for such name isn't presence in data. + VariableNotFound(String), + + /// Multiple values for such name found. + MultipleVariablesFound(String), + + /// Value with such path wasn't found in data with such error. + VariableNotInJsonPath(String, JsonPathError), + + /// Value for such name isn't presence in data. + IncompatibleJValueType(JValue, String), + + /// Multiple values found for such json path. + MultipleValuesInJsonPath(String), + + /// Fold state wasn't found for such iterator name. + FoldStateNotFound(String), + + /// Multiple fold states found for such iterator name. + MultipleFoldStates(String), +} + +impl Error for AquamarineError {} + +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::DataSerdeError(err) => write!( + f, + "an error occurred while serializing/deserializing data: {:?}", + err + ), + AquamarineError::FuncArgsSerdeError(args, err) => write!( + f, + "function arguments {} can't be serialized or deserialized with an error: {:?}", + args, err + ), + AquamarineError::CallServiceSerdeError(result, err) => write!( + f, + "call_service result \"{:?}\" can't be serialized or deserialized with an error: {:?}", + result, err + ), + AquamarineError::CurrentPeerIdEnvError(err, env_name) => write!( + f, + "the environment variable \"{}\" can't be obtained: {:?}", + env_name, + err + ), + AquamarineError::InstructionError(err_msg) => write!(f, "{}", err_msg), + AquamarineError::LocalServiceError(err_msg) => write!(f, "{}", err_msg), + AquamarineError::VariableNotFound(variable_name) => write!( + f, + "variable with name {} isn't present in data", + variable_name + ), + AquamarineError::MultipleVariablesFound(variable_name) => write!( + f, + "multiple variables found for name {} in data", + variable_name + ), + AquamarineError::VariableNotInJsonPath(json_path, json_path_err) => write!( + f, + "variable with path {} not found with error: {:?}", + json_path, json_path_err + ), + AquamarineError::IncompatibleJValueType(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 + ), + AquamarineError::FoldStateNotFound(iterator) => write!( + f, + "fold state not found for this iterable {}", + iterator + ), + AquamarineError::MultipleFoldStates(iterator) => write!( + f, + "multiple fold states found for iterable {}", + iterator + ), + } + } +} + +impl From for AquamarineError { + fn from(err: SExprError) -> Self { + AquamarineError::SExprParseError(err) + } +} + +impl From for AquamarineError { + fn from(_: std::convert::Infallible) -> Self { + unreachable!() + } +} + +impl Into for AquamarineError { + fn into(self) -> StepperOutcome { + let ret_code = match self { + AquamarineError::SExprParseError(_) => 1, + AquamarineError::DataSerdeError(..) => 2, + AquamarineError::FuncArgsSerdeError(..) => 3, + AquamarineError::CallServiceSerdeError(..) => 4, + AquamarineError::CurrentPeerIdEnvError(..) => 5, + AquamarineError::InstructionError(..) => 6, + AquamarineError::LocalServiceError(..) => 7, + AquamarineError::VariableNotFound(..) => 8, + AquamarineError::MultipleVariablesFound(..) => 9, + AquamarineError::VariableNotInJsonPath(..) => 10, + AquamarineError::IncompatibleJValueType(..) => 11, + AquamarineError::MultipleValuesInJsonPath(..) => 12, + AquamarineError::FoldStateNotFound(..) => 13, + AquamarineError::MultipleFoldStates(..) => 14, + }; + + StepperOutcome { + ret_code, + data: format!("{}", self), + next_peer_pks: vec![], + } + } +} diff --git a/stepper/src/execution.rs b/stepper/src/execution.rs new file mode 100644 index 00000000..839a2033 --- /dev/null +++ b/stepper/src/execution.rs @@ -0,0 +1,120 @@ +/* + * 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::StepperOutcome; +use crate::air::ExecutableInstruction; +use crate::air::ExecutionContext; +use crate::air::Instruction; +use crate::AquaData; +use crate::AquamarineError; +use crate::Result; + +const CURRENT_PEER_ID_ENV_NAME: &str = "CURRENT_PEER_ID"; + +pub(crate) fn execute_aqua(init_user_id: String, aqua: String, data: String) -> StepperOutcome { + log::info!( + "stepper invoked with user_id = {}, aqua = {:?}, data = {:?}", + init_user_id, + aqua, + data + ); + + execute_aqua_impl(init_user_id, aqua, data).unwrap_or_else(Into::into) +} + +fn execute_aqua_impl(_init_user_id: String, aqua: String, data: String) -> Result { + let parsed_data: AquaData = + serde_json::from_str(&data).map_err(AquamarineError::DataSerdeError)?; + let formatted_aqua = format_aqua(aqua); + let parsed_aqua = serde_sexpr::from_str::(&formatted_aqua)?; + + log::info!( + "\nparsed_aqua: {:?}\nparsed_data: {:?}", + parsed_aqua, + parsed_data + ); + + let current_peer_id = std::env::var(CURRENT_PEER_ID_ENV_NAME).map_err(|e| { + AquamarineError::CurrentPeerIdEnvError(e, String::from(CURRENT_PEER_ID_ENV_NAME)) + })?; + + let mut execution_ctx = ExecutionContext::new(parsed_data, current_peer_id); + parsed_aqua.execute(&mut execution_ctx)?; + + let data = + serde_json::to_string(&execution_ctx.data).map_err(AquamarineError::DataSerdeError)?; + + Ok(StepperOutcome { + ret_code: 0, + data, + next_peer_pks: execution_ctx.next_peer_pks, + }) +} + +/// Formats aqua script in a form of S-expressions to a form compatible with the serde_sexpr crate. +fn format_aqua(aqua: String) -> String { + use std::iter::FromIterator; + + let mut formatted_aqua = Vec::with_capacity(aqua.len()); + // whether to skip the next whitespace + let mut skip_next_whitespace = false; + // whether c was a closing brace + let mut was_cbr = false; + + for c in aqua.chars() { + let is_whitespace = c == ' '; + if (skip_next_whitespace && is_whitespace) || c == '\n' { + continue; + } + + let is_cbr = c == ')'; + + skip_next_whitespace = is_whitespace || c == '(' || is_cbr; + if was_cbr && !is_cbr { + formatted_aqua.push(' '); + } + + was_cbr = is_cbr; + formatted_aqua.push(c) + } + + String::from_iter(formatted_aqua.into_iter()) +} + +#[cfg(test)] +mod tests { + #[test] + fn format_aqua_test() { + let aqua = format!( + r#"(( (( (seq ( + (call (%current_peer_id% (add_module ||) (module) module)) + (seq ( + (call (%current_peer_id% (add_blueprint ||) (blueprint) blueprint_id)) + (seq ( + (call (%current_peer_id% (create ||) (blueprint_id) service_id)) + (call ({} (|| ||) (service_id) client_result)) + ) ) + ) ) + ))"#, + "abc" + ); + + let aqua = super::format_aqua(aqua); + let formatted_aqua = String::from("(((((seq ((call (%current_peer_id% (add_module ||) (module) module)) (seq ((call (%current_peer_id% (add_blueprint ||) (blueprint) blueprint_id)) (seq ((call (%current_peer_id% (create ||) (blueprint_id) service_id)) (call (abc (|| ||) (service_id) client_result))))))))"); + + assert_eq!(aqua, formatted_aqua); + } +} diff --git a/src/fce.rs b/stepper/src/fce.rs similarity index 78% rename from src/fce.rs rename to stepper/src/fce.rs index eb251793..a7975fa4 100644 --- a/src/fce.rs +++ b/stepper/src/fce.rs @@ -14,15 +14,26 @@ * limitations under the License. */ +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + mod air; mod defines; mod errors; -mod instructions; -mod stepper; +mod execution; +mod stepper_outcome; pub(crate) use crate::defines::*; -use crate::stepper::execute_aqua; +use crate::execution::execute_aqua; use fluence::fce; pub fn main() { @@ -35,7 +46,7 @@ pub fn invoke(init_user_id: String, aqua: String, data: String) -> StepperOutcom } #[fce] -#[link(wasm_import_module = "aqua_test_module")] +#[link(wasm_import_module = "host")] extern "C" { pub fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult; } diff --git a/src/stepper/stepper_outcome.rs b/stepper/src/stepper_outcome.rs similarity index 93% rename from src/stepper/stepper_outcome.rs rename to stepper/src/stepper_outcome.rs index 4efd5199..87fe3004 100644 --- a/src/stepper/stepper_outcome.rs +++ b/stepper/src/stepper_outcome.rs @@ -17,10 +17,8 @@ use fluence::fce; use serde::{Deserialize, Serialize}; -pub const SUCCESS_ERROR_CODE: i32 = 0; - #[fce] -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct StepperOutcome { /// A return code, where SUCCESS_ERROR_CODE means success. pub ret_code: i32, diff --git a/src/wasm_bindgen.rs b/stepper/src/wasm_bindgen.rs similarity index 95% rename from src/wasm_bindgen.rs rename to stepper/src/wasm_bindgen.rs index 86588683..ecad6444 100644 --- a/src/wasm_bindgen.rs +++ b/stepper/src/wasm_bindgen.rs @@ -17,12 +17,12 @@ mod air; mod defines; mod errors; -mod instructions; -mod stepper; +mod execution; +mod stepper_outcome; pub(crate) use crate::defines::*; -use crate::stepper::execute_aqua; +use crate::execution::execute_aqua; use wasm_bindgen::prelude::*; diff --git a/stepper/tests/basic.rs b/stepper/tests/basic.rs new file mode 100644 index 00000000..c37a014e --- /dev/null +++ b/stepper/tests/basic.rs @@ -0,0 +1,162 @@ +/* + * 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 aquamarine_vm::StepperOutcome; + +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!([String::from("asd"), script, String::from("{}"),])) + .expect("should be successful"); + let right_outcome = StepperOutcome { + data: String::from("{\"result_1\":\"test\"}"), + next_peer_pks: vec![String::from("remote_peer_id")], + }; + + assert_eq!(res, right_outcome); +} + +#[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!([String::from("asd"), script, String::from("{}"),])) + .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"} ); + + 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!([String::from("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")), + ); + + assert_eq!(resulted_data, data_value); + assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); +} diff --git a/test_module/main.rs b/test_module/main.rs deleted file mode 100644 index defb4537..00000000 --- a/test_module/main.rs +++ /dev/null @@ -1,22 +0,0 @@ -use fluence::fce; - -fn main() {} - -#[fce] -pub struct CallServiceResult { - pub ret_code: i32, - pub result: String, -} - -#[fce] -pub fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult { - println!( - "call service invoked with:\n service_id: {}\n fn_name: {}\n args: {:?}", - service_id, fn_name, args - ); - - CallServiceResult { - ret_code: 0, - result: String::from("[\"result string\"]"), - } -}