air_interpreter_interface/
interpreter_outcome.rs

1/*
2 * Copyright 2021 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#[cfg(feature = "marine")]
18use marine_rs_sdk::marine;
19
20#[cfg(feature = "marine")]
21use fluence_it_types::IValue;
22use serde::Deserialize;
23use serde::Serialize;
24
25pub const INTERPRETER_SUCCESS: i64 = 0;
26
27/// This stores soft limits triggering flags.
28#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct SoftLimitsTriggering {
30    pub air_size_limit_exceeded: bool,
31    pub particle_size_limit_exceeded: bool,
32    pub call_result_size_limit_exceeded: bool,
33}
34
35/// Describes a result returned at the end of the interpreter execution_step.
36#[cfg_attr(feature = "marine", marine)]
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct InterpreterOutcome {
39    /// A return code, where INTERPRETER_SUCCESS means success.
40    pub ret_code: i64,
41
42    /// Contains error message if ret_code != INTERPRETER_SUCCESS.
43    pub error_message: String,
44
45    /// Contains script data that should be preserved in an executor of this interpreter
46    /// regardless of ret_code value.
47    pub data: Vec<u8>,
48
49    /// Public keys of peers that should receive data.
50    pub next_peer_pks: Vec<String>,
51
52    /// Collected parameters of all met call instructions that could be executed on a current peer.
53    pub call_requests: Vec<u8>,
54
55    /// This flag signals that AIR script size exceeds the limit.
56    pub air_size_limit_exceeded: bool,
57
58    /// This flag signals that particle data size exceeds the limit.
59    pub particle_size_limit_exceeded: bool,
60
61    /// This flag signals that call result size exceeds the limit.
62    pub call_result_size_limit_exceeded: bool,
63}
64
65impl SoftLimitsTriggering {
66    pub fn new(
67        air_size_limit_exceeded: bool,
68        particle_size_limit_exceeded: bool,
69        call_result_size_limit_exceeded: bool,
70    ) -> Self {
71        Self {
72            air_size_limit_exceeded,
73            particle_size_limit_exceeded,
74            call_result_size_limit_exceeded,
75        }
76    }
77
78    pub fn are_limits_exceeded(&self) -> bool {
79        self.air_size_limit_exceeded
80            || self.particle_size_limit_exceeded
81            || self.call_result_size_limit_exceeded
82    }
83}
84
85impl InterpreterOutcome {
86    pub fn new(
87        ret_code: i64,
88        error_message: String,
89        data: Vec<u8>,
90        next_peer_pks: Vec<String>,
91        call_requests: SerializedCallRequests,
92        soft_limits_triggering: SoftLimitsTriggering,
93    ) -> Self {
94        let call_requests = call_requests.into();
95        Self {
96            ret_code,
97            error_message,
98            data,
99            next_peer_pks,
100            call_requests,
101            air_size_limit_exceeded: soft_limits_triggering.air_size_limit_exceeded,
102            particle_size_limit_exceeded: soft_limits_triggering.particle_size_limit_exceeded,
103            call_result_size_limit_exceeded: soft_limits_triggering.call_result_size_limit_exceeded,
104        }
105    }
106}
107
108#[cfg(feature = "marine")]
109impl InterpreterOutcome {
110    pub fn from_ivalue(ivalue: IValue) -> Result<Self, String> {
111        const OUTCOME_FIELDS_COUNT: usize = 8;
112
113        let mut record_values = try_as_record(ivalue)?.into_vec();
114        if record_values.len() != OUTCOME_FIELDS_COUNT {
115            return Err(format!(
116                "expected InterpreterOutcome struct with {OUTCOME_FIELDS_COUNT} fields, got {record_values:?}"
117            ));
118        }
119
120        let call_result_size_limit_exceeded = try_as_boolean(
121            record_values.pop().unwrap(),
122            "call_result_size_limit_exceeded",
123        )?;
124        let particle_size_limit_exceeded =
125            try_as_boolean(record_values.pop().unwrap(), "particle_size_limit_exceeded")?;
126        let air_size_limit_exceeded =
127            try_as_boolean(record_values.pop().unwrap(), "air_size_limit_exceeded")?;
128
129        let call_requests = try_as_byte_vec(record_values.pop().unwrap(), "call_requests")?;
130        let next_peer_pks = try_as_string_vec(record_values.pop().unwrap(), "next_peer_pks")?;
131        let data = try_as_byte_vec(record_values.pop().unwrap(), "data")?;
132        let error_message = try_as_string(record_values.pop().unwrap(), "error_message")?;
133        let ret_code = try_as_i64(record_values.pop().unwrap(), "ret_code")?;
134        let soft_limits_triggering = SoftLimitsTriggering::new(
135            air_size_limit_exceeded,
136            particle_size_limit_exceeded,
137            call_result_size_limit_exceeded,
138        );
139
140        let outcome = Self::new(
141            ret_code,
142            error_message,
143            data,
144            next_peer_pks,
145            call_requests.into(),
146            soft_limits_triggering,
147        );
148
149        Ok(outcome)
150    }
151}
152
153#[cfg(feature = "marine")]
154use fluence_it_types::ne_vec::NEVec;
155
156use crate::SerializedCallRequests;
157
158#[cfg(feature = "marine")]
159fn try_as_record(ivalue: IValue) -> Result<NEVec<IValue>, String> {
160    match ivalue {
161        IValue::Record(record_values) => Ok(record_values),
162        v => Err(format!("expected record for InterpreterOutcome, got {v:?}")),
163    }
164}
165
166#[cfg(feature = "marine")]
167fn try_as_i64(ivalue: IValue, field_name: &str) -> Result<i64, String> {
168    match ivalue {
169        IValue::S64(value) => Ok(value),
170        v => Err(format!("expected an i64 for {field_name}, got {v:?}")),
171    }
172}
173
174#[cfg(feature = "marine")]
175pub fn try_as_string(ivalue: IValue, field_name: &str) -> Result<String, String> {
176    match ivalue {
177        IValue::String(value) => Ok(value),
178        v => Err(format!("expected a string for {field_name}, got {v:?}")),
179    }
180}
181
182#[cfg(feature = "marine")]
183fn try_as_byte_vec(ivalue: IValue, field_name: &str) -> Result<Vec<u8>, String> {
184    let byte_vec = match ivalue {
185        IValue::Array(array) => {
186            let array: Result<Vec<_>, _> = array
187                .into_iter()
188                .map(|v| match v {
189                    IValue::U8(byte) => Ok(byte),
190                    v => Err(format!("expected a byte, got {v:?}")),
191                })
192                .collect();
193            array?
194        }
195        IValue::ByteArray(array) => array,
196        v => return Err(format!("expected a Vec<u8> for {field_name}, got {v:?}")),
197    };
198
199    Ok(byte_vec)
200}
201
202#[cfg(feature = "marine")]
203fn try_as_string_vec(ivalue: IValue, field_name: &str) -> Result<Vec<String>, String> {
204    match ivalue {
205        IValue::Array(ar_values) => {
206            let array = ar_values
207                .into_iter()
208                .map(|v| match v {
209                    IValue::String(str) => Ok(str),
210                    v => Err(format!("expected string for next_peer_pks, got {v:?}")),
211                })
212                .collect::<Result<Vec<String>, _>>()?;
213
214            Ok(array)
215        }
216        v => Err(format!("expected an array for {field_name}, got {v:?}")),
217    }
218}
219
220#[cfg(feature = "marine")]
221fn try_as_boolean(ivalue: IValue, field_name: &str) -> Result<bool, String> {
222    match ivalue {
223        IValue::Boolean(value) => Ok(value),
224        v => Err(format!("expected a bool for {field_name}, got {v:?}")),
225    }
226}