diff --git a/Cargo.lock b/Cargo.lock index 29a5083b..3705cc9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,7 +70,7 @@ dependencies = [ [[package]] name = "aquamarine-vm" version = "0.1.6" -source = "git+https://github.com/fluencelabs/fce?branch=master#f022a2dec4229f1dd739794e98a0e5d080ea13d5" +source = "git+https://github.com/fluencelabs/fce#f022a2dec4229f1dd739794e98a0e5d080ea13d5" dependencies = [ "fluence-faas 0.1.18 (git+https://github.com/fluencelabs/fce)", "maplit", @@ -654,7 +654,7 @@ dependencies = [ [[package]] name = "fce" version = "0.1.14" -source = "git+https://github.com/fluencelabs/fce?branch=master#f022a2dec4229f1dd739794e98a0e5d080ea13d5" +source = "git+https://github.com/fluencelabs/fce#f022a2dec4229f1dd739794e98a0e5d080ea13d5" dependencies = [ "boolinator", "fce-utils 0.1.0 (git+https://github.com/fluencelabs/fce)", @@ -682,7 +682,7 @@ checksum = "dcdd9d63fa5f87e03514ba8ca3bb91a25121bbf1c63a26df710f166ab8f274f7" [[package]] name = "fce-utils" version = "0.1.0" -source = "git+https://github.com/fluencelabs/fce?branch=master#f022a2dec4229f1dd739794e98a0e5d080ea13d5" +source = "git+https://github.com/fluencelabs/fce#f022a2dec4229f1dd739794e98a0e5d080ea13d5" [[package]] name = "fce-wit-interfaces" @@ -697,7 +697,7 @@ dependencies = [ [[package]] name = "fce-wit-interfaces" version = "0.1.9" -source = "git+https://github.com/fluencelabs/fce?branch=master#f022a2dec4229f1dd739794e98a0e5d080ea13d5" +source = "git+https://github.com/fluencelabs/fce#f022a2dec4229f1dd739794e98a0e5d080ea13d5" dependencies = [ "multimap", "wasmer-interface-types-fl", @@ -720,7 +720,7 @@ dependencies = [ [[package]] name = "fce-wit-parser" version = "0.1.11" -source = "git+https://github.com/fluencelabs/fce?branch=master#f022a2dec4229f1dd739794e98a0e5d080ea13d5" +source = "git+https://github.com/fluencelabs/fce#f022a2dec4229f1dd739794e98a0e5d080ea13d5" dependencies = [ "anyhow", "fce-wit-interfaces 0.1.9 (git+https://github.com/fluencelabs/fce)", @@ -788,7 +788,7 @@ dependencies = [ [[package]] name = "fluence-faas" version = "0.1.18" -source = "git+https://github.com/fluencelabs/fce?branch=master#f022a2dec4229f1dd739794e98a0e5d080ea13d5" +source = "git+https://github.com/fluencelabs/fce#f022a2dec4229f1dd739794e98a0e5d080ea13d5" dependencies = [ "cmd_lib", "fce 0.1.14 (git+https://github.com/fluencelabs/fce)", @@ -1685,7 +1685,7 @@ dependencies = [ [[package]] name = "stepper-interface" version = "0.1.0" -source = "git+https://github.com/fluencelabs/aquamarine?branch=master#6e5efe4903fa6c89781ef2f20edabca281ff9011" +source = "git+https://github.com/fluencelabs/aquamarine#5cb4cc0fb0c149a4bd3160dd0bac9c3d5ac3db7d" dependencies = [ "fluence", "serde", diff --git a/stepper-lib/src/air/fold.rs b/stepper-lib/src/air/fold.rs index 24f475a4..5b3c9e0e 100644 --- a/stepper-lib/src/air/fold.rs +++ b/stepper-lib/src/air/fold.rs @@ -18,7 +18,7 @@ mod iterable; mod utils; pub(crate) use iterable::Iterable; -pub(crate) use iterable::IterableItemType; +pub(crate) use iterable::IterableItem; use super::CallEvidenceCtx; use super::ExecutionCtx; diff --git a/stepper-lib/src/air/fold/iterable.rs b/stepper-lib/src/air/fold/iterable.rs index 6d37c485..9604bc3b 100644 --- a/stepper-lib/src/air/fold/iterable.rs +++ b/stepper-lib/src/air/fold/iterable.rs @@ -39,8 +39,11 @@ pub(crate) trait Iterable<'ctx> { } /// Combines all possible iterable item types. +/// +/// Iterable item is a variable that `fold` sets to each element of the collection it iterates +/// through, i.e., it is the `iterable` in the `(fold collection iterable instruction)` statement. #[derive(Clone, Debug, Eq, PartialEq)] -pub(crate) enum IterableItemType<'ctx> { +pub(crate) enum IterableItem<'ctx> { RefRef((&'ctx JValue, &'ctx SecurityTetraplet)), RefValue((&'ctx JValue, SecurityTetraplet)), RcValue((Rc, SecurityTetraplet)), @@ -141,7 +144,7 @@ macro_rules! foldable_prev { } impl<'ctx> Iterable<'ctx> for IterableResolvedCall { - type Item = IterableItemType<'ctx>; + type Item = IterableItem<'ctx>; fn next(&mut self) -> bool { foldable_next!(self, self.len) @@ -170,13 +173,13 @@ impl<'ctx> Iterable<'ctx> for IterableResolvedCall { _ => unimplemented!("this jvalue is set only by fold instruction, so it must have an array type"), }; - let result = IterableItemType::RefValue((jvalue, tetraplet)); + let result = IterableItem::RefValue((jvalue, tetraplet)); Some(result) } } impl<'ctx> Iterable<'ctx> for IterableVecResolvedCall { - type Item = IterableItemType<'ctx>; + type Item = IterableItem<'ctx>; fn next(&mut self) -> bool { foldable_next!(self, self.call_results.len()) @@ -197,13 +200,13 @@ impl<'ctx> Iterable<'ctx> for IterableVecResolvedCall { json_path: String::new(), }; - let result = IterableItemType::RcValue((result, tetraplet)); + let result = IterableItem::RcValue((result, tetraplet)); Some(result) } } impl<'ctx> Iterable<'ctx> for IterableJsonPathResult { - type Item = IterableItemType<'ctx>; + type Item = IterableItem<'ctx>; fn next(&mut self) -> bool { foldable_next!(self, self.jvalues.len()) @@ -219,14 +222,14 @@ impl<'ctx> Iterable<'ctx> for IterableJsonPathResult { } let jvalue = &self.jvalues[self.cursor]; - let result = IterableItemType::RefRef((jvalue, &self.tetraplet)); + let result = IterableItem::RefRef((jvalue, &self.tetraplet)); Some(result) } } impl<'ctx> Iterable<'ctx> for IterableVecJsonPathResult { - type Item = IterableItemType<'ctx>; + type Item = IterableItem<'ctx>; fn next(&mut self) -> bool { foldable_next!(self, self.jvalues.len()) @@ -243,7 +246,7 @@ impl<'ctx> Iterable<'ctx> for IterableVecJsonPathResult { let jvalue = &self.jvalues[self.cursor]; let tetraplet = &self.tetraplets[self.cursor]; - let result = IterableItemType::RefRef((jvalue, tetraplet)); + let result = IterableItem::RefRef((jvalue, tetraplet)); Some(result) } diff --git a/stepper-lib/src/air/fold/utils.rs b/stepper-lib/src/air/fold/utils.rs index 7e0ca956..d91e3737 100644 --- a/stepper-lib/src/air/fold/utils.rs +++ b/stepper-lib/src/air/fold/utils.rs @@ -16,19 +16,25 @@ use super::iterable::*; use super::Iterable; -use super::IterableItemType; +use super::IterableItem; use crate::air::ExecutionCtx; +use crate::air::JValuable; use crate::AValue; use crate::AquamarineError; use crate::JValue; +use crate::ResolvedCallResult; +use crate::ResolvedTriplet; use crate::Result; use crate::SecurityTetraplet; use air_parser::ast::InstructionValue; +use jsonpath_lib::select; +use jsonpath_lib::select_with_iter; use std::ops::Deref; +use std::rc::Rc; -pub(super) type IterableValue = Box Iterable<'ctx, Item = IterableItemType<'ctx>>>; +pub(super) type IterableValue = Box Iterable<'ctx, Item = IterableItem<'ctx>>>; /// Constructs iterable value for given instruction value, /// return Some if iterable isn't empty and None otherwise. @@ -36,10 +42,17 @@ pub(super) fn construct_iterable_value<'ctx>( value: &InstructionValue<'ctx>, exec_ctx: &ExecutionCtx<'ctx>, ) -> Result> { + use AquamarineError::AIRParseError; + match value { InstructionValue::Variable(name) => handle_instruction_variable(exec_ctx, name), InstructionValue::JsonPath { variable, path } => handle_instruction_json_path(exec_ctx, variable, path), - _ => unreachable!("it's statically checked that other types of iterable value aren't possible here"), + // TODO: check statically that it isn't possible to use string literals and so on as fold iterable + _ => { + return Err(AIRParseError(String::from( + "only variables could be used as fold iterables", + ))) + } } } @@ -47,24 +60,8 @@ fn handle_instruction_variable<'ctx>( exec_ctx: &ExecutionCtx<'ctx>, variable_name: &str, ) -> Result> { - use AquamarineError::IncompatibleJValueType; - - let iterable: IterableValue = match exec_ctx.data_cache.get(variable_name) { - Some(AValue::JValueRef(call_result)) => { - let len = match &call_result.result.deref() { - JValue::Array(array) => { - if array.is_empty() { - // skip fold if array is empty - return Ok(None); - } - array.len() - } - v => return Err(IncompatibleJValueType((*v).clone(), "array")), - }; - - let foldable = IterableResolvedCall::init(call_result.clone(), len); - Box::new(foldable) - } + let iterable: Option = match exec_ctx.data_cache.get(variable_name) { + Some(AValue::JValueRef(call_result)) => from_call_result(call_result.clone())?, Some(AValue::JValueAccumulatorRef(acc)) => { let acc = acc.borrow(); if acc.is_empty() { @@ -73,78 +70,123 @@ fn handle_instruction_variable<'ctx>( let call_results = acc.iter().cloned().collect::>(); let foldable = IterableVecResolvedCall::init(call_results); - Box::new(foldable) + Some(Box::new(foldable)) } - _ => { - return Err(AquamarineError::InstructionError(String::from( - "At now, it isn't possible to use fold iterator in other folds", - ))) + Some(AValue::JValueFoldCursor(fold_state)) => { + let iterable_value = fold_state.iterable.peek().unwrap(); + let jvalue = iterable_value.as_jvalue(); + let result = Rc::new(jvalue.into_owned()); + let triplet = as_triplet(&iterable_value); + + let call_result = ResolvedCallResult { result, triplet }; + from_call_result(call_result)? } + _ => return Err(AquamarineError::VariableNotFound(variable_name.to_string())), }; - Ok(Some(iterable)) + Ok(iterable) +} + +/// Constructs iterable value from resolved call result. +fn from_call_result(call_result: ResolvedCallResult) -> Result> { + use AquamarineError::IncompatibleJValueType; + + let len = match &call_result.result.deref() { + JValue::Array(array) => { + if array.is_empty() { + // skip fold if array is empty + return Ok(None); + } + array.len() + } + v => return Err(IncompatibleJValueType((*v).clone(), "array")), + }; + + let foldable = IterableResolvedCall::init(call_result, len); + let foldable = Box::new(foldable); + + Ok(Some(foldable)) } fn handle_instruction_json_path<'ctx>( exec_ctx: &ExecutionCtx<'ctx>, - variable: &str, - path: &str, + variable_name: &str, + json_path: &str, ) -> Result> { use AquamarineError::JValueAccJsonPathError; - use AquamarineError::JValueJsonPathError; - let iterable: IterableValue = match exec_ctx.data_cache.get(variable) { + let iterable: Option = match exec_ctx.data_cache.get(variable_name) { Some(AValue::JValueRef(variable)) => { - use jsonpath_lib::select; - - let jvalues = select(&variable.result, path) - .map_err(|e| JValueJsonPathError(variable.result.deref().clone(), path.to_string(), e))?; - - let len = jvalues.len(); - if len == 0 { - return Ok(None); - } - - let jvalues = jvalues.into_iter().cloned().collect(); - - let tetraplet = SecurityTetraplet { - triplet: variable.triplet.clone(), - json_path: path.to_string(), - }; - - let foldable = IterableJsonPathResult::init(jvalues, tetraplet); - Box::new(foldable) + let jvalues = apply_json_path(&variable.result, json_path)?; + from_jvalues(jvalues, variable.triplet.clone(), json_path) } Some(AValue::JValueAccumulatorRef(acc)) => { - use jsonpath_lib::select_with_iter; - let acc = acc.borrow(); if acc.is_empty() { return Ok(None); } let acc_iter = acc.iter().map(|v| v.result.deref()); - let (jvalues, tetraplet_indices) = select_with_iter(acc_iter, &path) - .map_err(|e| JValueAccJsonPathError(acc.clone(), path.to_string(), e))?; + let (jvalues, tetraplet_indices) = select_with_iter(acc_iter, &json_path) + .map_err(|e| JValueAccJsonPathError(acc.clone(), json_path.to_string(), e))?; let jvalues = jvalues.into_iter().cloned().collect(); let tetraplets = tetraplet_indices .into_iter() .map(|id| SecurityTetraplet { triplet: acc[id].triplet.clone(), - json_path: path.to_string(), + json_path: json_path.to_string(), }) .collect::>(); let foldable = IterableVecJsonPathResult::init(jvalues, tetraplets); - Box::new(foldable) + Some(Box::new(foldable)) } - _ => { - return Err(AquamarineError::InstructionError(String::from( - "At now, it isn't possible to use fold iterator in other folds", - ))) + Some(AValue::JValueFoldCursor(fold_state)) => { + let iterable_value = fold_state.iterable.peek().unwrap(); + let jvalues = iterable_value.apply_json_path(json_path)?; + let triplet = as_triplet(&iterable_value); + + from_jvalues(jvalues, triplet, json_path) } + _ => return Err(AquamarineError::VariableNotFound(variable_name.to_string())), }; - Ok(Some(iterable)) + Ok(iterable) +} + +fn apply_json_path<'jvalue, 'str>(jvalue: &'jvalue JValue, json_path: &'str str) -> Result> { + use AquamarineError::JValueJsonPathError; + + select(jvalue, json_path).map_err(|e| JValueJsonPathError(jvalue.clone(), json_path.to_string(), e)) +} + +/// Applies json_path to provided jvalues and construct IterableValue from the result and given triplet. +fn from_jvalues(jvalues: Vec<&JValue>, triplet: Rc, json_path: &str) -> Option { + if jvalues.is_empty() { + return None; + } + + let jvalues = jvalues.into_iter().cloned().collect(); + + let tetraplet = SecurityTetraplet { + triplet, + json_path: json_path.to_string(), + }; + + let foldable = IterableJsonPathResult::init(jvalues, tetraplet); + Some(Box::new(foldable)) +} + +fn as_triplet(iterable: &IterableItem<'_>) -> Rc { + use IterableItem::*; + + let tetraplet = match iterable { + RefRef((_, tetraplet)) => tetraplet, + RefValue((_, tetraplet)) => tetraplet, + RcValue((_, tetraplet)) => tetraplet, + }; + + // clone is cheap here, because triplet is under Rc + Rc::clone(&tetraplet.triplet) } diff --git a/stepper-lib/src/air/jvaluable.rs b/stepper-lib/src/air/jvaluable.rs index 8e189bd0..7346b23e 100644 --- a/stepper-lib/src/air/jvaluable.rs +++ b/stepper-lib/src/air/jvaluable.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::air::fold::IterableItemType; +use crate::air::fold::IterableItem; use crate::JValue; use crate::ResolvedCallResult; use crate::Result; @@ -44,10 +44,10 @@ pub(crate) trait JValuable { fn as_tetraplets(&self) -> Vec; } -impl<'ctx> JValuable for IterableItemType<'ctx> { +impl<'ctx> JValuable for IterableItem<'ctx> { fn apply_json_path(&self, json_path: &str) -> Result> { use crate::AquamarineError::JValueJsonPathError as JsonPathError; - use IterableItemType::*; + use IterableItem::*; let jvalue = match self { RefRef((jvalue, _)) => *jvalue, @@ -61,7 +61,7 @@ impl<'ctx> JValuable for IterableItemType<'ctx> { } fn apply_json_path_with_tetraplets(&self, json_path: &str) -> Result<(Vec<&JValue>, Vec)> { - use super::fold::IterableItemType::*; + use super::fold::IterableItem::*; use crate::AquamarineError::JValueJsonPathError as JsonPathError; let (jvalue, tetraplet) = match self { @@ -76,7 +76,7 @@ impl<'ctx> JValuable for IterableItemType<'ctx> { } fn as_jvalue(&self) -> Cow<'_, JValue> { - use IterableItemType::*; + use IterableItem::*; match self { RefRef((jvalue, _)) => Cow::Borrowed(jvalue), @@ -86,7 +86,7 @@ impl<'ctx> JValuable for IterableItemType<'ctx> { } fn into_jvalue(self: Box) -> JValue { - use IterableItemType::*; + use IterableItem::*; match *self { RefRef((jvalue, _)) => jvalue.deref().clone(), @@ -96,7 +96,7 @@ impl<'ctx> JValuable for IterableItemType<'ctx> { } fn as_tetraplets(&self) -> Vec { - use IterableItemType::*; + use IterableItem::*; // these clones are needed because rust-sdk allows passing arguments only by value match self { diff --git a/stepper-lib/tests/network_explore.rs b/stepper-lib/tests/network_explore.rs new file mode 100644 index 00000000..5d2f7a00 --- /dev/null +++ b/stepper-lib/tests/network_explore.rs @@ -0,0 +1,80 @@ +/* + * 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 serde_json::json; + +#[test] +fn network_explore() { + //use stepper_lib::CallResult::*; + //use stepper_lib::EvidenceState::{self, *}; + + let relay_id = String::from("12D3KooWSpr929UacQSTUWQeK7CQPhcW2TSmLrGTdE1NcFWkuCvY"); + let client_id = String::from("12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck"); + let set_variables_state = maplit::hashmap!( + String::from("relay") => json!(relay_id).to_string(), + String::from("client") => json!(client_id).to_string(), + ); + + let client_call_service = set_variables_call_service(set_variables_state); + let mut client = create_aqua_vm( + client_call_service, + "12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck", + ); + + let client_1_id = String::from("12D3KooWFX27Tg3cNJkFk3W2iapnyRhwfwdQ4ZiTucsy1Go3MSGL"); + let client_2_id = String::from("12D3KooWGNJoFmCNEHq8NpunB4pZSUh9UBHM53W1NwE7gM8L3RjZ"); + let relay_call_service = + aqua_test_utils::set_variable_call_service(format!(r#"["{}", "{}"]"#, client_1_id, client_2_id)); + let mut relay = create_aqua_vm(relay_call_service, relay_id.clone()); + + let client_1_call_service = + aqua_test_utils::set_variable_call_service(format!(r#"["{}", "{}"]"#, relay_id, client_2_id)); + let mut client_1 = create_aqua_vm(client_1_call_service, client_1_id.clone()); + + let client_2_call_service = + aqua_test_utils::set_variable_call_service(format!(r#"["{}", "{}"]"#, relay_id, client_1_id)); + let mut client_2 = create_aqua_vm(client_2_call_service, client_2_id.clone()); + + let script = include_str!("./scripts/network_explore.clj"); + + let client_res = call_vm!(client, "", script, "[]", "[]"); + assert_eq!(client_res.next_peer_pks, vec![relay_id.clone()]); + + let relay_res = call_vm!(relay, "", script, "", client_res.data); + assert_eq!(relay_res.next_peer_pks, vec![client_1_id.clone()]); + + let client_1_res = call_vm!(client_1, "", script, "", relay_res.data.clone()); + assert_eq!(client_1_res.next_peer_pks, vec![client_2_id.clone()]); + + let client_2_res = call_vm!(client_2, "", script, "", client_1_res.data.clone()); + assert_eq!(client_2_res.next_peer_pks, vec![relay_id.clone()]); + + let relay_res = call_vm!(relay, "", script, relay_res.data, client_2_res.data.clone()); + assert_eq!(relay_res.next_peer_pks, vec![client_2_id.clone()]); + + let client_2_res = call_vm!(client_2, "", script, client_2_res.data, relay_res.data.clone()); + assert_eq!(client_2_res.next_peer_pks, vec![relay_id.clone()]); + + let relay_res = call_vm!(relay, "", script, relay_res.data, client_2_res.data); + assert_eq!(relay_res.next_peer_pks, vec![client_1_id.clone()]); + + let client_1_res = call_vm!(client_1, "", script, client_1_res.data, relay_res.data); + assert_eq!(client_1_res.next_peer_pks, Vec::::new()); +} diff --git a/stepper-lib/tests/scripts/network_explore.clj b/stepper-lib/tests/scripts/network_explore.clj new file mode 100644 index 00000000..634535d1 --- /dev/null +++ b/stepper-lib/tests/scripts/network_explore.clj @@ -0,0 +1,28 @@ +(seq + (seq + (call "12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck" ("" "") ["relay"] relay) + (call "12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck" ("" "") ["client"] client) + ) + (seq + (call relay ("dht" "neighborhood") [relay] neighs_top) + (seq + (fold neighs_top n + (seq + (call n ("dht" "neighborhood") [n] neighs_inner[]) + (next n) + ) + ) + (fold neighs_inner ns + (seq + (fold ns n + (seq + (call n ("op" "identify") [] services[]) + (next n) + ) + ) + (next ns) + ) + ) + ) + ) + ) \ No newline at end of file diff --git a/stepper-lib/tests/security_tetraplets/auth_module/Cargo.lock b/stepper-lib/tests/security_tetraplets/auth_module/Cargo.lock index 6d1c117c..997d37e0 100644 --- a/stepper-lib/tests/security_tetraplets/auth_module/Cargo.lock +++ b/stepper-lib/tests/security_tetraplets/auth_module/Cargo.lock @@ -397,9 +397,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jsonpath_lib-fl" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e81233a3c2e1f4579f1fdb856eeec115dcb23817374268212ebad691bd53e664" +checksum = "243653439f0992adf0bbf6ed5b798966fdbacd417b9dcb025b50200ec20c17ff" dependencies = [ "array_tool", "env_logger", diff --git a/stepper-lib/tests/security_tetraplets/log_storage/Cargo.lock b/stepper-lib/tests/security_tetraplets/log_storage/Cargo.lock index 0aa953d2..352facb6 100644 --- a/stepper-lib/tests/security_tetraplets/log_storage/Cargo.lock +++ b/stepper-lib/tests/security_tetraplets/log_storage/Cargo.lock @@ -389,9 +389,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jsonpath_lib-fl" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e81233a3c2e1f4579f1fdb856eeec115dcb23817374268212ebad691bd53e664" +checksum = "243653439f0992adf0bbf6ed5b798966fdbacd417b9dcb025b50200ec20c17ff" dependencies = [ "array_tool", "env_logger",