New data format (#12)

This commit is contained in:
vms 2020-10-30 20:29:05 +03:00 committed by GitHub
parent a9ad93f64d
commit fbca76444b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 2521 additions and 2222 deletions

View File

@ -22,12 +22,12 @@ jobs:
cargo install fcli
cd stepper
fce build
fce build --features fce
cd ..
cargo fmt --all -- --check --color always
cargo build --release --all-features
cargo test --release --all-features
cargo check
cargo test --release
cargo clippy -v
- save_cache:

12
CHANGELOG.md Normal file
View 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
View File

@ -42,31 +42,22 @@ dependencies = [
[[package]]
name = "aquamarine"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"aqua-test-utils",
"aquamarine-vm",
"boolinator",
"env_logger",
"fluence",
"jsonpath_lib",
"log",
"maplit",
"once_cell",
"pretty_assertions",
"serde",
"serde_derive",
"serde_json",
"serde_sexpr",
"stepper-lib",
"wasm-bindgen",
]
[[package]]
name = "aquamarine-vm"
version = "0.1.0"
source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed"
version = "0.1.1"
source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457"
dependencies = [
"fluence-faas",
"maplit",
"serde",
"serde_json",
]
@ -85,9 +76,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "atty"
@ -131,7 +122,7 @@ dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"cfg-if 0.1.10",
"constant_time_eq",
"crypto-mac",
"digest 0.9.0",
@ -167,6 +158,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -182,6 +179,12 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "475bd7aa7680b4ed8f6bb59745e882bcbaeb39326532bb79ffb1716480d9a274"
[[package]]
name = "const_fn"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@ -250,48 +253,48 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.4.4"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 1.0.0",
"const_fn",
"lazy_static",
]
@ -369,9 +372,9 @@ dependencies = [
[[package]]
name = "errno"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01"
checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe"
dependencies = [
"errno-dragonfly",
"libc",
@ -390,8 +393,8 @@ dependencies = [
[[package]]
name = "fce"
version = "0.1.8"
source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed"
version = "0.1.9"
source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457"
dependencies = [
"boolinator",
"fce-wit-interfaces",
@ -402,6 +405,7 @@ dependencies = [
"parity-wasm",
"pwasm-utils",
"safe-transmute",
"serde",
"wasmer-interface-types-fl",
"wasmer-runtime-core-fl",
"wasmer-runtime-fl",
@ -410,8 +414,8 @@ dependencies = [
[[package]]
name = "fce-wit-interfaces"
version = "0.1.5"
source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed"
version = "0.1.6"
source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457"
dependencies = [
"multimap",
"wasmer-interface-types-fl",
@ -419,8 +423,8 @@ dependencies = [
[[package]]
name = "fce-wit-parser"
version = "0.1.7"
source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed"
version = "0.1.8"
source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457"
dependencies = [
"anyhow",
"fce-wit-interfaces",
@ -432,7 +436,7 @@ dependencies = [
[[package]]
name = "fluence"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [
"fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
"fluence-sdk-main 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
@ -440,8 +444,8 @@ dependencies = [
[[package]]
name = "fluence-faas"
version = "0.1.9"
source = "git+https://github.com/fluencelabs/fce#05473672a15d368802cc525c6290eee11b7907ed"
version = "0.1.10"
source = "git+https://github.com/fluencelabs/fce#d52eb4432faa924adf51698567018a4326440457"
dependencies = [
"cmd_lib",
"fce",
@ -459,6 +463,14 @@ dependencies = [
"wasmer-wasi-fl",
]
[[package]]
name = "fluence-sdk-macro"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [
"fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
]
[[package]]
name = "fluence-sdk-macro"
version = "0.2.8"
@ -469,11 +481,13 @@ dependencies = [
]
[[package]]
name = "fluence-sdk-macro"
name = "fluence-sdk-main"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [
"fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
"fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
"log",
"serde",
]
[[package]]
@ -487,21 +501,10 @@ dependencies = [
"serde",
]
[[package]]
name = "fluence-sdk-main"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd"
dependencies = [
"fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
"log",
"serde",
]
[[package]]
name = "fluence-sdk-wit"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [
"proc-macro2",
"quote",
@ -514,7 +517,8 @@ dependencies = [
[[package]]
name = "fluence-sdk-wit"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#4d6c4f6b862c22ebd8db7244daac0adf3f1bd2fd"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17"
dependencies = [
"proc-macro2",
"quote",
@ -536,7 +540,7 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"serde",
]
@ -565,7 +569,7 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@ -717,16 +721,16 @@ checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"cfg-if 0.1.10",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "lock_api"
@ -743,7 +747,7 @@ version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
@ -752,17 +756,11 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.3"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "memmap"
@ -806,7 +804,7 @@ checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"cfg-if 0.1.10",
"libc",
"void",
]
@ -842,12 +840,6 @@ dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
[[package]]
name = "output_vt100"
version = "0.1.2"
@ -889,7 +881,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi",
"libc",
"redox_syscall",
@ -1004,9 +996,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032"
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
dependencies = [
"autocfg",
"crossbeam-deque",
@ -1016,9 +1008,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.8.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@ -1166,6 +1158,26 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stepper-lib"
version = "0.1.2"
dependencies = [
"aqua-test-utils",
"aquamarine-vm",
"boolinator",
"env_logger",
"fluence",
"jsonpath_lib",
"log",
"maplit",
"pretty_assertions",
"serde",
"serde_derive",
"serde_json",
"serde_sexpr",
"wasm-bindgen",
]
[[package]]
name = "subtle"
version = "2.3.0"
@ -1174,9 +1186,9 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
[[package]]
name = "syn"
version = "1.0.44"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
dependencies = [
"proc-macro2",
"quote",
@ -1360,7 +1372,7 @@ version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"wasm-bindgen-macro",
]
@ -1462,9 +1474,9 @@ dependencies = [
[[package]]
name = "wasmer-interface-types-fl"
version = "0.17.9"
version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "475cafb8bf9763895f6fde0d7417b9090145fc461ad7aef21b1a866b8357c091"
checksum = "1de496e366bd1c198942248fc1de4b94e4647b263dd60099d5f7776f0d621656"
dependencies = [
"log",
"nom 5.1.2",

View File

@ -3,6 +3,7 @@ members = [
"crates/test-module",
"crates/test-utils",
"stepper",
"stepper-lib",
]
[profile.release]

View File

@ -33,8 +33,11 @@ use aquamarine_vm::HostImportDescriptor;
use aquamarine_vm::IType;
use aquamarine_vm::IValue;
use std::collections::HashMap;
use std::path::PathBuf;
type JValue = serde_json::Value;
pub fn create_aqua_vm(
call_service: HostExportedFunc,
current_peer_id: impl Into<String>,
@ -46,10 +49,14 @@ pub fn create_aqua_vm(
error_handler: None,
};
let tmp_dir = std::env::temp_dir();
let config = AquamarineVMConfig {
aquamarine_wasm_path: PathBuf::from("../target/wasm32-wasi/debug/aquamarine.wasm"),
call_service: call_service_descriptor,
current_peer_id: current_peer_id.into(),
particle_data_store: tmp_dir,
logging_mask: i64::max_value(),
};
AquamarineVM::new(config).expect("vm should be created")
@ -100,3 +107,47 @@ pub fn echo_number_call_service() -> HostExportedFunc {
))
})
}
pub fn set_variable_call_service(json: impl Into<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");
};
}

View File

@ -0,0 +1 @@
max_width = 120

37
stepper-lib/Cargo.toml Normal file
View 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 = []

View File

@ -21,6 +21,7 @@ use parsed_call::ParsedCall;
use super::CallEvidenceCtx;
use super::ExecutionCtx;
use crate::log_instruction;
use crate::AquamarineError::VariableNotFound;
use crate::AquamarineError::VariableNotInJsonPath;
use crate::Result;
@ -60,7 +61,7 @@ pub(crate) struct Call(PeerPart, FunctionPart, Vec<String>, String);
impl super::ExecutableInstruction for Call {
fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
log::info!("call {:?} is called with contexts: {:?} {:?}", self, exec_ctx, call_ctx);
log_instruction!(call, exec_ctx, call_ctx);
let parsed_call = match ParsedCall::new(self, exec_ctx) {
Ok(parsed_call) => parsed_call,
@ -89,81 +90,107 @@ impl super::ExecutableInstruction for Call {
#[cfg(test)]
mod tests {
use crate::call_evidence::CallEvidencePath;
use crate::JValue;
use aqua_test_utils::call_vm;
use aqua_test_utils::create_aqua_vm;
use aqua_test_utils::echo_string_call_service;
use aqua_test_utils::set_variable_call_service;
use aqua_test_utils::unit_call_service;
use aquamarine_vm::vec1::Vec1;
use aquamarine_vm::HostExportedFunc;
use aquamarine_vm::IValue;
use serde_json::json;
use std::rc::Rc;
#[test]
fn current_peer_id_call() {
let mut vm = create_aqua_vm(echo_string_call_service(), "test_peer_id");
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let mut vm = create_aqua_vm(unit_call_service(), "test_peer_id");
let script = String::from(
r#"
(call (%current_peer_id% ("local_service_id" "local_fn_name") (value) result_name))
(call (%current_peer_id% ("local_service_id" "local_fn_name") () result_name))
"#,
);
let res = vm
.call(json!(["asd", script, "{}", "{\"value\": \"test\"}",]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", "[]");
let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json");
let res: JValue = serde_json::from_str(&res.data).unwrap();
assert_eq!(res.get("result_name").unwrap(), &json!("test"));
assert_eq!(call_path.len(), 1);
assert_eq!(
call_path[0],
Call(Executed(Rc::new(JValue::String(String::from("test")))))
);
assert!(res.next_peer_pks.is_empty());
let script = String::from(
r#"
(call ("test_peer_id" ("local_service_id" "local_fn_name") (value) result_name))
(call ("test_peer_id" ("local_service_id" "local_fn_name") () result_name))
"#,
);
let res = vm
.call(json!(["asd", script, "{}", "{\"value\": \"test\"}",]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", "[]");
let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json");
let res: JValue = serde_json::from_str(&res.data).unwrap();
assert_eq!(res.get("result_name").unwrap(), &json!("test"));
assert_eq!(call_path.len(), 1);
assert_eq!(
call_path[0],
Call(Executed(Rc::new(JValue::String(String::from("test")))))
);
assert!(res.next_peer_pks.is_empty());
}
#[test]
fn remote_peer_id_call() {
let mut vm = create_aqua_vm(echo_string_call_service(), "");
let remote_peer_id = String::from("some_remote_peer_id");
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let some_local_peer_id = String::from("some_local_peer_id");
let mut vm = create_aqua_vm(echo_string_call_service(), some_local_peer_id.clone());
let remote_peer_id = String::from("some_remote_peer_id");
let script = format!(
r#"(call ("{}" ("local_service_id" "local_fn_name") (value) result_name))"#,
remote_peer_id
);
let res = vm
.call(json!(["asd", script, "{}", "{\"value\": \"test\"}",]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", "[]");
let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json");
assert_eq!(call_path.len(), 1);
assert_eq!(call_path[0], Call(RequestSent(some_local_peer_id)));
assert_eq!(res.next_peer_pks, vec![remote_peer_id]);
}
#[test]
fn variables() {
let mut vm = create_aqua_vm(echo_string_call_service(), "");
let mut vm = create_aqua_vm(unit_call_service(), "remote_peer_id");
let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#""remote_peer_id""#), "set_variable");
let script = format!(r#"(call (remote_peer_id ("some_service_id" "local_fn_name") ("param") result_name))"#,);
let script = format!(
r#"
(seq (
(call ("set_variable" ("some_service_id" "local_fn_name") () remote_peer_id))
(call (remote_peer_id ("some_service_id" "local_fn_name") () result_name))
))
"#,
);
let res = vm
.call(json!(["asd", script, "{}", "{\"remote_peer_id\": \"some_peer_id\"}",]))
.expect("call should be successful");
let res = call_vm!(set_variable_vm, "asd", script.clone(), "[]", "[]");
let res = call_vm!(vm, "asd", script, "[]", res.data);
assert_eq!(res.next_peer_pks, vec![String::from("some_peer_id")]);
assert!(res.next_peer_pks.is_empty());
}
#[test]
fn string_parameters() {
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let call_service: HostExportedFunc = Box::new(|_, args| -> Option<IValue> {
let arg = match &args[2] {
IValue::String(str) => str,
@ -175,18 +202,30 @@ mod tests {
))
});
let mut vm = create_aqua_vm(call_service, "");
let mut vm = create_aqua_vm(call_service, "A");
let mut set_variable_vm = create_aqua_vm(set_variable_call_service(r#""arg3_value""#), "set_variable");
let script = format!(
r#"(call (%current_peer_id% ("some_service_id" "local_fn_name") ("arg1" "arg2" arg3) result_name))"#,
let script = String::from(
r#"
(seq (
(call ("set_variable" ("some_service_id" "local_fn_name") () arg3))
(call ("A" ("some_service_id" "local_fn_name") ("arg1" "arg2" arg3) result))
))
"#,
);
let res = vm
.call(json!(["asd", script, "{}", json!({"arg3": "arg3_value"}).to_string(),]))
.expect("call should be successful");
let res = call_vm!(set_variable_vm, "asd", script.clone(), "[]", "[]");
let res = call_vm!(vm, "asd", script, "[]", res.data);
let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be a valid json");
let jdata: JValue = serde_json::from_str(&res.data).expect("should be valid json");
assert_eq!(jdata["result_name"], json!(["arg1", "arg2", "arg3_value"]));
assert_eq!(call_path.len(), 2);
assert_eq!(
call_path[1],
Call(Executed(Rc::new(JValue::Array(vec![
JValue::String(String::from("arg1")),
JValue::String(String::from("arg2")),
JValue::String(String::from("arg3_value")),
]))))
);
}
}

View 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"))),
}
}

View 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('"')
}

View File

@ -14,16 +14,17 @@
* limitations under the License.
*/
use super::fold::FoldState;
use crate::AquaData;
use crate::AValue;
use std::collections::HashMap;
use std::fmt::Display;
use std::fmt::Formatter;
/// Execution context contains all necessary information needed to execute aqua script.
#[derive(Clone, Default, Debug)]
pub(crate) struct ExecutionCtx {
/// Contains all set variables.
pub data: AquaData,
pub data_cache: HashMap<String, AValue>,
/// Set of peer public keys that should receive resulted data.
pub next_peer_pks: Vec<String>,
@ -31,25 +32,36 @@ pub(crate) struct ExecutionCtx {
/// PeerId of a peer executing this aqua script.
pub current_peer_id: String,
/// Describes all met folds on the current execution step.
pub folds: HashMap<String, FoldState>,
/// Indicates that previous executed subtree is complete.
/// A subtree treats as a complete if all subtree elements satisfy the following rules:
/// - at least one of par subtrees is complete
/// - non-thrown subtree of xor is complete
/// - all of seq subtrees are complete
/// - call executes successfully (call evidence equals to Executed)
pub subtree_complete: bool,
}
impl ExecutionCtx {
pub(crate) fn new(data: AquaData, current_peer_id: String) -> Self {
pub(crate) fn new(current_peer_id: String) -> Self {
Self {
data,
data_cache: HashMap::new(),
next_peer_pks: vec![],
current_peer_id,
folds: HashMap::new(),
subtree_complete: true,
}
}
}
impl Display for ExecutionCtx {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "data cache:")?;
for (key, value) in self.data_cache.iter() {
writeln!(f, " {} => {}", key, value)?;
}
writeln!(f, "current peer id: {}", self.current_peer_id)?;
writeln!(f, "subtree complete: {}", self.subtree_complete)?;
writeln!(f, "next peer public keys: {:?}", self.next_peer_pks)?;
Ok(())
}
}

346
stepper-lib/src/air/fold.rs Normal file
View 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!([])))));
}
}

View File

@ -23,6 +23,7 @@ mod seq;
mod xor;
pub(crate) use execution_context::ExecutionCtx;
pub(crate) use fold::FoldState;
pub(self) use crate::call_evidence::CallEvidenceCtx;
pub(self) use crate::call_evidence::EvidenceState;
@ -39,15 +40,6 @@ use xor::Xor;
use serde_derive::Deserialize;
use serde_derive::Serialize;
use once_cell::sync::Lazy;
use std::collections::HashSet;
pub(self) static RESERVED_KEYWORDS: Lazy<HashSet<&str>> = Lazy::new(|| {
let mut set = HashSet::new();
set.insert("__call");
set
});
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum Instruction {
@ -77,3 +69,46 @@ impl ExecutableInstruction for Instruction {
}
}
}
#[macro_export]
macro_rules! log_instruction {
($instr_name:expr, $exec_ctx:expr, $call_ctx:expr) => {
log::info!(target: crate::log_targets::INSTRUCTION, "> {}", stringify!($instr_name));
let mut data_cache_log = String::from(" data cache:");
if $exec_ctx.data_cache.is_empty() {
data_cache_log.push_str(" empty");
}
for (key, value) in $exec_ctx.data_cache.iter() {
data_cache_log.push_str(&format!("\n {} => {}", key, value));
}
log::info!(target: crate::log_targets::DATA_CACHE, "{}", data_cache_log);
log::info!(
target: crate::log_targets::NEXT_PEER_PKS,
" next peers pk: {:?}",
$exec_ctx.next_peer_pks
);
log::info!(
target: crate::log_targets::SUBTREE_COMPLETE,
" subtree complete: {}",
$exec_ctx.subtree_complete
);
log::info!(
target: crate::log_targets::CALL_EVIDENCE_PATH,
" current call evidence path: {:?}",
$call_ctx.current_path
);
log::info!(
target: crate::log_targets::SUBTREE_ELEMENTS,
" subtree elements count: {:?}",
$call_ctx.current_subtree_elements_count
);
log::info!(
target: crate::log_targets::NEW_CALL_EVIDENCE_PATH,
" new call evidence path: {:?}",
$call_ctx.new_path
);
};
}

View File

@ -16,6 +16,7 @@
use super::CallEvidenceCtx;
use super::ExecutionCtx;
use crate::log_instruction;
use crate::Result;
use serde_derive::Deserialize;
@ -26,7 +27,7 @@ pub(crate) struct Null {}
impl super::ExecutableInstruction for Null {
fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
log::info!("null is called with contexts: {:?} {:?}", exec_ctx, call_ctx);
log_instruction!(null, exec_ctx, call_ctx);
Ok(())
}

View File

@ -19,6 +19,8 @@ use super::EvidenceState;
use super::ExecutableInstruction;
use super::ExecutionCtx;
use super::Instruction;
use crate::log_instruction;
use crate::log_targets::EVIDENCE_CHANGING;
use crate::Result;
use serde_derive::Deserialize;
@ -29,7 +31,7 @@ pub(crate) struct Par(Box<Instruction>, Box<Instruction>);
impl ExecutableInstruction for Par {
fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
log::info!("par is called with context: {:?} {:?}", exec_ctx, call_ctx);
log_instruction!(par, exec_ctx, call_ctx);
let (left_subtree_size, right_subtree_size) = extract_subtree_sizes(call_ctx)?;
@ -39,11 +41,9 @@ impl ExecutableInstruction for Par {
let pre_new_states_count = call_ctx.new_path.len();
call_ctx.new_path.push_back(EvidenceState::Par(0, 0));
exec_ctx.subtree_complete = determine_subtree_complete(&self.0);
let new_left_subtree_size = execute_subtree(&self.0, left_subtree_size, exec_ctx, call_ctx)?;
let left_subtree_complete = exec_ctx.subtree_complete;
exec_ctx.subtree_complete = determine_subtree_complete(&self.1);
let new_right_subtree_size = execute_subtree(&self.1, right_subtree_size, exec_ctx, call_ctx)?;
let right_subtree_complete = exec_ctx.subtree_complete;
@ -51,7 +51,11 @@ impl ExecutableInstruction for Par {
exec_ctx.subtree_complete = left_subtree_complete || right_subtree_complete;
let new_par_evidence_state = EvidenceState::Par(new_left_subtree_size, new_right_subtree_size);
log::info!("call evidence: adding new state {:?}", new_par_evidence_state);
log::info!(
target: EVIDENCE_CHANGING,
" adding new call evidence state {:?}",
new_par_evidence_state
);
call_ctx.new_path[pre_new_states_count] = new_par_evidence_state;
let post_states_count = call_ctx.current_path.len();
@ -71,7 +75,8 @@ fn extract_subtree_sizes(call_ctx: &mut CallEvidenceCtx) -> Result<(usize, usize
call_ctx.current_subtree_elements_count -= 1;
log::info!(
"call evidence: the previous state was found {:?}",
target: EVIDENCE_CHANGING,
" previous call evidence state was found {:?}",
call_ctx.current_path[0]
);
@ -91,6 +96,8 @@ fn execute_subtree(
call_ctx.current_subtree_elements_count = subtree_size;
let before_states_count = call_ctx.new_path.len();
exec_ctx.subtree_complete = determine_subtree_complete(&subtree);
// execute subtree
subtree.execute(exec_ctx, call_ctx)?;
@ -106,20 +113,15 @@ fn determine_subtree_complete(next_instruction: &Instruction) -> bool {
// ))
// ))
// par will be executed after the last next that wouldn't change subtree_complete
if let Instruction::Next(_) = next_instruction {
false
} else {
true
}
!matches!(next_instruction, Instruction::Next(_))
}
#[cfg(test)]
mod tests {
use aqua_test_utils::call_vm;
use aqua_test_utils::create_aqua_vm;
use aqua_test_utils::unit_call_service;
use serde_json::json;
#[test]
fn par_remote_remote() {
use std::collections::HashSet;
@ -134,14 +136,11 @@ mod tests {
))"#,
);
let mut res = vm
.call(json!(["asd", script, "{}", "{}",]))
.expect("call should be successful");
let mut res = call_vm!(vm, "", script, "[]", "[]");
let peers_result: HashSet<_> = res.next_peer_pks.drain(..).collect();
let peers_right: HashSet<_> = vec![String::from("remote_peer_id_1"), String::from("remote_peer_id_2")]
.drain(..)
.collect();
let peers_right: HashSet<_> =
maplit::hashset!(String::from("remote_peer_id_1"), String::from("remote_peer_id_2"));
assert_eq!(peers_result, peers_right);
}
@ -158,9 +157,7 @@ mod tests {
))"#,
);
let res = vm
.call(json!(["asd", script, "{}", "{}",]))
.expect("call should be successful");
let res = call_vm!(vm, "", script, "[]", "[]");
assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]);
}

View File

@ -17,6 +17,7 @@
use super::CallEvidenceCtx;
use super::ExecutionCtx;
use super::Instruction;
use crate::log_instruction;
use crate::Result;
use serde_derive::Deserialize;
@ -27,7 +28,7 @@ pub(crate) struct Seq(Box<Instruction>, Box<Instruction>);
impl super::ExecutableInstruction for Seq {
fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
log::info!("seq is called with contexts: {:?} {:?}", exec_ctx, call_ctx);
log_instruction!(seq, exec_ctx, call_ctx);
exec_ctx.subtree_complete = true;
self.0.execute(exec_ctx, call_ctx)?;
@ -42,6 +43,7 @@ impl super::ExecutableInstruction for Seq {
#[cfg(test)]
mod tests {
use aqua_test_utils::call_vm;
use aqua_test_utils::create_aqua_vm;
use aqua_test_utils::unit_call_service;
@ -59,25 +61,10 @@ mod tests {
))"#,
);
let res = vm
.call(json!(["asd", script, "{}", "{}",]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script.clone(), "[]", "[]");
assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_1")]);
let res = vm
.call(json!([
"asd",
script,
"{}",
json!({
"__call": [{"call": "executed"}]
}
)
.to_string(),
]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", json!([{"call": {"executed": ""}}]).to_string());
assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]);
}
@ -93,10 +80,7 @@ mod tests {
))"#,
);
let res = vm
.call(json!(["asd", script, "{}", "{}",]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", "[]");
assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]);
}
}

View File

@ -17,6 +17,7 @@
use super::CallEvidenceCtx;
use super::ExecutionCtx;
use super::Instruction;
use crate::log_instruction;
use crate::AquamarineError::LocalServiceError;
use crate::Result;
@ -28,7 +29,7 @@ pub(crate) struct Xor(Box<Instruction>, Box<Instruction>);
impl super::ExecutableInstruction for Xor {
fn execute(&self, exec_ctx: &mut ExecutionCtx, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
log::info!("xor is called with contexts: {:?} {:?}", exec_ctx, call_ctx);
log_instruction!(xor, exec_ctx, call_ctx);
exec_ctx.subtree_complete = true;
match self.0.execute(exec_ctx, call_ctx) {
@ -43,17 +44,22 @@ impl super::ExecutableInstruction for Xor {
#[cfg(test)]
mod tests {
use crate::call_evidence::CallEvidencePath;
use crate::JValue;
use aqua_test_utils::call_vm;
use aqua_test_utils::create_aqua_vm;
use aquamarine_vm::vec1::Vec1;
use aquamarine_vm::HostExportedFunc;
use aquamarine_vm::IValue;
use serde_json::json;
use std::rc::Rc;
#[test]
fn xor() {
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let call_service: HostExportedFunc = Box::new(|_, args| -> Option<IValue> {
let builtin_service = match &args[0] {
IValue::String(str) => str,
@ -63,12 +69,12 @@ mod tests {
if builtin_service == "service_id_1" {
// return a error for service with id service_id_1
Some(IValue::Record(
Vec1::new(vec![IValue::S32(1), IValue::String(String::from("{}"))]).unwrap(),
Vec1::new(vec![IValue::S32(1), IValue::String(String::from(r#""error""#))]).unwrap(),
))
} else {
// return success for services with other ids
Some(IValue::Record(
Vec1::new(vec![IValue::S32(0), IValue::String(String::from("\"res\""))]).unwrap(),
Vec1::new(vec![IValue::S32(0), IValue::String(String::from(r#""res""#))]).unwrap(),
))
}
});
@ -83,13 +89,15 @@ mod tests {
))"#,
);
let res = vm
.call(json!(["asd", script, "{}", json!({"arg3": "arg3_value"}).to_string(),]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", "[]");
let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid json");
let jdata: JValue = serde_json::from_str(&res.data).expect("should be valid json");
assert_eq!(jdata["result_2"], json!("res"));
assert_eq!(call_path.len(), 2);
assert_eq!(call_path[0], Call(CallServiceFailed(String::from(r#""error""#))));
assert_eq!(
call_path[1],
Call(Executed(Rc::new(JValue::String(String::from("res")))))
);
let script = String::from(
r#"
@ -99,12 +107,13 @@ mod tests {
))"#,
);
let res = vm
.call(json!(["asd", script, "{}", json!({"arg3": "arg3_value"}).to_string(),]))
.expect("call should be successful");
let res = call_vm!(vm, "asd", script, "[]", "[]");
let call_path: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid json");
let jdata: JValue = serde_json::from_str(&res.data).expect("should be valid json");
assert_eq!(jdata["result_1"], json!("res"));
assert_eq!(call_path.len(), 1);
assert_eq!(
call_path[0],
Call(Executed(Rc::new(JValue::String(String::from("res")))))
);
}
}

View 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;
}

View File

@ -14,20 +14,15 @@
* limitations under the License.
*/
#[cfg(feature = "fce")]
mod fce_target;
#[cfg(not(feature = "fce"))]
mod wasm_bindgen_target;
use serde_derive::Deserialize;
use serde_derive::Serialize;
/// This file contains defines of some things similar both for FCE and browser targets.
pub(crate) type Result<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;
pub const CALL_SERVICE_SUCCESS: i32 = 0;
#[fluence::fce]
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -35,3 +30,13 @@ pub struct CallServiceResult {
pub ret_code: i32,
pub result: String,
}
#[cfg(feature = "fce")]
pub(crate) use fce_target::call_service;
#[cfg(feature = "fce")]
pub(crate) use fce_target::get_current_peer_id;
#[cfg(not(feature = "fce"))]
pub(crate) use wasm_bindgen_target::call_service;
#[cfg(not(feature = "fce"))]
pub(crate) use wasm_bindgen_target::get_current_peer_id;

View 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;
}

View File

@ -18,6 +18,8 @@ use super::CallEvidencePath;
use serde::Deserialize;
use serde::Serialize;
use std::fmt::Display;
use std::fmt::Formatter;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub(crate) struct CallEvidenceCtx {
@ -37,3 +39,15 @@ impl CallEvidenceCtx {
}
}
}
impl Display for CallEvidenceCtx {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "current path:\n{:?}", self.current_path)?;
writeln!(
f,
"current subtree elements count:\n{:?}",
self.current_subtree_elements_count
)?;
writeln!(f, "new path:\n{:?}", self.new_path)
}
}

View File

@ -19,6 +19,6 @@ mod state;
pub(crate) use context::CallEvidenceCtx;
pub(crate) use state::merge_call_paths;
pub(crate) use state::CallEvidencePath;
pub(crate) use state::CallResult;
pub(crate) use state::EvidenceState;
pub use state::CallEvidencePath;
pub use state::CallResult;
pub use state::EvidenceState;

View File

@ -14,22 +14,25 @@
* limitations under the License.
*/
use crate::log_targets::EVIDENCE_PATH_MERGE;
use crate::JValue;
use crate::Result;
use serde::Deserialize;
use serde::Serialize;
use std::cmp::max;
use std::rc::Rc;
pub(crate) type CallEvidencePath = std::collections::VecDeque<EvidenceState>;
pub type CallEvidencePath = std::collections::VecDeque<EvidenceState>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum CallResult {
/// Request was sent to a target node and it shouldn't be called again.
RequestSent,
pub enum CallResult {
/// Request was sent to a target node by node with such public key and it shouldn't be called again.
RequestSent(String),
/// A corresponding call's been already executed.
Executed,
/// A corresponding call's been already executed with such value and result.
Executed(Rc<JValue>),
/// call_service ended with a service error.
CallServiceFailed(String),
@ -37,7 +40,7 @@ pub(crate) enum CallResult {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum EvidenceState {
pub enum EvidenceState {
Par(usize, usize),
Call(CallResult),
}
@ -59,7 +62,7 @@ pub(crate) fn merge_call_paths(
&mut merged_path,
)?;
log::info!("merged path: {:?}", merged_path);
log::info!(target: EVIDENCE_PATH_MERGE, "merged path: {:?}", merged_path);
Ok(merged_path)
}
@ -145,14 +148,26 @@ fn merge_call(prev_call_result: CallResult, current_call_result: CallResult) ->
}
Ok(current_call_result)
}
(RequestSent, CallServiceFailed(_)) => Ok(current_call_result),
(CallServiceFailed(_), RequestSent) => Ok(prev_call_result),
(RequestSent, RequestSent) => Ok(prev_call_result),
(RequestSent, Executed) => Ok(current_call_result),
(Executed, RequestSent) => Ok(prev_call_result),
(Executed, Executed) => Ok(prev_call_result),
(CallServiceFailed(_), Executed) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
(Executed, CallServiceFailed(_)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
(RequestSent(_), CallServiceFailed(_)) => Ok(current_call_result),
(CallServiceFailed(_), RequestSent(_)) => Ok(prev_call_result),
(RequestSent(prev_sender), RequestSent(sender)) => {
if prev_sender != sender {
return Err(IncompatibleCallResults(prev_call_result, current_call_result));
}
Ok(prev_call_result)
}
(RequestSent(_), Executed(..)) => Ok(current_call_result),
(Executed(..), RequestSent(_)) => Ok(prev_call_result),
(Executed(prev_result), Executed(result)) => {
if prev_result != result {
return Err(IncompatibleCallResults(prev_call_result, current_call_result));
}
Ok(prev_call_result)
}
(CallServiceFailed(_), Executed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
(Executed(..), CallServiceFailed(_)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
}
}
@ -161,6 +176,9 @@ mod tests {
use crate::call_evidence::CallResult;
use crate::call_evidence::EvidenceState;
use crate::call_evidence::{merge_call_paths, CallEvidencePath};
use crate::JValue;
use std::rc::Rc;
#[test]
fn merge_call_states_1() {
@ -169,29 +187,29 @@ mod tests {
let mut prev_path = CallEvidencePath::new();
prev_path.push_back(Par(1, 1));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(Executed));
prev_path.push_back(Call(RequestSent(String::from("peer_1"))));
prev_path.push_back(Call(Executed(Rc::new(JValue::Null))));
prev_path.push_back(Par(1, 1));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(Executed));
prev_path.push_back(Call(RequestSent(String::from("peer_3"))));
prev_path.push_back(Call(Executed(Rc::new(JValue::Null))));
let mut current_path = CallEvidencePath::new();
current_path.push_back(Par(1, 1));
current_path.push_back(Call(Executed));
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(RequestSent(String::from("peer_2"))));
current_path.push_back(Par(1, 1));
current_path.push_back(Call(Executed));
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(RequestSent(String::from("peer_4"))));
let merged_path = merge_call_paths(prev_path, current_path).expect("merging should be successful");
let mut right_merged_path = CallEvidencePath::new();
right_merged_path.push_back(Par(1, 1));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Par(1, 1));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
assert_eq!(merged_path, right_merged_path);
}
@ -203,32 +221,32 @@ mod tests {
let mut prev_path = CallEvidencePath::new();
prev_path.push_back(Par(1, 0));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(RequestSent(String::from("peer_1"))));
prev_path.push_back(Par(1, 1));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(Executed));
prev_path.push_back(Call(RequestSent(String::from("peer_2"))));
prev_path.push_back(Call(Executed(Rc::new(JValue::Null))));
let mut current_path = CallEvidencePath::new();
current_path.push_back(Par(2, 2));
current_path.push_back(Call(Executed));
current_path.push_back(Call(Executed));
current_path.push_back(Call(Executed));
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(RequestSent(String::from("peer_1"))));
current_path.push_back(Par(1, 1));
current_path.push_back(Call(Executed));
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(RequestSent(String::from("peer_2"))));
let merged_path = merge_call_paths(prev_path, current_path).expect("merging should be successful");
let mut right_merged_path = CallEvidencePath::new();
right_merged_path.push_back(Par(2, 2));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(RequestSent));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(RequestSent(String::from("peer_1"))));
right_merged_path.push_back(Par(1, 1));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
assert_eq!(merged_path, right_merged_path);
}
@ -239,43 +257,43 @@ mod tests {
use EvidenceState::*;
let mut prev_path = CallEvidencePath::new();
prev_path.push_back(Call(Executed));
prev_path.push_back(Call(Executed(Rc::new(JValue::Null))));
prev_path.push_back(Par(2, 0));
prev_path.push_back(Par(1, 0));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(RequestSent(String::from("peer_1"))));
prev_path.push_back(Par(1, 2));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(Executed));
prev_path.push_back(Call(RequestSent));
prev_path.push_back(Call(RequestSent(String::from("peer_1"))));
prev_path.push_back(Call(Executed(Rc::new(JValue::Null))));
prev_path.push_back(Call(RequestSent(String::from("peer_1"))));
let mut current_path = CallEvidencePath::new();
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Par(3, 3));
current_path.push_back(Par(1, 1));
current_path.push_back(Call(Executed));
current_path.push_back(Call(Executed));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Par(1, 1));
current_path.push_back(Call(Executed));
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(RequestSent(String::from("peer_1"))));
current_path.push_back(Par(1, 1));
current_path.push_back(Call(Executed));
current_path.push_back(Call(RequestSent));
current_path.push_back(Call(Executed(Rc::new(JValue::Null))));
current_path.push_back(Call(RequestSent(String::from("peer_1"))));
let merged_path = merge_call_paths(prev_path, current_path).expect("merging should be successful");
let mut right_merged_path = CallEvidencePath::new();
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Par(3, 3));
right_merged_path.push_back(Par(1, 1));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Par(1, 1));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(RequestSent));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(RequestSent(String::from("peer_1"))));
right_merged_path.push_back(Par(1, 2));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(Executed));
right_merged_path.push_back(Call(RequestSent));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(Executed(Rc::new(JValue::Null))));
right_merged_path.push_back(Call(RequestSent(String::from("peer_1"))));
assert_eq!(merged_path, right_merged_path);
}

View File

@ -14,8 +14,9 @@
* limitations under the License.
*/
use crate::call_evidence::{CallResult, EvidenceState};
use crate::CallServiceResult;
use crate::build_targets::CallServiceResult;
use crate::call_evidence::CallResult;
use crate::call_evidence::EvidenceState;
use crate::JValue;
use crate::StepperOutcome;
@ -28,16 +29,10 @@ use std::env::VarError;
use std::error::Error;
#[derive(Debug)]
pub(crate) enum AquamarineError {
pub enum AquamarineError {
/// Errors occurred while parsing aqua script in the form of S expressions.
SExprParseError(SExprError),
/// Errors occurred on aqua data deserialization.
DataDeserializationError(SerdeJsonError),
/// Errors occurred on aqua data serialization.
DataSerializationError(SerdeJsonError),
/// Errors occurred while parsing function arguments of an expression.
FuncArgsSerializationError(JValue, SerdeJsonError),
@ -62,9 +57,12 @@ pub(crate) enum AquamarineError {
/// Value with such path wasn't found in data with such error.
VariableNotInJsonPath(JValue, String, JsonPathError),
/// Value for such name isn't presence in data.
/// Provided JValue has incompatible with target type.
IncompatibleJValueType(JValue, String),
/// Provided AValue has incompatible with target type.
IncompatibleAValueType(String, String),
/// Multiple values found for such json path.
MultipleValuesInJsonPath(String),
@ -83,9 +81,6 @@ pub(crate) enum AquamarineError {
/// Errors occurred on call evidence serialization.
CallEvidenceSerializationError(SerdeJsonError),
/// Errors occurred when reserved keyword is used for variable name.
ReservedKeywordError(String),
/// Errors occurred when previous and current evidence states are incompatible.
IncompatibleEvidenceStates(EvidenceState, EvidenceState),
@ -102,12 +97,6 @@ impl std::fmt::Display for AquamarineError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
AquamarineError::SExprParseError(err) => write!(f, "aqua script can't be parsed: {:?}", err),
AquamarineError::DataDeserializationError(err) => {
write!(f, "an error occurred while data deserialization: {:?}", err)
}
AquamarineError::DataSerializationError(err) => {
write!(f, "an error occurred while data serialization: {:?}", err)
}
AquamarineError::FuncArgsSerializationError(args, err) => write!(
f,
"function arguments {} can't be serialized or deserialized with an error: {:?}",
@ -137,7 +126,10 @@ impl std::fmt::Display for AquamarineError {
json_path, value, json_path_err
),
AquamarineError::IncompatibleJValueType(jvalue, desired_type) => {
write!(f, "got avalue \"{:?}\", but {} type is needed", jvalue, desired_type,)
write!(f, "got jvalue \"{:?}\", but {} type is needed", jvalue, desired_type,)
}
AquamarineError::IncompatibleAValueType(avalue, desired_type) => {
write!(f, "got avalue {}, but {} type is needed", avalue, desired_type,)
}
AquamarineError::MultipleValuesInJsonPath(json_path) => {
write!(f, "multiple variables found for this json path {}", json_path)
@ -159,11 +151,6 @@ impl std::fmt::Display for AquamarineError {
AquamarineError::CallEvidenceSerializationError(err) => {
write!(f, "an error occurred while data serialization: {:?}", err)
}
AquamarineError::ReservedKeywordError(variable_name) => write!(
f,
"a variable can't be named as {} because this name is reserved",
variable_name
),
AquamarineError::IncompatibleEvidenceStates(prev_state, current_state) => write!(
f,
"previous and current data have incompatible states: {:?} {:?}",
@ -199,32 +186,30 @@ impl Into<StepperOutcome> for AquamarineError {
fn into(self) -> StepperOutcome {
let ret_code = match self {
AquamarineError::SExprParseError(_) => 1,
AquamarineError::DataDeserializationError(..) => 2,
AquamarineError::DataSerializationError(..) => 3,
AquamarineError::FuncArgsSerializationError(..) => 4,
AquamarineError::CallServiceResultDeserializationError(..) => 5,
AquamarineError::CurrentPeerIdEnvError(..) => 6,
AquamarineError::InstructionError(..) => 7,
AquamarineError::LocalServiceError(..) => 8,
AquamarineError::VariableNotFound(..) => 9,
AquamarineError::MultipleVariablesFound(..) => 10,
AquamarineError::VariableNotInJsonPath(..) => 11,
AquamarineError::IncompatibleJValueType(..) => 12,
AquamarineError::MultipleValuesInJsonPath(..) => 13,
AquamarineError::FoldStateNotFound(..) => 14,
AquamarineError::MultipleFoldStates(..) => 15,
AquamarineError::InvalidEvidenceState(..) => 16,
AquamarineError::CallEvidenceDeserializationError(..) => 17,
AquamarineError::CallEvidenceSerializationError(..) => 18,
AquamarineError::ReservedKeywordError(..) => 19,
AquamarineError::IncompatibleEvidenceStates(..) => 20,
AquamarineError::IncompatibleCallResults(..) => 21,
AquamarineError::EvidencePathTooSmall(..) => 21,
AquamarineError::FuncArgsSerializationError(..) => 2,
AquamarineError::CallServiceResultDeserializationError(..) => 3,
AquamarineError::CurrentPeerIdEnvError(..) => 4,
AquamarineError::InstructionError(..) => 5,
AquamarineError::LocalServiceError(..) => 6,
AquamarineError::VariableNotFound(..) => 7,
AquamarineError::MultipleVariablesFound(..) => 8,
AquamarineError::VariableNotInJsonPath(..) => 9,
AquamarineError::IncompatibleJValueType(..) => 10,
AquamarineError::IncompatibleAValueType(..) => 11,
AquamarineError::MultipleValuesInJsonPath(..) => 12,
AquamarineError::FoldStateNotFound(..) => 13,
AquamarineError::MultipleFoldStates(..) => 14,
AquamarineError::InvalidEvidenceState(..) => 15,
AquamarineError::CallEvidenceDeserializationError(..) => 16,
AquamarineError::CallEvidenceSerializationError(..) => 17,
AquamarineError::IncompatibleEvidenceStates(..) => 18,
AquamarineError::IncompatibleCallResults(..) => 19,
AquamarineError::EvidencePathTooSmall(..) => 20,
};
StepperOutcome {
ret_code,
data: format!("{}", self),
call_path: format!("{}", self),
next_peer_pks: vec![],
}
}

View File

@ -14,47 +14,41 @@
* limitations under the License.
*/
mod epilog;
mod prolog;
mod utils;
use epilog::make_result_data;
use prolog::make_contexts;
use prolog::prepare;
use utils::dedup;
use crate::air::ExecutableInstruction;
use crate::AquamarineError::CallEvidenceSerializationError as CallSeError;
use crate::Result;
use crate::StepperOutcome;
use crate::STEPPER_SUCCESS;
pub(self) const CALL_EVIDENCE_CTX_KEY: &str = "__call";
pub(crate) fn execute_aqua(init_user_id: String, aqua: String, prev_data: String, data: String) -> StepperOutcome {
log::info!("aquamarine version is {}", env!("CARGO_PKG_VERSION"));
pub fn execute_aqua(init_user_id: String, aqua: String, prev_data: String, data: String) -> StepperOutcome {
log::info!(
"stepper invoked with user_id = {}, aqua = {:?}, prev_data = {:?}, data = {:?}",
init_user_id,
aqua,
prev_data,
data
"aquamarine version is {}, init user id is {}",
env!("CARGO_PKG_VERSION"),
init_user_id
);
execute_aqua_impl(init_user_id, aqua, prev_data, data).unwrap_or_else(Into::into)
}
fn execute_aqua_impl(_init_user_id: String, aqua: String, prev_data: String, data: String) -> Result<StepperOutcome> {
let (prev_data, data, aqua) = prepare(prev_data, data, aqua)?;
let (mut exec_ctx, mut call_ctx) = make_contexts(prev_data, data)?;
fn execute_aqua_impl(_init_user_id: String, aqua: String, prev_path: String, path: String) -> Result<StepperOutcome> {
let (prev_path, path, aqua) = prepare(prev_path, path, aqua)?;
let (mut exec_ctx, mut call_ctx) = make_contexts(prev_path, path)?;
aqua.execute(&mut exec_ctx, &mut call_ctx)?;
let data = make_result_data(exec_ctx.data, call_ctx)?;
let next_peer_pks = dedup(exec_ctx.next_peer_pks);
let serialized_call_path = serde_json::to_string(&call_ctx.new_path).map_err(CallSeError)?;
Ok(StepperOutcome {
ret_code: STEPPER_SUCCESS,
data,
next_peer_pks: dedup(exec_ctx.next_peer_pks),
call_path: serialized_call_path,
next_peer_pks,
})
}

View 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
View 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(())
}
}

View 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),
];

View File

@ -26,7 +26,7 @@ pub struct StepperOutcome {
pub ret_code: i32,
/// Contains data if ret_code == 0, otherwise error message (that could be empty string).
pub data: String,
pub call_path: String,
/// Public keys of peers that should receive data.
pub next_peer_pks: Vec<String>,

View 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")]);
}

View 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());
}

View 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);
}

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
use aqua_test_utils::call_vm;
use aqua_test_utils::create_aqua_vm;
use aqua_test_utils::unit_call_service;
use aquamarine_vm::vec1::Vec1;
@ -68,58 +69,42 @@ fn join_chat() {
"#,
);
let client_1_res = client_1
.call(json!(["asd", script, "{}", "{}"]))
.expect("should be successful");
let client_1_res = call_vm!(client_1, "asd", script.clone(), "[]", "[]");
let client_1_res_json: JValue = serde_json::from_str(&client_1_res.data).expect("stepper should return valid json");
let client_1_right_json = json!( {
"__call": [
{ "call": "request_sent" },
]
});
let client_1_right_json = json!([
{ "call": {"request_sent": "A" } },
]);
assert_eq!(client_1_res_json, client_1_right_json);
assert_eq!(client_1_res.next_peer_pks, vec![String::from("Relay1")]);
let relay_1_res = relay_1
.call(json!(["asd", script, client_1_res.data, "{}"]))
.expect("should be successful");
let relay_1_res = call_vm!(relay_1, "asd", script.clone(), client_1_res.data, "[]");
let relay_1_res_json: JValue = serde_json::from_str(&relay_1_res.data).expect("stepper should return valid json");
let relay_1_right_json = json!( {
"void1": ["test"],
"__call": [
{ "call": "executed" },
{ "call": "request_sent" },
]
});
let relay_1_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "request_sent": "Relay1" } },
]);
assert_eq!(relay_1_res_json, relay_1_right_json);
assert_eq!(relay_1_res.next_peer_pks, vec![String::from("Remote")]);
let remote_res = remote
.call(json!(["asd", script, relay_1_res.data, "{}"]))
.expect("should be successful");
let remote_res = call_vm!(remote, "asd", script.clone(), relay_1_res.data, "[]");
let remote_res_json: JValue = serde_json::from_str(&remote_res.data).expect("stepper should return valid json");
let remote_right_json = json!( {
"void1": ["test"],
"void2": [[["A", "Relay1"], ["B", "Relay2"]]],
"members": [["A", "Relay1"], ["B", "Relay2"]],
"__call": [
{ "call": "executed" },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [1, 2] },
{ "call": "request_sent" },
{ "par": [1, 0] },
{ "call": "request_sent" },
]
});
let remote_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "par": [1, 2] },
{ "call": { "request_sent" : "Remote" } },
{ "par": [1, 0] },
{ "call": { "request_sent" : "Remote" } },
]);
let remote_res_next_peer_pks: HashSet<_> = remote_res.next_peer_pks.iter().map(|s| s.as_str()).collect();
let next_peer_pks_right = maplit::hashset! {
@ -130,108 +115,74 @@ fn join_chat() {
assert_eq!(remote_res_json, remote_right_json);
assert_eq!(remote_res_next_peer_pks, next_peer_pks_right);
let relay_1_res = relay_1
.call(json!(["asd", script, remote_res.data, "{}"]))
.expect("should be successful");
let relay_1_res = call_vm!(relay_1, "asd", script.clone(), remote_res.data.clone(), "[]");
let relay_1_res_json: JValue = serde_json::from_str(&relay_1_res.data).expect("stepper should return valid json");
let relay_1_right_json = json!( {
"void1": ["test"],
"void2": [[["A", "Relay1"], ["B", "Relay2"]]],
"void": ["test"],
"members": [["A", "Relay1"], ["B", "Relay2"]],
"__call": [
{ "call": "executed" },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [2, 2] },
{ "call": "executed" },
{ "call": "request_sent" },
{ "par": [1, 0] },
{ "call": "request_sent" },
]
});
let relay_1_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "par": [2, 2] },
{ "call": { "executed" : "test" } },
{ "call": { "request_sent" : "Relay1" } },
{ "par": [1, 0] },
{ "call": { "request_sent" : "Remote" } },
]);
assert_eq!(relay_1_res_json, relay_1_right_json);
assert_eq!(relay_1_res.next_peer_pks, vec![String::from("A")]);
let client_1_res = client_1
.call(json!(["asd", script, relay_1_res.data, "{}"]))
.expect("should be successful");
let client_1_res = call_vm!(client_1, "asd", script.clone(), relay_1_res.data, "[]");
let client_1_res_json: JValue = serde_json::from_str(&client_1_res.data).expect("stepper should return valid json");
let client_1_right_json = json!( {
"void1": ["test"],
"void2": [[["A", "Relay1"], ["B", "Relay2"]]],
"void": ["test"],
"void3": ["test"],
"members": [["A", "Relay1"], ["B", "Relay2"]],
"__call": [
{ "call": "executed" },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [2, 2] },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [1, 0] },
{ "call": "request_sent" },
]
});
let client_1_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "par": [2, 2] },
{ "call": { "executed" : "test" } },
{ "call": { "executed" : "test" } },
{ "par": [1, 0] },
{ "call": { "request_sent" : "Remote" } },
]);
assert_eq!(client_1_res_json, client_1_right_json);
assert_eq!(client_1_res.next_peer_pks, Vec::<String>::new());
let relay_2_res = relay_2
.call(json!(["asd", script, remote_res.data, "{}"]))
.expect("should be successful");
let relay_2_res = call_vm!(relay_2, "asd", script.clone(), remote_res.data, "[]");
let relay_2_res_json: JValue = serde_json::from_str(&relay_2_res.data).expect("stepper should return valid json");
let relay_2_right_json = json!( {
"void1": ["test"],
"void2": [[["A", "Relay1"], ["B", "Relay2"]]],
"void": ["test"],
"members": [["A", "Relay1"], ["B", "Relay2"]],
"__call": [
{ "call": "executed" },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [1, 3] },
{ "call": "request_sent" },
{ "par": [2, 0] },
{ "call": "executed" },
{ "call": "request_sent" },
]
});
let relay_2_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "par": [1, 3] },
{ "call": { "request_sent" : "Remote" } },
{ "par": [2, 0] },
{ "call": { "executed" : "test" } },
{ "call": { "request_sent" : "Relay2" } },
]);
assert_eq!(relay_2_res_json, relay_2_right_json);
assert_eq!(relay_2_res.next_peer_pks, vec![String::from("B")]);
let client_2_res = client_2
.call(json!(["asd", script, relay_2_res.data, "{}"]))
.expect("should be successful");
let client_2_res = call_vm!(client_2, "asd", script, relay_2_res.data, "[]");
let client_2_res_json: JValue = serde_json::from_str(&client_2_res.data).expect("stepper should return valid json");
let client_2_right_json = json!( {
"void1": ["test"],
"void2": [[["A", "Relay1"], ["B", "Relay2"]]],
"void": ["test"],
"void3": ["test"],
"members": [["A", "Relay1"], ["B", "Relay2"]],
"__call": [
{ "call": "executed" },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [1, 3] },
{ "call": "request_sent" },
{ "par": [2, 0] },
{ "call": "executed" },
{ "call": "executed" },
]
});
let client_2_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "call": { "executed" : [["A", "Relay1"], ["B", "Relay2"]]} },
{ "par": [1, 3] },
{ "call": { "request_sent" : "Remote" } },
{ "par": [2, 0] },
{ "call": { "executed" : "test" } },
{ "call": { "executed" : "test" } },
]);
assert_eq!(client_2_res_json, client_2_right_json);
assert_eq!(client_2_res.next_peer_pks, Vec::<String>::new());
@ -271,44 +222,24 @@ fn join() {
"#,
);
let client_1_res = client_1
.call(json!(["asd", script, "{}", "{}"]))
.expect("should be successful");
let relay_1_res = relay_1
.call(json!(["asd", script, client_1_res.data, "{}"]))
.expect("should be successful");
let remote_res = remote
.call(json!(["asd", script, relay_1_res.data, "{}"]))
.expect("should be successful");
let relay_1_res = relay_1
.call(json!(["asd", script, remote_res.data, "{}"]))
.expect("should be successful");
let client_1_res = client_1
.call(json!(["asd", script, relay_1_res.data, "{}"]))
.expect("should be successful");
let client_1_res = call_vm!(client_1, "asd", script.clone(), "[]", "[]");
let relay_1_res = call_vm!(relay_1, "asd", script.clone(), client_1_res.data, "[]");
let remote_res = call_vm!(remote, "asd", script.clone(), relay_1_res.data, "[]");
let relay_1_res = call_vm!(relay_1, "asd", script.clone(), remote_res.data, "[]");
let client_1_res = call_vm!(client_1, "asd", script, relay_1_res.data, "[]");
let client_1_res_json: JValue = serde_json::from_str(&client_1_res.data).expect("stepper should return valid json");
let client_1_right_json = json!( {
"void1": ["test"],
"void": ["test", "test"],
"void3": ["test", "test"],
"members": [["A"], ["B"]],
"__call": [
{ "call": "executed" },
{ "call": "executed" },
{ "par": [2, 3] },
{ "call": "executed" },
{ "call": "executed" },
{ "par": [2, 0] },
{ "call": "executed" },
{ "call": "executed" },
]
});
let client_1_right_json = json!( [
{ "call": { "executed" : "test" } },
{ "call": { "executed" : [["A"], ["B"]]} },
{ "par": [2, 3] },
{ "call": { "executed" : "test" } },
{ "call": { "executed" : "test" } },
{ "par": [2, 0] },
{ "call": { "executed" : "test" } },
{ "call": { "executed" : "test" } },
]);
assert_eq!(client_1_res_json, client_1_right_json);
assert_eq!(client_1_res.next_peer_pks, Vec::<String>::new());

