Skip to content

Commit

Permalink
feat(trace-handler): TracePos becomes a wrapper for u32 alias [fixes …
Browse files Browse the repository at this point in the history
…VM-267]
  • Loading branch information
raftedproc committed Mar 31, 2023
1 parent 76d263b commit 52b79a8
Show file tree
Hide file tree
Showing 22 changed files with 309 additions and 127 deletions.
5 changes: 5 additions & 0 deletions air/src/execution_step/errors/uncatchable_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::ToErrorCode;
use air_interpreter_cid::CidCalculationError;
use air_interpreter_data::ValueRef;
use air_trace_handler::GenerationCompatificationError;
use air_trace_handler::IntConversionError;
use air_trace_handler::TraceHandlerError;

use strum::IntoEnumIterator;
Expand All @@ -47,6 +48,10 @@ pub enum UncatchableError {
#[error(transparent)]
GenerationCompatificationError(#[from] GenerationCompatificationError),

/// Integer casts, e.g. usize(=u64) to u32, might trigger such errors.
#[error(transparent)]
IntConversionError(#[from] IntConversionError),

/// Fold state wasn't found for such iterator name.
#[error("fold state not found for this iterable '{0}'")]
FoldStateNotFound(String),
Expand Down
60 changes: 47 additions & 13 deletions air/src/execution_step/instructions/ap/apply_to_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

use super::*;
use crate::execution_step::PEEK_ALLOWED_ON_NON_EMPTY;
use crate::ExecutionError;
use crate::UncatchableError;

use air_lambda_parser::LambdaAST;
use air_parser::ast;
use air_trace_handler::IntConversionError;

pub(super) fn apply_to_arg(
argument: &ast::ApArgument<'_>,
Expand All @@ -30,28 +33,37 @@ pub(super) fn apply_to_arg(

let result = match argument {
InitPeerId => apply_const(exec_ctx.run_parameters.init_peer_id.as_str(), exec_ctx, trace_ctx),
LastError(error_accessor) => apply_last_error(error_accessor, exec_ctx, trace_ctx)?,
LastError(error_accessor) => apply_last_error(error_accessor, exec_ctx, trace_ctx),
Literal(value) => apply_const(*value, exec_ctx, trace_ctx),
Timestamp => apply_const(exec_ctx.run_parameters.timestamp, exec_ctx, trace_ctx),
TTL => apply_const(exec_ctx.run_parameters.ttl, exec_ctx, trace_ctx),
Number(value) => apply_const(value, exec_ctx, trace_ctx),
Boolean(value) => apply_const(*value, exec_ctx, trace_ctx),
EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx),
Scalar(scalar) => apply_scalar(scalar, exec_ctx, trace_ctx, should_touch_trace)?,
ScalarWithLambda(scalar) => apply_scalar_wl(scalar, exec_ctx, trace_ctx)?,
CanonStream(canon_stream) => apply_canon_stream(canon_stream, exec_ctx, trace_ctx)?,
CanonStreamWithLambda(canon_stream) => apply_canon_stream_wl(canon_stream, exec_ctx, trace_ctx)?,
};
Scalar(scalar) => apply_scalar(scalar, exec_ctx, trace_ctx, should_touch_trace),
ScalarWithLambda(scalar) => apply_scalar_wl(scalar, exec_ctx, trace_ctx),
CanonStream(canon_stream) => apply_canon_stream(canon_stream, exec_ctx, trace_ctx),
CanonStreamWithLambda(canon_stream) => apply_canon_stream_wl(canon_stream, exec_ctx, trace_ctx),
}?;

Ok(result)
}

fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ValueAggregate {
fn apply_const(
value: impl Into<JValue>,
exec_ctx: &ExecutionCtx<'_>,
trace_ctx: &TraceHandler,
) -> ExecutionResult<ValueAggregate> {
let value = Rc::new(value.into());
let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.run_parameters.init_peer_id.as_ref());
let tetraplet = Rc::new(tetraplet);
let position = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;

ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos())
Ok(ValueAggregate::new(value, tetraplet, position))
}

fn apply_last_error<'i>(
Expand All @@ -63,8 +75,13 @@ fn apply_last_error<'i>(
let value = Rc::new(value);
// removing is safe because prepare_last_error always returns a vec with one element.
let tetraplet = tetraplets.remove(0);
let position = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;

let result = ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos());
let result = ValueAggregate::new(value, tetraplet, position);
Ok(result)
}

