Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(trace-handler): TracePos becomes a wrapper for u32 alias [fixes VM-267] #544

Merged
merged 1 commit into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
34 changes: 21 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,6 +16,7 @@

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

use air_lambda_parser::LambdaAST;
use air_parser::ast;
Expand All @@ -30,28 +31,33 @@ 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(UncatchableError::from)?;

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

fn apply_last_error<'i>(
Expand All @@ -63,8 +69,9 @@ 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(UncatchableError::from)?;

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

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

if should_touch_trace {
result.trace_pos = trace_ctx.trace_pos();
result.trace_pos = trace_ctx.trace_pos().map_err(UncatchableError::from)?;
}

Ok(result)
Expand All @@ -101,7 +108,8 @@ 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(UncatchableError::from)?;
let result = ValueAggregate::new(Rc::new(jvalue), tetraplet, position);

Ok(result)
}
Expand All @@ -117,7 +125,7 @@ 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(UncatchableError::from)?;
let value = ValueAggregate::new(Rc::new(value), tetraplet, position);
Ok(value)
}
Expand All @@ -132,7 +140,7 @@ 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(UncatchableError::from)?;
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 @@ -141,7 +141,7 @@ 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(UncatchableError::from)?;

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