Implement two last fold iterable usage scenarious (#51)

This commit is contained in:
vms 2020-12-25 16:20:25 +03:00 committed by GitHub
parent 5cb4cc0fb0
commit cdaa24dc03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 242 additions and 89 deletions

14
Cargo.lock generated
View File

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

View File

@ -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;

View File

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

View File

@ -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<dyn for<'ctx> Iterable<'ctx, Item = IterableItemType<'ctx>>>;
pub(super) type IterableValue = Box<dyn for<'ctx> 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<Option<IterableValue>> {
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<Option<IterableValue>> {
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<IterableValue> = 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::<Vec<_>>();
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<Option<IterableValue>> {
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<Option<IterableValue>> {
use AquamarineError::JValueAccJsonPathError;
use AquamarineError::JValueJsonPathError;
let iterable: IterableValue = match exec_ctx.data_cache.get(variable) {
let iterable: Option<IterableValue> = 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::<Vec<_>>();
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<Vec<&'jvalue JValue>> {
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<ResolvedTriplet>, json_path: &str) -> Option<IterableValue> {
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<ResolvedTriplet> {
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)
}

View File

@ -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<SecurityTetraplet>;
}
impl<'ctx> JValuable for IterableItemType<'ctx> {
impl<'ctx> JValuable for IterableItem<'ctx> {
fn apply_json_path(&self, json_path: &str) -> Result<Vec<&JValue>> {
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<SecurityTetraplet>)> {
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<Self>) -> 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<SecurityTetraplet> {
use IterableItemType::*;
use IterableItem::*;
// these clones are needed because rust-sdk allows passing arguments only by value
match self {

View File

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

View File

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

View File

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

View File

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