View File

@ -1,6 +1,6 @@
[package]
name = "aquamarine"
version = "0.1.1"
version = "0.1.2"
authors = ["Fluence Labs"]
edition = "2018"
@ -14,25 +14,13 @@ name = "aquamarine"
path = "src/fce.rs"
[dependencies]
stepper-lib = { path = "../stepper-lib" }
fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] }
serde = { version = "1.0.116", features = [ "derive", "rc" ] }
serde_derive = "1.0.116"
serde_sexpr = "0.1.0"
jsonpath_lib = "0.2.5"
boolinator = "2.4.0"
log = "0.4.11"
once_cell = "1.4.1"
serde_json = "1.0"
wasm-bindgen = "0.2.68"
[dev_dependencies]
aqua-test-utils = { path = "../crates/test-utils" }
aquamarine-vm = { git = "https://github.com/fluencelabs/fce" }
log = "0.4.11"
serde_json = "1.0"
env_logger = "0.7.1"
maplit = "1.0.2"
pretty_assertions = "0.6.1"
serde_json = "1.0.56"
[features]
fce = ["stepper-lib/fce"]

View File

@ -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"),
)),
}
}
}

View File

@ -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('"')
}

View File

@ -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());
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -26,36 +26,26 @@
unreachable_patterns
)]
mod air;
mod call_evidence;
mod defines;
mod errors;
mod execution;
mod stepper_outcome;
pub(crate) use crate::defines::*;
use crate::execution::execute_aqua;
use fluence::fce;
use std::env::VarError;
use stepper_lib::execute_aqua;
use stepper_lib::log_targets::TARGET_MAP;
use stepper_lib::StepperOutcome;
const CURRENT_PEER_ID_ENV_NAME: &str = "CURRENT_PEER_ID";
use std::collections::HashMap;
pub fn main() {
fluence::WasmLogger::init_with_level(log::Level::Info).unwrap();
use std::iter::FromIterator;
let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned());
fluence::WasmLogger::new()
.with_log_level(log::Level::Info)
.with_target_map(target_map)
.build()
.unwrap();
}
#[fce]
pub fn invoke(init_user_id: String, aqua: String, prev_data: String, data: String) -> StepperOutcome {
execute_aqua(init_user_id, aqua, prev_data, data)
}
pub fn get_current_peer_id() -> std::result::Result<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;
}

