Make xor catch more execution errors; fix joinable behaviour (#57)

This commit is contained in:
vms 2021-01-16 00:40:57 +03:00 committed by GitHub
parent a5858e1a98
commit dabadeb75b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 138 additions and 69 deletions

4
Cargo.lock generated
View File

@ -56,7 +56,7 @@ dependencies = [
[[package]]
name = "aquamarine"
version = "0.2.2"
version = "0.2.3"
dependencies = [
"fluence",
"log",
@ -1651,7 +1651,7 @@ dependencies = [
[[package]]
name = "stepper-lib"
version = "0.2.2"
version = "0.2.3"
dependencies = [
"air-parser",
"aqua-test-utils",

View File

@ -97,7 +97,7 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.119"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3"
dependencies = [
@ -106,7 +106,7 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.119"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd"
dependencies = [

View File

@ -1,6 +1,6 @@
[package]
name = "stepper-lib"
version = "0.2.2"
version = "0.3.0"
authors = ["Fluence Labs"]
edition = "2018"

View File

@ -28,35 +28,54 @@ use crate::log_instruction;
use air_parser::ast::Call;
/// This macro converts joinable errors to Ok and sets subtree complete to true.
macro_rules! joinable {
($cmd:expr, $exec_ctx:expr) => {
match $cmd {
Err(e) if is_joinable_error_type(&e) => {
$exec_ctx.subtree_complete = false;
return Ok(());
}
v => v,
}
};
}
impl<'i> super::ExecutableInstruction<'i> for Call<'i> {
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
use ExecutionError::JValueJsonPathError;
use ExecutionError::VariableNotFound;
log_instruction!(call, exec_ctx, trace_ctx);
let resolved_call = match ResolvedCall::new(self, exec_ctx) {
Ok(resolved_call) => resolved_call,
// to support lazy variable evaluation
Err(VariableNotFound(variable_name)) => {
log::trace!(r#"variable with name "{}" not found, waiting"#, variable_name);
exec_ctx.subtree_complete = false;
return Ok(());
}
Err(JValueJsonPathError(variable, json_path, json_path_err)) => {
log::trace!(
r#"variable not found with json path "{}" in {:?} with error "{:?}", waiting"#,
json_path,
variable,
json_path_err
);
exec_ctx.subtree_complete = false;
return Ok(());
}
Err(err) => return Err(err),
};
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx)?;
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx)
}
}
resolved_call.execute(exec_ctx, trace_ctx)
macro_rules! log_join {
($($args:tt)*) => {
log::trace!(target: crate::log_targets::JOIN_BEHAVIOUR, $($args)*)
}
}
/// Returns true, if supplied error is related to variable not found errors type.
/// Print log if this is joinable error type.
#[rustfmt::skip::macros(log_join)]
fn is_joinable_error_type(exec_error: &ExecutionError) -> bool {
use ExecutionError::*;
match exec_error {
VariableNotFound(var_name) => {
log_join!(" call is waiting for an argument with name '{}'", var_name);
true
}
JValueJsonPathError(value, json_path, _) => {
log_join!(" call is waiting for an argument with path '{}' on jvalue '{:?}'", json_path, value);
true
}
JValueAccJsonPathError(acc, json_path, _) => {
log_join!(" call is waiting for an argument with path '{}' on accumulator '{:?}'", json_path, acc);
true
}
_ => false,
}
}

View File

@ -34,13 +34,14 @@ use air_parser::ast::{CallOutput, InstructionValue};
use std::rc::Rc;
/// Represents Call instruction with resolved internal parts.
#[derive(Clone)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub(super) struct ResolvedCall<'i> {
triplet: Rc<ResolvedTriplet>,
function_arg_paths: Rc<Vec<InstructionValue<'i>>>,
output: CallOutput<'i>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
struct ResolvedArguments {
call_arguments: String,
tetraplets: Vec<Vec<SecurityTetraplet>>,

View File

@ -456,36 +456,6 @@ mod tests {
vm_a.call_with_prev_data("", script, "[]", res.data)
}
let use_non_exist_variable_script = String::from(
r#"
(seq
(seq
(call "set_variable" ("" "") [] Iterable1)
(call "set_variable" ("" "") [] Iterable2)
)
(fold Iterable1 i
(seq
(seq
(fold Iterable2 j
(seq
(seq
(call "A" ("" "") [i] local_j)
(call "B" ("" "") [local_j])
)
(next j)
)
)
(call "A" ("" "") [local_j])
)
(next i)
)
)
)"#,
);
let res = execute_script(use_non_exist_variable_script).unwrap();
assert_eq!(res.ret_code, 1004);
let variable_shadowing_script = String::from(
r#"
(seq

View File

@ -15,7 +15,7 @@
*/
use super::ExecutionCtx;
use super::ExecutionError::LocalServiceError;
use super::ExecutionError;
use super::ExecutionResult;
use super::ExecutionTraceCtx;
use crate::log_instruction;
@ -28,7 +28,7 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
exec_ctx.subtree_complete = true;
match self.0.execute(exec_ctx, trace_ctx) {
Err(LocalServiceError(_)) => {
Err(e) if is_catchable_by_xor(&e) => {
exec_ctx.subtree_complete = true;
self.1.execute(exec_ctx, trace_ctx)
}
@ -37,6 +37,17 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
}
}
/// Returns true, if this execution error type should be catched by xor.
fn is_catchable_by_xor(exec_error: &ExecutionError) -> bool {
use ExecutionError::*;
match exec_error {
// this type of errors related to invalid data and should treat as hard errors.
InvalidExecutedState(..) => false,
_ => true,
}
}
#[cfg(test)]
mod tests {
use crate::contexts::execution_trace::ExecutionTrace;
@ -113,6 +124,64 @@ mod tests {
assert_eq!(actual_trace[0], executed_call_result);
}
#[test]
fn xor_var_not_found() {
use aqua_test_utils::echo_string_call_service;
let local_peer_id = "local_peer_id";
let mut vm = create_aqua_vm(echo_string_call_service(), local_peer_id);
let script = format!(
r#"
(xor
(call "{0}" ("service_id_1" "local_fn_name") [non_existent_variable] result)
(call "{0}" ("service_id_2" "local_fn_name") ["expected"] result)
)"#,
local_peer_id,
);
let res = call_vm!(vm, "asd", script, "[]", "[]");
let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json");
assert!(actual_trace.is_empty());
assert!(res.next_peer_pks.is_empty());
}
#[test]
fn xor_multiple_variables_found() {
use crate::contexts::execution_trace::CallResult::*;
use crate::contexts::execution_trace::ExecutedState::*;
use aqua_test_utils::echo_string_call_service;
let set_variables_peer_id = "set_variables_peer_id";
let mut set_variables_vm = create_aqua_vm(echo_string_call_service(), set_variables_peer_id);
let local_peer_id = "local_peer_id";
let mut vm = create_aqua_vm(echo_string_call_service(), local_peer_id);
let test_string_1 = String::from("some_string");
let test_string_2 = String::from("expected_string");
let script = format!(
r#"
(seq
(call "{0}" ("service_id_1" "local_fn_name") ["{2}"] result_1)
(xor
(call "{1}" ("service_id_1" "local_fn_name") [""] result_1)
(call "{1}" ("service_id_2" "local_fn_name") ["{3}"] result_2)
)
)"#,
set_variables_peer_id, local_peer_id, test_string_1, test_string_2
);
let res = call_vm!(set_variables_vm, "asd", script.clone(), "[]", "[]");
let res = call_vm!(vm, "asd", script, "[]", res.data);
let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json");
assert_eq!(actual_trace.len(), 2);
assert_eq!(actual_trace[0], Call(Executed(Rc::new(JValue::String(test_string_1)))));
assert_eq!(actual_trace[1], Call(Executed(Rc::new(JValue::String(test_string_2)))));
}
#[test]
fn xor_par() {
use crate::contexts::execution_trace::CallResult::*;

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
use super::ExecutionError::JValueAccJsonPathError;
use super::ExecutionResult;
use super::JValuable;
use crate::contexts::execution::ResolvedCallResult;
@ -27,7 +28,9 @@ use std::ops::Deref;
impl JValuable for std::cell::Ref<'_, Vec<ResolvedCallResult>> {
fn apply_json_path(&self, json_path: &str) -> ExecutionResult<Vec<&JValue>> {
let (selected_values, _) = select_with_iter(self.iter().map(|r| r.result.deref()), json_path).unwrap();
let acc_iter = self.iter().map(|r| r.result.deref());
let (selected_values, _) = select_with_iter(acc_iter, json_path)
.map_err(|e| JValueAccJsonPathError(self.iter().cloned().collect::<Vec<_>>(), json_path.to_string(), e))?;
Ok(selected_values)
}
@ -36,8 +39,11 @@ impl JValuable for std::cell::Ref<'_, Vec<ResolvedCallResult>> {
&self,
json_path: &str,
) -> ExecutionResult<(Vec<&JValue>, Vec<SecurityTetraplet>)> {
let (selected_values, tetraplet_indices) =
select_with_iter(self.iter().map(|r| r.result.deref()), json_path).unwrap();
let acc_iter = self.iter().map(|r| r.result.deref());
let (selected_values, tetraplet_indices) = select_with_iter(acc_iter, json_path)
.map_err(|e| JValueAccJsonPathError(self.iter().cloned().collect::<Vec<_>>(), json_path.to_string(), e))?;
let tetraplets = tetraplet_indices
.into_iter()
.map(|id| SecurityTetraplet {

View File

@ -44,8 +44,11 @@ pub const RUN_PARAMS: &str = "initial_params";
/// Print out state of data cache at the beginning of each instruction execution.
pub const EXECUTED_STATE_CHANGING: &str = "executed_state_changing";
/// Print log if call is postponed due the join behaviour.
pub const JOIN_BEHAVIOUR: &str = "join_behaviour";
/// This map should be used by rust-sdk logger that allows print only necessary targets by id.
pub const TARGET_MAP: [(&str, i32); 10] = [
pub const TARGET_MAP: [(&str, i32); 11] = [
(INSTRUCTION, 1 << 1),
(DATA_CACHE, 1 << 2),
(NEXT_PEER_PKS, 1 << 3),
@ -56,4 +59,5 @@ pub const TARGET_MAP: [(&str, i32); 10] = [
(EXECUTED_TRACE_MERGE, 1 << 8),
(RUN_PARAMS, 1 << 9),
(EXECUTED_STATE_CHANGING, 1 << 9),
(JOIN_BEHAVIOUR, 1 << 10),
];

View File

@ -671,7 +671,7 @@ dependencies = [
[[package]]
name = "stepper-lib"
version = "0.2.2"
version = "0.2.3"
dependencies = [
"air-parser",
"boolinator",

View File

@ -671,7 +671,7 @@ dependencies = [
[[package]]
name = "stepper-lib"
version = "0.2.2"
version = "0.2.3"
dependencies = [
"air-parser",
"boolinator",

View File

@ -1,6 +1,6 @@
[package]
name = "aquamarine"
version = "0.2.2"
version = "0.3.0"
authors = ["Fluence Labs"]
edition = "2018"