#![allow(unused_unsafe)] use super::call_result_setter::*;
use super::prev_result_handler::*;
use super::triplet::resolve;
use super::*;
use crate::execution_step::resolver::Resolvable;
use crate::execution_step::RcSecurityTetraplet;
use crate::execution_step::RcSecurityTetraplets;
use crate::execution_step::UncatchableError;
use crate::trace_to_exec_err;
use crate::JValue;
use crate::SecurityTetraplet;
use air_interpreter_cid::value_to_json_cid;
use air_interpreter_cid::CidRef;
use air_interpreter_data::CallResult;
use air_interpreter_interface::CallArgumentsRepr;
use air_interpreter_interface::CallRequestParams;
use air_interpreter_interface::SerializedCallArguments;
use air_interpreter_interface::TetrapletsRepr;
use air_parser::ast;
use air_trace_handler::merger::MergerCallResult;
use air_trace_handler::TraceHandler;
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq)]
pub(super) struct ResolvedCall<'i> {
tetraplet: RcSecurityTetraplet,
function_arg_paths: Rc<Vec<ast::ImmutableValue<'i>>>,
output: ast::CallOutputValue<'i>,
}
#[derive(Debug, Clone, PartialEq)]
struct ResolvedArguments {
call_arguments: SerializedCallArguments,
tetraplets: Vec<RcSecurityTetraplets>,
}
#[derive(Debug)]
enum CheckArgsResult<T> {
Ok(T),
Joinable(#[allow(dead_code)] ExecutionError),
}
impl<T> CheckArgsResult<T> {
fn new(result: ExecutionResult<T>) -> ExecutionResult<Self> {
match result {
Ok(nested) => Ok(CheckArgsResult::Ok(nested)),
Err(err) if err.is_joinable() => Ok(CheckArgsResult::Joinable(err)),
Err(err) => Err(err),
}
}
}
impl<'i> ResolvedCall<'i> {
#[tracing::instrument(level = "trace", skip_all)]
pub(super) fn new(raw_call: &Call<'i>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<Self> {
let triplet = resolve(&raw_call.triplet, exec_ctx)?;
let tetraplet = triplet.into();
let tetraplet = Rc::new(tetraplet);
check_output_name(&raw_call.output, exec_ctx)?;
Ok(Self {
tetraplet,
function_arg_paths: raw_call.args.clone(),
output: raw_call.output.clone(),
})
}
#[tracing::instrument(level = "trace", skip_all)]
pub(super) fn execute(
&self,
raw_call: &Call<'i>,
exec_ctx: &mut ExecutionCtx<'i>,
trace_ctx: &mut TraceHandler,
) -> ExecutionResult<()> {
let checked_args = match self.check_args(exec_ctx)? {
CheckArgsResult::Ok(args) => Some(args),
CheckArgsResult::Joinable(_) => None,
};
let argument_hash: Option<Rc<CidRef>> =
checked_args.map(|args| value_to_json_cid(&args).expect("serializer shouldn't fail").get_inner());
let state = self.prepare_current_executed_state(raw_call, argument_hash.as_ref(), exec_ctx, trace_ctx)?;
if !state.should_execute() {
state.maybe_set_prev_state(trace_ctx);
return Ok(());
}
let tetraplet = &self.tetraplet;
if tetraplet.peer_pk.as_str() != exec_ctx.run_parameters.current_peer_id.as_str() {
handle_remote_call(tetraplet.peer_pk.clone(), exec_ctx, trace_ctx);
return Ok(());
}
let request_params = match self.prepare_request_params(exec_ctx, tetraplet) {
Ok(params) => params,
Err(e) if e.is_joinable() => {
state.maybe_set_prev_state(trace_ctx);
return Err(e);
}
Err(e) => {
return Err(e);
}
};
let call_id = exec_ctx.next_call_request_id();
exec_ctx.call_requests.insert(call_id, request_params);
exec_ctx.make_subgraph_incomplete();
trace_ctx.meet_call_end(CallResult::sent_peer_id_with_call_id(
exec_ctx.run_parameters.current_peer_id.clone(),
call_id,
));
Ok(())
}
pub(super) fn as_tetraplet(&self) -> RcSecurityTetraplet {
self.tetraplet.clone()
}
#[tracing::instrument(level = "trace", skip_all)]
fn prepare_request_params(
&self,
exec_ctx: &ExecutionCtx<'_>,
tetraplet: &SecurityTetraplet,
) -> ExecutionResult<CallRequestParams> {
use air_interpreter_sede::ToSerialized;
let ResolvedArguments {
call_arguments,
tetraplets,
} = self.resolve_args(exec_ctx)?;
let serialized_tetraplets = TetrapletsRepr
.serialize(&tetraplets)
.map_err(UncatchableError::TetrapletSerializationFailed)?;
let request_params = CallRequestParams::new(
tetraplet.service_id.to_string(),
tetraplet.function_name.to_string(),
call_arguments,
serialized_tetraplets,
);
Ok(request_params)
}
fn prepare_current_executed_state(
&self,
raw_call: &Call<'i>,
argument_hash: Option<&Rc<str>>,
exec_ctx: &mut ExecutionCtx<'i>,
trace_ctx: &mut TraceHandler,
) -> ExecutionResult<StateDescriptor> {
let prev_result = trace_ctx.meet_call_start();
match trace_to_exec_err!(prev_result, raw_call)? {
MergerCallResult::Met(call_result) => handle_prev_state(
call_result,
&self.tetraplet,
argument_hash,
&self.output,
exec_ctx,
trace_ctx,
),
MergerCallResult::NotMet => Ok(StateDescriptor::no_previous_state()),
}
}
fn resolve_args(&self, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedArguments> {
use air_interpreter_sede::ToSerialized;
let (call_arguments, tetraplets) = self.collect_args(exec_ctx)?;
let call_arguments = CallArgumentsRepr
.serialize(&call_arguments)
.map_err(UncatchableError::CallArgumentsSerializationFailed)?;
let resolved_arguments = ResolvedArguments {
call_arguments,
tetraplets,
};
Ok(resolved_arguments)
}
fn check_args(&self, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<CheckArgsResult<Vec<JValue>>> {
let fun_result = self.collect_args(exec_ctx);
CheckArgsResult::new(fun_result.map(|values| values.0))
}
fn collect_args(&self, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<(Vec<JValue>, Vec<RcSecurityTetraplets>)> {
let function_args = self.function_arg_paths.iter();
let mut call_arguments = Vec::with_capacity(function_args.len());
let mut tetraplets = Vec::with_capacity(function_args.len());
for instruction_value in function_args {
let (arg, tetraplet, _) = instruction_value.resolve(exec_ctx)?;
call_arguments.push(arg);
tetraplets.push(tetraplet);
}
Ok((call_arguments, tetraplets))
}
}
fn check_output_name(output: &ast::CallOutputValue<'_>, exec_ctx: &ExecutionCtx<'_>) -> ExecutionResult<()> {
use crate::execution_step::value_types::ScalarRef;
let scalar_name = match output {
ast::CallOutputValue::Scalar(scalar) => scalar.name,
_ => return Ok(()),
};
match exec_ctx.scalars.get_value(scalar_name) {
Ok(ScalarRef::Value(_)) => {
if exec_ctx.scalars.variable_could_be_set(scalar_name) {
Ok(())
} else {
Err(UncatchableError::ShadowingIsNotAllowed(scalar_name.to_string()).into())
}
}
Ok(ScalarRef::IterableValue(_)) => Err(UncatchableError::IterableShadowing(scalar_name.to_string()).into()),
Err(_) => Ok(()),
}
}