mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 15:20:16 +00:00
Make xor catch more execution errors; fix joinable behaviour (#57)
This commit is contained in:
parent
a5858e1a98
commit
dabadeb75b
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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",
|
||||
|
4
crates/air-interpreter-wasm/Cargo.lock
generated
4
crates/air-interpreter-wasm/Cargo.lock
generated
@ -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 = [
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "stepper-lib"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>>,
|
||||
|
@ -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
|
||||
|
@ -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::*;
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
];
|
||||
|
@ -671,7 +671,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "stepper-lib"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"air-parser",
|
||||
"boolinator",
|
||||
|
@ -671,7 +671,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "stepper-lib"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"air-parser",
|
||||
"boolinator",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "aquamarine"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user