Expand All @@ -87,7 +104,11 @@ fn apply_scalar(
};

if should_touch_trace {
result.trace_pos = trace_ctx.trace_pos();
result.trace_pos = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;
}

Ok(result)
Expand All @@ -101,7 +122,12 @@ fn apply_scalar_wl(
let variable = Variable::scalar(ast_scalar.name);
let (jvalue, tetraplet) = apply_lambda(variable, &ast_scalar.lambda, exec_ctx)?;
let tetraplet = Rc::new(tetraplet);
let result = ValueAggregate::new(Rc::new(jvalue), tetraplet, trace_ctx.trace_pos());
let position = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;
let result = ValueAggregate::new(Rc::new(jvalue), tetraplet, position);

Ok(result)
}
Expand All @@ -117,7 +143,11 @@ fn apply_canon_stream(
let canon_stream = exec_ctx.scalars.get_canon_stream(ast_stream.name)?;
let value = JValuable::as_jvalue(&canon_stream).into_owned();
let tetraplet = canon_stream.tetraplet().clone();
let position = trace_ctx.trace_pos();
let position = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;
let value = ValueAggregate::new(Rc::new(value), tetraplet, position);
Ok(value)
}
Expand All @@ -132,7 +162,11 @@ fn apply_canon_stream_wl(

let canon_stream = exec_ctx.scalars.get_canon_stream(ast_stream.name)?;
let (result, tetraplet) = JValuable::apply_lambda_with_tetraplets(&canon_stream, &ast_stream.lambda, exec_ctx)?;
let position = trace_ctx.trace_pos();
let position = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;
let value = ValueAggregate::new(Rc::new(result.into_owned()), Rc::new(tetraplet), position);
Ok(value)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use air_interpreter_data::Sender;
use air_interpreter_interface::CallServiceResult;
use air_parser::ast::CallOutputValue;
use air_trace_handler::merger::MetCallResult;
use air_trace_handler::IntConversionError;
use air_trace_handler::TraceHandler;

use fstrings::f;
Expand Down Expand Up @@ -141,7 +142,11 @@ fn update_state_with_service_result<'i>(
// try to get service result from call service result
let result = try_to_service_result(service_result, &argument_hash, &tetraplet, exec_ctx, trace_ctx)?;

let trace_pos = trace_ctx.trace_pos();
let trace_pos = trace_ctx.trace_pos().map_err(|e| {
ExecutionError::Uncatchable(UncatchableError::IntConversionError(
IntConversionError::TryIntoTracePosError(e),
))
})?;

let executed_result = ValueAggregate::new(result, tetraplet.clone(), trace_pos);
let new_call_result =
Expand Down
12 changes: 10 additions & 2 deletions air/tests/test_module/features/streams/streams_early_exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use air_trace_handler::TraceHandlerError;

use pretty_assertions::assert_eq;

use std::convert::TryInto;

#[test]
fn par_early_exit() {
let init_peer_id = "init_peer_id";
Expand Down Expand Up @@ -287,10 +289,16 @@ fn fold_early_exit() {
let expected_state = unused!(error_value.clone(), peer = last_peer_checker_id, args = [error_value]);

let bubbled_error_from_stream_1 = actual_trace.len() - 3;
assert_eq!(&actual_trace[bubbled_error_from_stream_1.into()], &expected_state);
assert_eq!(
&actual_trace[bubbled_error_from_stream_1.try_into().unwrap()],
&expected_state
);

let bubbled_error_from_stream_2 = actual_trace.len() - 2;
assert_eq!(&actual_trace[bubbled_error_from_stream_2.into()], &expected_state);
assert_eq!(
&actual_trace[bubbled_error_from_stream_2.try_into().unwrap()],
&expected_state
);
}

#[test]
Expand Down
37 changes: 1 addition & 36 deletions air/tests/test_module/instructions/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
* limitations under the License.
*/

use air::{ExecutionCidState, UncatchableError};
use air_interpreter_data::{ExecutionTrace, ServiceResultAggregate};
use air::UncatchableError;
use air_test_utils::prelude::*;

// Check that %init_peer_id% alias works correctly (by comparing result with it and explicit peer id).
Expand Down Expand Up @@ -187,37 +186,3 @@ fn string_parameters() {
assert_eq!(actual_trace.len(), 2);
assert_eq!(actual_trace[1.into()], expected_state);
}

#[test]
fn test_invalid_call_service_failed() {
let peer_id = "init_peer_id";
let mut cid_state = ExecutionCidState::new();

// Craft an artificial incorrect error result
let value = json!("error");
let value_cid = cid_state.value_tracker.record_value(value).unwrap();
let tetraplet = SecurityTetraplet::literal_tetraplet(peer_id);
let tetraplet_cid = cid_state.tetraplet_tracker.record_value(tetraplet).unwrap();
let service_result_agg = ServiceResultAggregate {
value_cid,
argument_hash: "0000000000000".into(),
tetraplet_cid,
};
let service_result_agg_cid = cid_state
.service_result_agg_tracker
.record_value(service_result_agg)
.unwrap();

let trace = ExecutionTrace::from(vec![ExecutedState::Call(CallResult::Failed(service_result_agg_cid))]);
let data = raw_data_from_trace(trace, cid_state);

let mut vm = create_avm(unit_call_service(), peer_id);
let air = format!(r#"(call "{peer_id}" ("" "") [] var)"#);
let res = vm.call(&air, vec![], data, TestRunParameters::default()).unwrap();

assert_eq!(res.ret_code, 20011);
assert_eq!(
res.error_message,
"failed to deserialize to CallServiceFailed: invalid type: string \"error\", expected struct CallServiceFailed",
);
}
125 changes: 122 additions & 3 deletions air/tests/test_module/negative_tests/uncatchable_trace_related.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,79 @@ use air_trace_handler::merger::CallResultError;
use air_trace_handler::merger::CanonResultError;
use air_trace_handler::merger::FoldResultError;
use air_trace_handler::merger::MergeCtxType::Current;
use air_trace_handler::DataType::Previous;
use air_trace_handler::merger::MergeCtxType::Previous;
use air_trace_handler::merger::ResolvedFold;
use air_trace_handler::merger::ResolvedSubTraceDescs;
use air_trace_handler::KeeperError::*;
use air_trace_handler::StateFSMError::*;
use air_trace_handler::TraceHandlerError::KeeperError;
use air_trace_handler::TraceHandlerError::MergeError;
use air_trace_handler::TraceHandlerError::StateFSMError;
use maplit::hashmap;

#[test]
fn par_len_overflow() {
let vm_peer_id_1 = "vm_peer_id_1";
let mut peer_vm_1 = create_avm(unit_call_service(), vm_peer_id_1);

let script = format!(
r#"
(par
(ap 42 some)
(call "other" ("" "") [some] other)
)
"#
);

let error_left_pos_value = 1;
let error_right_pos_value = u32::MAX;
let trace = vec![
executed_state::par(error_left_pos_value, error_right_pos_value),
executed_state::request_sent_by(vm_peer_id_1),
];
let data = raw_data_from_trace(trace, <_>::default());
let result = call_vm!(peer_vm_1, <_>::default(), script, "", data);
let expected_error = UncatchableError::TraceError {
trace_error: StateFSMError(ParLenOverflow(ParResult::new(
error_left_pos_value,
error_right_pos_value,
))),
instruction: "par".to_string(),
};
assert!(check_error(&result, expected_error));
}

#[test]
fn par_pos_overflow() {
let vm_peer_id_1 = "vm_peer_id_1";
let mut peer_vm_1 = create_avm(unit_call_service(), vm_peer_id_1);

let script = format!(
r#"
(par
(ap 42 some)
(call "other" ("" "") [some] other)
)
"#
);

let error_pos_value = u32::MAX;
let trace = vec![
executed_state::par(error_pos_value, error_pos_value),
executed_state::request_sent_by(vm_peer_id_1),
];
let data = raw_data_from_trace(trace, <_>::default());
let result = call_vm!(peer_vm_1, <_>::default(), script, "", data);
let expected_error = UncatchableError::TraceError {
trace_error: StateFSMError(ParPosOverflow(
ParResult::new(error_pos_value, error_pos_value),
1.into(),
Previous,
)),
instruction: "par".to_string(),
};
assert!(check_error(&result, expected_error));
}

#[test]
fn par_len_underflow() {
Expand Down Expand Up @@ -227,7 +294,7 @@ fn different_executed_state_expected() {
let expected_error = UncatchableError::TraceError {
trace_error: MergeError(air_trace_handler::merger::MergeError::DifferentExecutedStateExpected(
wrong_state,
Previous,
air_trace_handler::DataType::Previous,
"call",
)),
instruction: String::from(r#"call "vm_peer_id_2" ("" "") [] $s"#),
Expand Down Expand Up @@ -399,7 +466,6 @@ fn several_records_with_same_pos() {
];
let wrong_data = raw_data_from_trace(trace, cid_state);
let result = call_vm!(peer_vm_1, <_>::default(), &script, wrong_data, "");
// let result = peer_vm_1.call(script, wrong_data, "", <_>::default()).unwrap();
let fold_lore = FoldSubTraceLore {
value_pos: value_pos.into(),
subtraces_desc: vec![
Expand Down Expand Up @@ -467,3 +533,56 @@ fn values_not_equal() {
};
assert!(check_error(&result, expected_error));
}

#[test]
fn fold_pos_overflow() {
let vm_peer_id_1 = "vm_peer_id_1";
let mut peer_vm_1 = create_avm(unit_call_service(), vm_peer_id_1);
let script = format!(
r#"
(par
(call "vm_peer_id_1" ("" "") [] $s)
(fold $s i
(call "vm_peer_id_2" ("" "") [] a)
(next i)
)
)
"#
);
let mut cid_state = ExecutionCidState::new();
let value_pos = 1;
let before_subtrace_pos = 3;
let after_subtrace_pos = 4;
let wrong_after_subtrace_len = u32::MAX - 1;
let trace = vec![
executed_state::par(1, 2),
stream_tracked!(json!([42, 43]), 0, cid_state),
fold(vec![subtrace_lore(
value_pos,
subtrace_desc(before_subtrace_pos, 1),
subtrace_desc(after_subtrace_pos, wrong_after_subtrace_len),
)]),
request_sent_by("vm_peer_id_1"),
];
let wrong_data = raw_data_from_trace(trace, cid_state);
let result = call_vm!(peer_vm_1, <_>::default(), &script, wrong_data, "");
let fold_lore = ResolvedSubTraceDescs::new(
SubTraceDesc {
begin_pos: before_subtrace_pos.into(),
subtrace_len: wrong_after_subtrace_len + 1,
},
SubTraceDesc {
begin_pos: after_subtrace_pos.into(),
subtrace_len: wrong_after_subtrace_len,
},
);
let resolved_fold = ResolvedFold::new(
hashmap![value_pos.into() => fold_lore],
(wrong_after_subtrace_len + 1) as usize,
);
let expected_error = UncatchableError::TraceError {
trace_error: StateFSMError(FoldPosOverflow(resolved_fold, before_subtrace_pos.into(), Previous)),
instruction: String::from(String::from("fold $s i")),
};
assert!(check_error(&result, expected_error));
}
Loading

0 comments on commit 52b79a8

Please sign in to comment.