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