View File

@ -22,27 +22,27 @@
unused_imports,
unused_mut,
unused_variables,
// unused_unsafe,
unused_unsafe,
unreachable_patterns
)]
mod air;
mod call_evidence;
mod defines;
mod errors;
mod execution;
mod stepper_outcome;
pub(crate) use crate::defines::*;
use crate::execution::execute_aqua;
use wasm_bindgen::__rt::std::env::VarError;
use stepper_lib::execute_aqua;
use stepper_lib::log_targets::TARGET_MAP;
use wasm_bindgen::prelude::*;
use std::collections::HashMap;
#[wasm_bindgen(start)]
pub fn main() {
fluence::WasmLogger::init_with_level(log::Level::Info).unwrap();
use std::iter::FromIterator;
let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned());
fluence::WasmLogger::new()
.with_log_level(log::Level::Info)
.with_target_map(target_map)
.build()
.unwrap();
}
#[wasm_bindgen]
@ -50,25 +50,3 @@ pub fn invoke(init_user_id: String, aqua: String, prev_data: String, data: Strin
let outcome = execute_aqua(init_user_id, aqua, prev_data, data);
serde_json::to_string(&outcome).expect("Cannot parse StepperOutcome")
}
pub fn call_service(service_id: String, fn_name: String, args: String) -> CallServiceResult {
let result = call_service_impl(service_id, fn_name, args);
log::info!("result {}", result);
serde_json::from_str(&result).expect("Cannot parse CallServiceResult")
}
pub fn get_current_peer_id() -> std::result::Result<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;
}

View File

@ -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")]);
}

View File

@ -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());
}

View File

@ -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")]);
}