introduce farewell step; reafactoring

This commit is contained in:
vms 2021-12-17 22:02:16 +03:00
parent 8dbae91bda
commit 54e383cdaf
26 changed files with 233 additions and 111 deletions

13
Cargo.lock generated
View File

@ -25,6 +25,7 @@ dependencies = [
"air-test-utils",
"air-trace-handler",
"boolinator",
"concat-idents",
"criterion",
"csv",
"env_logger",
@ -73,7 +74,7 @@ dependencies = [
[[package]]
name = "air-interpreter-interface"
version = "0.7.0"
version = "0.8.0"
dependencies = [
"fluence-it-types",
"marine-rs-sdk",
@ -408,6 +409,16 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "concat-idents"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b6f90860248d75014b7b103db8fee4f291c07bfb41306cdf77a0a5ab7a10d2f"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"

View File

@ -30,6 +30,7 @@ serde = { version = "1.0.118", features = [ "derive", "rc" ] }
serde_json = "1.0.61"
boolinator = "2.4.0"
concat-idents = "1.1.3"
maplit = "1.0.2"
log = "0.4.11"
thiserror = "1.0.23"

View File

@ -22,7 +22,7 @@ use super::ExecutionResult;
use super::TraceHandler;
use crate::execution_step::air::ValueAggregate;
use crate::execution_step::boxed_value::Variable;
use crate::execution_step::utils::apply_lambda;
use crate::execution_step::resolver::apply_lambda;
use crate::trace_to_exec_err;
use crate::JValue;
use crate::SecurityTetraplet;

View File

@ -52,7 +52,7 @@ fn apply_last_error(
exec_ctx: &ExecutionCtx<'_>,
trace_ctx: &TraceHandler,
) -> ExecutionResult<ValueAggregate> {
let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?;
let (value, mut tetraplets) = crate::execution_step::resolver::prepare_last_error(error_path, exec_ctx)?;
let value = Rc::new(value);
// removing is safe because prepare_last_error always returns a vec with one element.
let tetraplet = tetraplets.remove(0);

View File

@ -149,7 +149,7 @@ impl<'i> ResolvedCall<'i> {
/// Prepare arguments of this call instruction by resolving and preparing their security tetraplets.
fn resolve_args(&self, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedArguments> {
use crate::execution_step::utils::resolve_to_args;
use crate::execution_step::resolver::resolve_to_args;
let function_args = self.function_arg_paths.iter();
let mut call_arguments = Vec::new();

View File

@ -45,7 +45,7 @@ pub(crate) fn resolve<'i>(triplet: &ast::Triplet<'i>, ctx: &ExecutionCtx<'i>) ->
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
// TODO: return Rc<String> to avoid excess cloning
fn resolve_to_string<'i>(value: &ast::CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
use crate::execution_step::utils::resolve_ast_variable_wl;
use crate::execution_step::resolver::resolve_ast_variable_wl;
use ast::CallInstrValue::*;
let resolved = match value {

View File

@ -16,8 +16,8 @@
use crate::execution_step::air::ExecutionResult;
use crate::execution_step::execution_context::ExecutionCtx;
use crate::execution_step::utils::prepare_last_error;
use crate::execution_step::utils::resolve_ast_variable_wl;
use crate::execution_step::resolver::prepare_last_error;
use crate::execution_step::resolver::resolve_ast_variable_wl;
use crate::JValue;
use air_parser::ast;

View File

@ -14,10 +14,11 @@
* limitations under the License.
*/
use super::ExecutionError;
use super::ExecutionError::LambdaApplierError;
use super::ExecutionResult;
use super::JValuable;
use super::LambdaAST;
use super::LambdaError::EmptyStream;
use crate::exec_err;
use crate::execution_step::ExecutionCtx;
use crate::execution_step::RSecurityTetraplet;
@ -29,7 +30,7 @@ use std::borrow::Cow;
impl JValuable for () {
fn apply_lambda<'i>(&self, _lambda: &LambdaAST<'_>, _exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
// applying lambda to an empty stream will produce a join behaviour
exec_err!(ExecutionError::EmptyStreamLambdaError)
exec_err!(LambdaApplierError(EmptyStream))
}
fn apply_lambda_with_tetraplets<'i>(
@ -38,7 +39,7 @@ impl JValuable for () {
_exec_ctx: &ExecutionCtx<'i>,
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
// applying lambda to an empty stream will produce a join behaviour
exec_err!(ExecutionError::EmptyStreamLambdaError)
exec_err!(LambdaApplierError(EmptyStream))
}
fn as_jvalue(&self) -> Cow<'_, JValue> {

View File

@ -23,8 +23,8 @@ pub(crate) use joinable::Joinable;
use super::Stream;
use crate::execution_step::lambda_applier::LambdaError;
use crate::JValue;
use crate::ToErrorCode;
use air_interpreter_interface::CallResults;
use air_trace_handler::MergerApResult;
use air_trace_handler::TraceHandlerError;
use strum::IntoEnumIterator;
@ -54,10 +54,6 @@ pub(crate) enum ExecutionError {
#[error(transparent)]
LambdaApplierError(#[from] LambdaError),
/// An error occurred while trying to apply lambda to an empty stream.
#[error("lambda is applied to an empty stream")]
EmptyStreamLambdaError,
/// Provided JValue has incompatible type with a requested one.
#[error(
"expected JValue type '{expected_value_type}' for the variable `{variable_name}`, but got '{actual_value}'"
@ -108,12 +104,6 @@ pub(crate) enum ExecutionError {
/// could be applied to a stream, but result doesn't contain generation in a source position.
#[error("ap result {0:?} doesn't match corresponding instruction")]
ApResultNotCorrespondToInstr(MergerApResult),
/// Call results should be empty at the end of execution thanks to a execution invariant.
#[error(
"after finishing execution of supplied AIR, call results aren't empty: `{0:?}`, probably wrong call_id used"
)]
CallResultsNotEmpty(CallResults),
}
impl From<LambdaError> for Rc<ExecutionError> {
@ -131,15 +121,31 @@ macro_rules! trace_to_exec_err {
};
}
impl ExecutionError {
pub(crate) fn to_error_code(&self) -> u32 {
const EXECUTION_ERRORS_START_ID: u32 = 1000;
/*
impl ToErrorCode for ExecutionError {
fn to_error_code(&self) -> i64 {
const EXECUTION_ERRORS_START_ID: i64 = 1000;
let mut errors = ExecutionErrorDiscriminants::iter();
let actual_error_type = ExecutionErrorDiscriminants::from(self);
// unwrap is safe here because errors are guaranteed to contain all errors variants
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as u32;
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
EXECUTION_ERRORS_START_ID + enum_variant_position
}
}
*/
impl ToErrorCode for Rc<ExecutionError> {
fn to_error_code(&self) -> i64 {
const EXECUTION_ERRORS_START_ID: i64 = 1000;
let mut errors = ExecutionErrorDiscriminants::iter();
let actual_error_type = ExecutionErrorDiscriminants::from(self.as_ref());
// unwrap is safe here because errors are guaranteed to contain all errors variants
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
EXECUTION_ERRORS_START_ID + enum_variant_position
}
}
@ -166,7 +172,7 @@ impl Joinable for ExecutionError {
log_join!(" waiting for an argument with idx '{}' on stream with size '{}'", idx, stream_size);
true
}
EmptyStreamLambdaError => {
LambdaApplierError(LambdaError::EmptyStream) => {
log_join!(" waiting on empty stream for path ");
true
}

View File

@ -23,6 +23,10 @@ pub(crate) enum LambdaError {
#[error("lambda is applied to a stream that have only '{stream_size}' elements, but '{idx}' requested")]
StreamNotHaveEnoughValues { stream_size: usize, idx: u32 },
/// An error occurred while trying to apply lambda to an empty stream.
#[error("lambda is applied to an empty stream")]
EmptyStream,
#[error("field accessor (with field name = '{field_name}') can't be applied to a stream")]
FieldAccessorAppliedToStream { field_name: String },

View File

@ -19,7 +19,7 @@ mod boxed_value;
mod errors;
pub(crate) mod execution_context;
mod lambda_applier;
mod utils;
mod resolver;
pub(super) use self::air::ExecutableInstruction;
pub(super) use self::air::FoldState;

View File

@ -0,0 +1,47 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ToErrorCode;
use air_interpreter_interface::CallResults;
use strum::IntoEnumIterator;
use strum_macros::EnumDiscriminants;
use strum_macros::EnumIter;
use thiserror::Error as ThisError;
/// Errors happened during the interpreter farewell step.
#[derive(Debug, EnumDiscriminants, ThisError)]
#[strum_discriminants(derive(EnumIter))]
pub enum FarewellError {
/// Call results should be empty at the end of execution thanks to a execution invariant.
#[error(
"after finishing execution of supplied AIR, call results aren't empty: `{0:?}`, probably wrong call_id used"
)]
CallResultsNotEmpty(CallResults),
}
impl ToErrorCode for FarewellError {
fn to_error_code(&self) -> i64 {
const FAREWELL_ERRORS_START_ID: i64 = 20000;
let mut errors = FarewellErrorDiscriminants::iter();
let actual_error_type = FarewellErrorDiscriminants::from(self);
// unwrap is safe here because errors are guaranteed to contain all errors variants
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
FAREWELL_ERRORS_START_ID + enum_variant_position
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
mod errors;
mod outcome;
pub(crate) use errors::FarewellError;
pub(crate) use outcome::from_execution_error;
pub(crate) use outcome::from_success_result;
pub(crate) use outcome::from_uncatchable_error;

View File

@ -14,11 +14,11 @@
* limitations under the License.
*/
use super::FarewellError;
use crate::execution_step::ExecutionCtx;
use crate::execution_step::ExecutionError;
use crate::execution_step::TraceHandler;
use crate::preparation_step::PreparationError;
use crate::InterpreterOutcome;
use crate::ToErrorCode;
use crate::INTERPRETER_SUCCESS;
use air_interpreter_data::InterpreterData;
@ -29,53 +29,34 @@ use std::rc::Rc;
/// Create InterpreterOutcome from supplied execution context and trace handler,
/// set ret_code to INTERPRETER_SUCCESS.
pub(crate) fn from_success_result(exec_ctx: ExecutionCtx<'_>, trace_handler: TraceHandler) -> InterpreterOutcome {
let (global_streams, restricted_streams) = exec_ctx.streams.into_streams_data();
let data = InterpreterData::from_execution_result(
trace_handler.into_result_trace(),
global_streams,
restricted_streams,
exec_ctx.last_call_request_id,
);
let data = serde_json::to_vec(&data).expect("default serializer shouldn't fail");
let next_peer_pks = dedup(exec_ctx.next_peer_pks);
let call_requests = serde_json::to_vec(&exec_ctx.call_requests).expect("default serializer shouldn't fail");
pub(crate) fn from_success_result(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
) -> Result<InterpreterOutcome, InterpreterOutcome> {
let (ret_code, error_message) = if exec_ctx.call_results.is_empty() {
(INTERPRETER_SUCCESS, String::new())
} else {
let farewell_error = Rc::new(FarewellError::CallResultsNotEmpty(exec_ctx.call_results.clone()));
(farewell_error.to_error_code(), farewell_error.to_string())
};
InterpreterOutcome {
ret_code: INTERPRETER_SUCCESS,
error_message: String::new(),
data,
next_peer_pks,
call_requests,
}
let outcome = populate_outcome_from_contexts(exec_ctx, trace_handler, ret_code, error_message);
Ok(outcome)
}
/// Create InterpreterOutcome from supplied data and error,
/// set ret_code based on the error.
pub(crate) fn from_preparation_error(data: impl Into<Vec<u8>>, err: PreparationError) -> InterpreterOutcome {
let ret_code = err.to_error_code() as i32;
pub(crate) fn from_uncatchable_error(
data: impl Into<Vec<u8>>,
error: impl ToErrorCode + ToString,
) -> InterpreterOutcome {
let ret_code = error.to_error_code();
let data = data.into();
let call_requests = serde_json::to_vec(&CallRequests::new()).expect("default serializer shouldn't fail");
InterpreterOutcome {
ret_code,
error_message: format!("{}", err),
data,
next_peer_pks: vec![],
call_requests,
}
}
/// Create InterpreterOutcome from supplied data and error,
/// set ret_code based on the error.
pub(crate) fn from_trace_error(data: impl Into<Vec<u8>>, err: Rc<ExecutionError>) -> InterpreterOutcome {
let ret_code = err.to_error_code() as i32;
let data = data.into();
let call_requests = serde_json::to_vec(&CallRequests::new()).expect("default serializer shouldn't fail");
InterpreterOutcome {
ret_code,
error_message: format!("{}", err),
error_message: error.to_string(),
data,
next_peer_pks: vec![],
call_requests,
@ -87,10 +68,17 @@ pub(crate) fn from_trace_error(data: impl Into<Vec<u8>>, err: Rc<ExecutionError>
pub(crate) fn from_execution_error(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
err: Rc<ExecutionError>,
error: impl ToErrorCode + ToString,
) -> InterpreterOutcome {
let ret_code = err.to_error_code() as i32;
populate_outcome_from_contexts(exec_ctx, trace_handler, error.to_error_code(), error.to_string())
}
fn populate_outcome_from_contexts(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
ret_code: i64,
error_message: String,
) -> InterpreterOutcome {
let (global_streams, restricted_streams) = exec_ctx.streams.into_streams_data();
let data = InterpreterData::from_execution_result(
trace_handler.into_result_trace(),
@ -104,7 +92,7 @@ pub(crate) fn from_execution_error(
InterpreterOutcome {
ret_code,
error_message: format!("{}", err),
error_message,
data,
next_peer_pks,
call_requests,

View File

@ -27,8 +27,10 @@
)]
mod execution_step;
mod farewell_step;
mod preparation_step;
mod runner;
mod utils;
pub use air_interpreter_interface::InterpreterOutcome;
pub use air_interpreter_interface::RunParameters;
@ -53,4 +55,6 @@ pub mod parser {
}
pub(crate) type JValue = serde_json::Value;
pub(crate) use utils::ToErrorCode;
use air_lambda_parser::LambdaAST;

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
use crate::ToErrorCode;
use air_interpreter_data::DATA_FORMAT_VERSION;
use serde_json::Error as SerdeJsonError;
@ -22,7 +23,7 @@ use strum_macros::EnumDiscriminants;
use strum_macros::EnumIter;
use thiserror::Error as ThisError;
/// Errors happened during the interpreter preparation_step step.
/// Errors happened during the interpreter preparation step.
#[derive(Debug, EnumDiscriminants, ThisError)]
#[strum_discriminants(derive(EnumIter))]
pub enum PreparationError {
@ -40,15 +41,15 @@ pub enum PreparationError {
CallResultsDeFailed(SerdeJsonError, Vec<u8>),
}
impl PreparationError {
pub(crate) fn to_error_code(&self) -> u32 {
const PREPARATION_ERRORS_START_ID: u32 = 1;
impl ToErrorCode for PreparationError {
fn to_error_code(&self) -> i64 {
const PREPARATION_ERRORS_START_ID: i64 = 1;
let mut errors = PreparationErrorDiscriminants::iter();
let actual_error_type = PreparationErrorDiscriminants::from(self);
// unwrap is safe here because errors are guaranteed to contain all errors variants
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as u32;
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
PREPARATION_ERRORS_START_ID + enum_variant_position
}
}

View File

@ -14,13 +14,9 @@
* limitations under the License.
*/
mod outcome;
use crate::execution_step::Catchable;
use crate::execution_step::ExecutableInstruction;
use crate::execution_step::ExecutionCtx;
use crate::execution_step::ExecutionError;
use crate::execution_step::TraceHandler;
use crate::farewell_step as farewell;
use crate::preparation_step::prepare;
use crate::preparation_step::PreparationDescriptor;
@ -28,8 +24,6 @@ use air_interpreter_interface::InterpreterOutcome;
use air_interpreter_interface::RunParameters;
use air_log_targets::RUN_PARAMS;
use std::rc::Rc;
pub fn execute_air(
air: String,
prev_data: Vec<u8>,
@ -66,30 +60,16 @@ fn execute_air_impl(
} = match prepare(&prev_data, &data, air.as_str(), &call_results, params) {
Ok(desc) => desc,
// return the initial data in case of errors
Err(error) => return Err(outcome::from_preparation_error(prev_data, error)),
Err(error) => return Err(farewell::from_uncatchable_error(prev_data, error)),
};
// match here is used instead of map_err, because the compiler can't determine that
// they are exclusive and would treat exec_ctx and trace_handler as moved
match air.execute(&mut exec_ctx, &mut trace_handler) {
Ok(_) => try_make_outcome(exec_ctx, trace_handler),
// return the old data in case of any trace errors
Err(e) if !e.is_catchable() => Err(outcome::from_trace_error(prev_data, e)),
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler),
// return new collected trace in case of errors
Err(e) => Err(outcome::from_execution_error(exec_ctx, trace_handler, e)),
Err(error) if error.is_catchable() => Err(farewell::from_execution_error(exec_ctx, trace_handler, error)),
// return the old data in case of any trace errors
Err(error) => Err(farewell::from_uncatchable_error(prev_data, error)),
}
}
fn try_make_outcome(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
) -> Result<InterpreterOutcome, InterpreterOutcome> {
if exec_ctx.call_results.is_empty() {
let outcome = outcome::from_success_result(exec_ctx, trace_handler);
return Ok(outcome);
}
let exec_error = Rc::new(ExecutionError::CallResultsNotEmpty(exec_ctx.call_results.clone()));
let outcome = outcome::from_execution_error(exec_ctx, trace_handler, exec_error);
Err(outcome)
}

19
air/src/utils/mod.rs Normal file
View File

@ -0,0 +1,19 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
mod to_error_code;
pub(crate) use to_error_code::ToErrorCode;

View File

@ -0,0 +1,37 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pub(crate) trait ToErrorCode {
fn to_error_code(&self) -> i64;
}
/*
use concat_idents::concat_idents;
#[macro_export]
macro_rules! generate_to_error_code {
($error_type:ident, $start_id: ident) => {
const PREPARATION_ERRORS_START_ID: u32 = $start_id;
let mut errors = PreparationErrorDiscriminants::iter();
let actual_error_type = PreparationErrorDiscriminants::from(self);
// unwrap is safe here because errors are guaranteed to contain all errors variants
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
PREPARATION_ERRORS_START_ID + enum_variant_position
}
}
*/

View File

@ -16,7 +16,7 @@ name = "avm_server"
path = "src/lib.rs"
[dependencies]
air-interpreter-interface = { version = "0.7.0", path = "../../crates/air-lib/interpreter-interface" }
air-interpreter-interface = { version = "0.8.0", path = "../../crates/air-lib/interpreter-interface" }
avm-data-store = { version = "0.1.0", path = "../../crates/data-store" }
fluence-faas = "0.10.0"
polyplets = { version = "0.2.0", path = "../../crates/air-lib/polyplets" }

View File

@ -37,7 +37,7 @@ pub struct AVMOutcome {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ErrorAVMOutcome {
pub error_code: i32,
pub error_code: i64,
pub error_message: String,
pub outcome: AVMOutcome,
}
@ -78,7 +78,7 @@ impl AVMOutcome {
}
impl ErrorAVMOutcome {
pub(self) fn new(error_code: i32, error_msg: String, outcome: AVMOutcome) -> Self {
pub(self) fn new(error_code: i64, error_msg: String, outcome: AVMOutcome) -> Self {
Self {
error_code,
error_message: error_msg,

View File

@ -25,7 +25,7 @@ use serde::Serialize;
/// This struct is very similar to AVMOutcome, but keeps error_code and error_msg for test purposes.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RawAVMOutcome {
pub ret_code: i32,
pub ret_code: i64,
pub error_message: String,
pub data: Vec<u8>,
pub call_requests: CallRequests,

View File

@ -1,7 +1,7 @@
[package]
name = "air-interpreter-interface"
description = "Interface of the AIR interpreter"
version = "0.7.0"
version = "0.8.0"
authors = ["Fluence Labs"]
edition = "2018"
license = "Apache-2.0"

View File

@ -20,14 +20,14 @@ use fluence_it_types::IValue;
use serde::Deserialize;
use serde::Serialize;
pub const INTERPRETER_SUCCESS: i32 = 0;
pub const INTERPRETER_SUCCESS: i64 = 0;
/// Describes a result returned at the end of the interpreter execution_step.
#[marine]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InterpreterOutcome {
/// A return code, where INTERPRETER_SUCCESS means success.
pub ret_code: i32,
pub ret_code: i64,
/// Contains error message if ret_code != INTERPRETER_SUCCESS.
pub error_message: String,
@ -59,7 +59,7 @@ impl InterpreterOutcome {
let next_peer_pks = try_as_string_vec(record_values.pop().unwrap(), "next_peer_pks")?;
let data = try_as_byte_vec(record_values.pop().unwrap(), "data")?;
let error_message = try_as_string(record_values.pop().unwrap(), "error_message")?;
let ret_code = try_as_i32(record_values.pop().unwrap(), "ret_code")?;
let ret_code = try_as_i64(record_values.pop().unwrap(), "ret_code")?;
let outcome = Self {
ret_code,
@ -87,10 +87,10 @@ fn try_as_record(ivalue: IValue) -> Result<NEVec<IValue>, String> {
}
}
fn try_as_i32(ivalue: IValue, field_name: &str) -> Result<i32, String> {
fn try_as_i64(ivalue: IValue, field_name: &str) -> Result<i64, String> {
match ivalue {
IValue::S32(value) => Ok(value),
v => return Err(format!("expected an i32 for {}, got {:?}", field_name, v)),
IValue::S64(value) => Ok(value),
v => return Err(format!("expected an i64 for {}, got {:?}", field_name, v)),
}
}