near_vm_logic/
logic.rs

1use crate::context::VMContext;
2use crate::dependencies::{External, MemoryLike};
3use crate::gas_counter::GasCounter;
4use crate::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData};
5use crate::utils::split_method_names;
6use crate::ValuePtr;
7use byteorder::ByteOrder;
8use near_primitives_core::config::ExtCosts::*;
9use near_primitives_core::config::{ActionCosts, ExtCosts, VMConfig};
10use near_primitives_core::profile::ProfileData;
11use near_primitives_core::runtime::fees::RuntimeFeesConfig;
12use near_primitives_core::types::{
13    AccountId, Balance, EpochHeight, Gas, ProtocolVersion, StorageUsage,
14};
15use near_runtime_utils::is_account_id_64_len_hex;
16use near_vm_errors::InconsistentStateError;
17use near_vm_errors::{HostError, VMLogicError};
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20use std::mem::size_of;
21
22type Result<T> = ::std::result::Result<T, VMLogicError>;
23
24const LEGACY_DEFAULT_PROTOCOL_VERSION: ProtocolVersion = 34;
25const IMPLICIT_ACCOUNT_CREATION_PROTOCOL_VERSION: ProtocolVersion = 35;
26
27pub struct VMLogic<'a> {
28    /// Provides access to the components outside the Wasm runtime for operations on the trie and
29    /// receipts creation.
30    ext: &'a mut dyn External,
31    /// Part of Context API and Economics API that was extracted from the receipt.
32    context: VMContext,
33    /// Parameters of Wasm and economic parameters.
34    config: &'a VMConfig,
35    /// Fees for creating (async) actions on runtime.
36    fees_config: &'a RuntimeFeesConfig,
37    /// If this method execution is invoked directly as a callback by one or more contract calls the
38    /// results of the methods that made the callback are stored in this collection.
39    promise_results: &'a [PromiseResult],
40    /// Pointer to the guest memory.
41    memory: &'a mut dyn MemoryLike,
42
43    /// Keeping track of the current account balance, which can decrease when we create promises
44    /// and attach balance to them.
45    current_account_balance: Balance,
46    /// Current amount of locked tokens, does not automatically change when staking transaction is
47    /// issued.
48    current_account_locked_balance: Balance,
49    /// Storage usage of the current account at the moment
50    current_storage_usage: StorageUsage,
51    gas_counter: GasCounter,
52    /// What method returns.
53    return_data: ReturnData,
54    /// Logs written by the runtime.
55    logs: Vec<String>,
56    /// Registers can be used by the guest to store blobs of data without moving them across
57    /// host-guest boundary.
58    registers: HashMap<u64, Vec<u8>>,
59
60    /// The DAG of promises, indexed by promise id.
61    promises: Vec<Promise>,
62    /// Record the accounts towards which the receipts are directed.
63    receipt_to_account: HashMap<ReceiptIndex, AccountId>,
64
65    /// Tracks the total log length. The sum of length of all logs.
66    total_log_length: u64,
67
68    /// Current protocol version that is used for the function call.
69    current_protocol_version: ProtocolVersion,
70}
71
72/// Promises API allows to create a DAG-structure that defines dependencies between smart contract
73/// calls. A single promise can be created with zero or several dependencies on other promises.
74/// * If a promise was created from a receipt (using `promise_create` or `promise_then`) it's a
75///   `Receipt`;
76/// * If a promise was created by merging several promises (using `promise_and`) then
77///   it's a `NotReceipt`, but has receipts of all promises it depends on.
78#[derive(Debug)]
79enum Promise {
80    Receipt(ReceiptIndex),
81    NotReceipt(Vec<ReceiptIndex>),
82}
83
84macro_rules! memory_get {
85    ($_type:ty, $name:ident) => {
86        fn $name(&mut self, offset: u64) -> Result<$_type> {
87            let mut array = [0u8; size_of::<$_type>()];
88            self.memory_get_into(offset, &mut array)?;
89            Ok(<$_type>::from_le_bytes(array))
90        }
91    };
92}
93
94macro_rules! memory_set {
95    ($_type:ty, $name:ident) => {
96        fn $name(&mut self, offset: u64, value: $_type) -> Result<()> {
97            self.memory_set_slice(offset, &value.to_le_bytes())
98        }
99    };
100}
101
102impl<'a> VMLogic<'a> {
103    pub fn new_with_protocol_version(
104        ext: &'a mut dyn External,
105        context: VMContext,
106        config: &'a VMConfig,
107        fees_config: &'a RuntimeFeesConfig,
108        promise_results: &'a [PromiseResult],
109        memory: &'a mut dyn MemoryLike,
110        profile: Option<ProfileData>,
111        current_protocol_version: ProtocolVersion,
112    ) -> Self {
113        ext.reset_touched_nodes_counter();
114        // Overflow should be checked before calling VMLogic.
115        let current_account_balance = context.account_balance + context.attached_deposit;
116        let current_storage_usage = context.storage_usage;
117        let max_gas_burnt = if context.is_view {
118            config.limit_config.max_gas_burnt_view
119        } else {
120            config.limit_config.max_gas_burnt
121        };
122        let current_account_locked_balance = context.account_locked_balance;
123        let gas_counter = GasCounter::new(
124            config.ext_costs.clone(),
125            max_gas_burnt,
126            context.prepaid_gas,
127            context.is_view,
128            profile,
129        );
130        Self {
131            ext,
132            context,
133            config,
134            fees_config,
135            promise_results,
136            memory,
137            current_account_balance,
138            current_account_locked_balance,
139            current_storage_usage,
140            gas_counter,
141            return_data: ReturnData::None,
142            logs: vec![],
143            registers: HashMap::new(),
144            promises: vec![],
145            receipt_to_account: HashMap::new(),
146            total_log_length: 0,
147            current_protocol_version,
148        }
149    }
150
151    /// Legacy initialization method that doesn't pass the protocol version and uses the last
152    /// protocol version before the change was introduced.
153    pub fn new(
154        ext: &'a mut dyn External,
155        context: VMContext,
156        config: &'a VMConfig,
157        fees_config: &'a RuntimeFeesConfig,
158        promise_results: &'a [PromiseResult],
159        memory: &'a mut dyn MemoryLike,
160        profile: Option<ProfileData>,
161    ) -> Self {
162        Self::new_with_protocol_version(
163            ext,
164            context,
165            config,
166            fees_config,
167            promise_results,
168            memory,
169            profile,
170            LEGACY_DEFAULT_PROTOCOL_VERSION,
171        )
172    }
173
174    // ###########################
175    // # Memory helper functions #
176    // ###########################
177
178    fn try_fit_mem(&mut self, offset: u64, len: u64) -> Result<()> {
179        if self.memory.fits_memory(offset, len) {
180            Ok(())
181        } else {
182            Err(HostError::MemoryAccessViolation.into())
183        }
184    }
185
186    fn memory_get_into(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> {
187        self.gas_counter.pay_base(read_memory_base)?;
188        self.gas_counter.pay_per_byte(read_memory_byte, buf.len() as _)?;
189        self.try_fit_mem(offset, buf.len() as _)?;
190        self.memory.read_memory(offset, buf);
191        Ok(())
192    }
193
194    fn memory_get_vec(&mut self, offset: u64, len: u64) -> Result<Vec<u8>> {
195        self.gas_counter.pay_base(read_memory_base)?;
196        self.gas_counter.pay_per_byte(read_memory_byte, len)?;
197        self.try_fit_mem(offset, len)?;
198        let mut buf = vec![0; len as usize];
199        self.memory.read_memory(offset, &mut buf);
200        Ok(buf)
201    }
202
203    memory_get!(u128, memory_get_u128);
204    memory_get!(u32, memory_get_u32);
205    memory_get!(u16, memory_get_u16);
206    memory_get!(u8, memory_get_u8);
207
208    /// Reads an array of `u64` elements.
209    fn memory_get_vec_u64(&mut self, offset: u64, num_elements: u64) -> Result<Vec<u64>> {
210        let memory_len = num_elements
211            .checked_mul(size_of::<u64>() as u64)
212            .ok_or(HostError::MemoryAccessViolation)?;
213        let data = self.memory_get_vec(offset, memory_len)?;
214        let mut res = vec![0u64; num_elements as usize];
215        byteorder::LittleEndian::read_u64_into(&data, &mut res);
216        Ok(res)
217    }
218
219    fn get_vec_from_memory_or_register(&mut self, offset: u64, len: u64) -> Result<Vec<u8>> {
220        if len != std::u64::MAX {
221            self.memory_get_vec(offset, len)
222        } else {
223            self.internal_read_register(offset)
224        }
225    }
226
227    fn memory_set_slice(&mut self, offset: u64, buf: &[u8]) -> Result<()> {
228        self.gas_counter.pay_base(write_memory_base)?;
229        self.gas_counter.pay_per_byte(write_memory_byte, buf.len() as _)?;
230        self.try_fit_mem(offset, buf.len() as _)?;
231        self.memory.write_memory(offset, buf);
232        Ok(())
233    }
234
235    memory_set!(u128, memory_set_u128);
236
237    // #################
238    // # Registers API #
239    // #################
240
241    fn internal_read_register(&mut self, register_id: u64) -> Result<Vec<u8>> {
242        if let Some(data) = self.registers.get(&register_id) {
243            self.gas_counter.pay_base(read_register_base)?;
244            self.gas_counter.pay_per_byte(read_register_byte, data.len() as _)?;
245            Ok(data.clone())
246        } else {
247            Err(HostError::InvalidRegisterId { register_id }.into())
248        }
249    }
250
251    fn internal_write_register(&mut self, register_id: u64, data: Vec<u8>) -> Result<()> {
252        self.gas_counter.pay_base(write_register_base)?;
253        self.gas_counter.pay_per_byte(write_register_byte, data.len() as u64)?;
254        if data.len() as u64 > self.config.limit_config.max_register_size
255            || self.registers.len() as u64 >= self.config.limit_config.max_number_registers
256        {
257            return Err(HostError::MemoryAccessViolation.into());
258        }
259        self.registers.insert(register_id, data);
260
261        // Calculate the new memory usage.
262        let usage: usize =
263            self.registers.values().map(|v| size_of::<u64>() + v.len() * size_of::<u8>()).sum();
264        if usage as u64 > self.config.limit_config.registers_memory_limit {
265            Err(HostError::MemoryAccessViolation.into())
266        } else {
267            Ok(())
268        }
269    }
270
271    /// Convenience function for testing.
272    pub fn wrapped_internal_write_register(&mut self, register_id: u64, data: &[u8]) -> Result<()> {
273        self.internal_write_register(register_id, data.to_vec())
274    }
275
276    /// Writes the entire content from the register `register_id` into the memory of the guest starting with `ptr`.
277    ///
278    /// # Arguments
279    ///
280    /// * `register_id` -- a register id from where to read the data;
281    /// * `ptr` -- location on guest memory where to copy the data.
282    ///
283    /// # Errors
284    ///
285    /// * If the content extends outside the memory allocated to the guest. In Wasmer, it returns `MemoryAccessViolation` error message;
286    /// * If `register_id` is pointing to unused register returns `InvalidRegisterId` error message.
287    ///
288    /// # Undefined Behavior
289    ///
290    /// If the content of register extends outside the preallocated memory on the host side, or the pointer points to a
291    /// wrong location this function will overwrite memory that it is not supposed to overwrite causing an undefined behavior.
292    ///
293    /// # Cost
294    ///
295    /// `base + read_register_base + read_register_byte * num_bytes + write_memory_base + write_memory_byte * num_bytes`
296    pub fn read_register(&mut self, register_id: u64, ptr: u64) -> Result<()> {
297        self.gas_counter.pay_base(base)?;
298        let data = self.internal_read_register(register_id)?;
299        self.memory_set_slice(ptr, &data)
300    }
301
302    /// Returns the size of the blob stored in the given register.
303    /// * If register is used, then returns the size, which can potentially be zero;
304    /// * If register is not used, returns `u64::MAX`
305    ///
306    /// # Arguments
307    ///
308    /// * `register_id` -- a register id from where to read the data;
309    ///
310    /// # Cost
311    ///
312    /// `base`
313    pub fn register_len(&mut self, register_id: u64) -> Result<u64> {
314        self.gas_counter.pay_base(base)?;
315        Ok(self.registers.get(&register_id).map(|r| r.len() as _).unwrap_or(std::u64::MAX))
316    }
317
318    /// Copies `data` from the guest memory into the register. If register is unused will initialize
319    /// it. If register has larger capacity than needed for `data` will not re-allocate it. The
320    /// register will lose the pre-existing data if any.
321    ///
322    /// # Arguments
323    ///
324    /// * `register_id` -- a register id where to write the data;
325    /// * `data_len` -- length of the data in bytes;
326    /// * `data_ptr` -- pointer in the guest memory where to read the data from.
327    ///
328    /// # Cost
329    ///
330    /// `base + read_memory_base + read_memory_bytes * num_bytes + write_register_base + write_register_bytes * num_bytes`
331    pub fn write_register(&mut self, register_id: u64, data_len: u64, data_ptr: u64) -> Result<()> {
332        self.gas_counter.pay_base(base)?;
333        let data = self.memory_get_vec(data_ptr, data_len)?;
334        self.internal_write_register(register_id, data)
335    }
336
337    // ###################################
338    // # String reading helper functions #
339    // ###################################
340
341    /// Helper function to read and return utf8-encoding string.
342    /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
343    ///
344    /// # Errors
345    ///
346    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
347    /// * If string is not UTF-8 returns `BadUtf8`.
348    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
349    ///   `TotalLogLengthExceeded`.
350    ///
351    /// # Cost
352    ///
353    /// For not nul-terminated string:
354    /// `read_memory_base + read_memory_byte * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes`
355    ///
356    /// For nul-terminated string:
357    /// `(read_memory_base + read_memory_byte) * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes`
358    fn get_utf8_string(&mut self, len: u64, ptr: u64) -> Result<String> {
359        self.gas_counter.pay_base(utf8_decoding_base)?;
360        let mut buf;
361        let max_len =
362            self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length);
363        if len != std::u64::MAX {
364            if len > max_len {
365                return Err(HostError::TotalLogLengthExceeded {
366                    length: self.total_log_length.saturating_add(len),
367                    limit: self.config.limit_config.max_total_log_length,
368                }
369                .into());
370            }
371            buf = self.memory_get_vec(ptr, len)?;
372        } else {
373            buf = vec![];
374            for i in 0..=max_len {
375                // self.try_fit_mem will check for u64 overflow on the first iteration (i == 0)
376                let el = self.memory_get_u8(ptr + i)?;
377                if el == 0 {
378                    break;
379                }
380                if i == max_len {
381                    return Err(HostError::TotalLogLengthExceeded {
382                        length: self.total_log_length.saturating_add(max_len).saturating_add(1),
383                        limit: self.config.limit_config.max_total_log_length,
384                    }
385                    .into());
386                }
387                buf.push(el);
388            }
389        }
390        self.gas_counter.pay_per_byte(utf8_decoding_byte, buf.len() as _)?;
391        String::from_utf8(buf).map_err(|_| HostError::BadUTF8.into())
392    }
393
394    /// Helper function to read UTF-16 formatted string from guest memory.
395    /// # Errors
396    ///
397    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
398    /// * If string is not UTF-16 returns `BadUtf16`.
399    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
400    ///   `TotalLogLengthExceeded`.
401    ///
402    /// # Cost
403    ///
404    /// For not nul-terminated string:
405    /// `read_memory_base + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes`
406    ///
407    /// For nul-terminated string:
408    /// `read_memory_base * num_bytes / 2 + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes`
409    fn get_utf16_string(&mut self, len: u64, ptr: u64) -> Result<String> {
410        self.gas_counter.pay_base(utf16_decoding_base)?;
411        let mut u16_buffer;
412        let max_len =
413            self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length);
414        if len != std::u64::MAX {
415            let input = self.memory_get_vec(ptr, len)?;
416            if len % 2 != 0 {
417                return Err(HostError::BadUTF16.into());
418            }
419            if len > max_len {
420                return Err(HostError::TotalLogLengthExceeded {
421                    length: self.total_log_length.saturating_add(len),
422                    limit: self.config.limit_config.max_total_log_length,
423                }
424                .into());
425            }
426            u16_buffer = vec![0u16; len as usize / 2];
427            byteorder::LittleEndian::read_u16_into(&input, &mut u16_buffer);
428        } else {
429            u16_buffer = vec![];
430            let limit = max_len / size_of::<u16>() as u64;
431            // Takes 2 bytes each iter
432            for i in 0..=limit {
433                // self.try_fit_mem will check for u64 overflow on the first iteration (i == 0)
434                let start = ptr + i * size_of::<u16>() as u64;
435                let el = self.memory_get_u16(start)?;
436                if el == 0 {
437                    break;
438                }
439                if i == limit {
440                    return Err(HostError::TotalLogLengthExceeded {
441                        length: self
442                            .total_log_length
443                            .saturating_add(i * size_of::<u16>() as u64)
444                            .saturating_add(size_of::<u16>() as u64),
445                        limit: self.config.limit_config.max_total_log_length,
446                    }
447                    .into());
448                }
449                u16_buffer.push(el);
450            }
451        }
452        self.gas_counter
453            .pay_per_byte(utf16_decoding_byte, u16_buffer.len() as u64 * size_of::<u16>() as u64)?;
454        String::from_utf16(&u16_buffer).map_err(|_| HostError::BadUTF16.into())
455    }
456
457    // ####################################################
458    // # Helper functions to prevent code duplication API #
459    // ####################################################
460
461    /// Checks that the current log number didn't reach the limit yet, so we can add a new message.
462    fn check_can_add_a_log_message(&self) -> Result<()> {
463        if self.logs.len() as u64 >= self.config.limit_config.max_number_logs {
464            Err(HostError::NumberOfLogsExceeded { limit: self.config.limit_config.max_number_logs }
465                .into())
466        } else {
467            Ok(())
468        }
469    }
470
471    /// Adds a given promise to the vector of promises and returns a new promise index.
472    /// Throws `NumberPromisesExceeded` if the total number of promises exceeded the limit.
473    fn checked_push_promise(&mut self, promise: Promise) -> Result<PromiseIndex> {
474        let new_promise_idx = self.promises.len() as PromiseIndex;
475        self.promises.push(promise);
476        if self.promises.len() as u64
477            > self.config.limit_config.max_promises_per_function_call_action
478        {
479            Err(HostError::NumberPromisesExceeded {
480                number_of_promises: self.promises.len() as u64,
481                limit: self.config.limit_config.max_promises_per_function_call_action,
482            }
483            .into())
484        } else {
485            Ok(new_promise_idx)
486        }
487    }
488
489    fn checked_push_log(&mut self, message: String) -> Result<()> {
490        // The size of logged data can't be too large. No overflow.
491        self.total_log_length += message.len() as u64;
492        if self.total_log_length > self.config.limit_config.max_total_log_length {
493            return Err(HostError::TotalLogLengthExceeded {
494                length: self.total_log_length,
495                limit: self.config.limit_config.max_total_log_length,
496            }
497            .into());
498        }
499        self.logs.push(message);
500        Ok(())
501    }
502
503    // ###############
504    // # Context API #
505    // ###############
506
507    /// Saves the account id of the current contract that we execute into the register.
508    ///
509    /// # Errors
510    ///
511    /// If the registers exceed the memory limit returns `MemoryAccessViolation`.
512    ///
513    /// # Cost
514    ///
515    /// `base + write_register_base + write_register_byte * num_bytes`
516    pub fn current_account_id(&mut self, register_id: u64) -> Result<()> {
517        self.gas_counter.pay_base(base)?;
518
519        self.internal_write_register(
520            register_id,
521            self.context.current_account_id.as_bytes().to_vec(),
522        )
523    }
524
525    /// All contract calls are a result of some transaction that was signed by some account using
526    /// some access key and submitted into a memory pool (either through the wallet using RPC or by
527    /// a node itself). This function returns the id of that account. Saves the bytes of the signer
528    /// account id into the register.
529    ///
530    /// # Errors
531    ///
532    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
533    /// * If called as view function returns `ProhibitedInView`.
534    ///
535    /// # Cost
536    ///
537    /// `base + write_register_base + write_register_byte * num_bytes`
538    pub fn signer_account_id(&mut self, register_id: u64) -> Result<()> {
539        self.gas_counter.pay_base(base)?;
540
541        if self.context.is_view {
542            return Err(HostError::ProhibitedInView {
543                method_name: "signer_account_id".to_string(),
544            }
545            .into());
546        }
547        self.internal_write_register(
548            register_id,
549            self.context.signer_account_id.as_bytes().to_vec(),
550        )
551    }
552
553    /// Saves the public key fo the access key that was used by the signer into the register. In
554    /// rare situations smart contract might want to know the exact access key that was used to send
555    /// the original transaction, e.g. to increase the allowance or manipulate with the public key.
556    ///
557    /// # Errors
558    ///
559    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
560    /// * If called as view function returns `ProhibitedInView`.
561    ///
562    /// # Cost
563    ///
564    /// `base + write_register_base + write_register_byte * num_bytes`
565    pub fn signer_account_pk(&mut self, register_id: u64) -> Result<()> {
566        self.gas_counter.pay_base(base)?;
567
568        if self.context.is_view {
569            return Err(HostError::ProhibitedInView {
570                method_name: "signer_account_pk".to_string(),
571            }
572            .into());
573        }
574        self.internal_write_register(register_id, self.context.signer_account_pk.clone())
575    }
576
577    /// All contract calls are a result of a receipt, this receipt might be created by a transaction
578    /// that does function invocation on the contract or another contract as a result of
579    /// cross-contract call. Saves the bytes of the predecessor account id into the register.
580    ///
581    /// # Errors
582    ///
583    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
584    /// * If called as view function returns `ProhibitedInView`.
585    ///
586    /// # Cost
587    ///
588    /// `base + write_register_base + write_register_byte * num_bytes`
589    pub fn predecessor_account_id(&mut self, register_id: u64) -> Result<()> {
590        self.gas_counter.pay_base(base)?;
591
592        if self.context.is_view {
593            return Err(HostError::ProhibitedInView {
594                method_name: "predecessor_account_id".to_string(),
595            }
596            .into());
597        }
598        self.internal_write_register(
599            register_id,
600            self.context.predecessor_account_id.as_bytes().to_vec(),
601        )
602    }
603
604    /// Reads input to the contract call into the register. Input is expected to be in JSON-format.
605    /// If input is provided saves the bytes (potentially zero) of input into register. If input is
606    /// not provided writes 0 bytes into the register.
607    ///
608    /// # Cost
609    ///
610    /// `base + write_register_base + write_register_byte * num_bytes`
611    pub fn input(&mut self, register_id: u64) -> Result<()> {
612        self.gas_counter.pay_base(base)?;
613
614        self.internal_write_register(register_id, self.context.input.clone())
615    }
616
617    /// Returns the current block height.
618    ///
619    /// # Cost
620    ///
621    /// `base`
622    // TODO #1903 rename to `block_height`
623    pub fn block_index(&mut self) -> Result<u64> {
624        self.gas_counter.pay_base(base)?;
625        Ok(self.context.block_index)
626    }
627
628    /// Returns the current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC).
629    ///
630    /// # Cost
631    ///
632    /// `base`
633    pub fn block_timestamp(&mut self) -> Result<u64> {
634        self.gas_counter.pay_base(base)?;
635        Ok(self.context.block_timestamp)
636    }
637
638    /// Returns the current epoch height.
639    ///
640    /// # Cost
641    ///
642    /// `base`
643    pub fn epoch_height(&mut self) -> Result<EpochHeight> {
644        self.gas_counter.pay_base(base)?;
645        Ok(self.context.epoch_height)
646    }
647
648    /// Get the stake of an account, if the account is currently a validator. Otherwise returns 0.
649    /// writes the value into the` u128` variable pointed by `stake_ptr`.
650    ///
651    /// # Cost
652    ///
653    /// `base + memory_write_base + memory_write_size * 16 + utf8_decoding_base + utf8_decoding_byte * account_id_len + validator_stake_base`.
654    pub fn validator_stake(
655        &mut self,
656        account_id_len: u64,
657        account_id_ptr: u64,
658        stake_ptr: u64,
659    ) -> Result<()> {
660        self.gas_counter.pay_base(base)?;
661        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
662        self.gas_counter.pay_base(validator_stake_base)?;
663        let balance = self.ext.validator_stake(&account_id)?.unwrap_or_default();
664        self.memory_set_u128(stake_ptr, balance)
665    }
666
667    /// Get the total validator stake of the current epoch.
668    /// Write the u128 value into `stake_ptr`.
669    /// writes the value into the` u128` variable pointed by `stake_ptr`.
670    ///
671    /// # Cost
672    ///
673    /// `base + memory_write_base + memory_write_size * 16 + validator_total_stake_base`
674    pub fn validator_total_stake(&mut self, stake_ptr: u64) -> Result<()> {
675        self.gas_counter.pay_base(base)?;
676        self.gas_counter.pay_base(validator_total_stake_base)?;
677        let total_stake = self.ext.validator_total_stake()?;
678        self.memory_set_u128(stake_ptr, total_stake)
679    }
680
681    /// Returns the number of bytes used by the contract if it was saved to the trie as of the
682    /// invocation. This includes:
683    /// * The data written with storage_* functions during current and previous execution;
684    /// * The bytes needed to store the access keys of the given account.
685    /// * The contract code size
686    /// * A small fixed overhead for account metadata.
687    ///
688    /// # Cost
689    ///
690    /// `base`
691    pub fn storage_usage(&mut self) -> Result<StorageUsage> {
692        self.gas_counter.pay_base(base)?;
693        Ok(self.current_storage_usage)
694    }
695
696    // #################
697    // # Economics API #
698    // #################
699
700    /// The current balance of the given account. This includes the attached_deposit that was
701    /// attached to the transaction.
702    ///
703    /// # Cost
704    ///
705    /// `base + memory_write_base + memory_write_size * 16`
706    pub fn account_balance(&mut self, balance_ptr: u64) -> Result<()> {
707        self.gas_counter.pay_base(base)?;
708        self.memory_set_u128(balance_ptr, self.current_account_balance)
709    }
710
711    /// The current amount of tokens locked due to staking.
712    ///
713    /// # Cost
714    ///
715    /// `base + memory_write_base + memory_write_size * 16`
716    pub fn account_locked_balance(&mut self, balance_ptr: u64) -> Result<()> {
717        self.gas_counter.pay_base(base)?;
718        self.memory_set_u128(balance_ptr, self.current_account_locked_balance)
719    }
720
721    /// The balance that was attached to the call that will be immediately deposited before the
722    /// contract execution starts.
723    ///
724    /// # Errors
725    ///
726    /// If called as view function returns `ProhibitedInView``.
727    ///
728    /// # Cost
729    ///
730    /// `base + memory_write_base + memory_write_size * 16`
731    pub fn attached_deposit(&mut self, balance_ptr: u64) -> Result<()> {
732        self.gas_counter.pay_base(base)?;
733
734        if self.context.is_view {
735            return Err(HostError::ProhibitedInView {
736                method_name: "attached_deposit".to_string(),
737            }
738            .into());
739        }
740        self.memory_set_u128(balance_ptr, self.context.attached_deposit)
741    }
742
743    /// The amount of gas attached to the call that can be used to pay for the gas fees.
744    ///
745    /// # Errors
746    ///
747    /// If called as view function returns `ProhibitedInView`.
748    ///
749    /// # Cost
750    ///
751    /// `base`
752    pub fn prepaid_gas(&mut self) -> Result<Gas> {
753        self.gas_counter.pay_base(base)?;
754        if self.context.is_view {
755            return Err(
756                HostError::ProhibitedInView { method_name: "prepaid_gas".to_string() }.into()
757            );
758        }
759        Ok(self.context.prepaid_gas)
760    }
761
762    /// The gas that was already burnt during the contract execution (cannot exceed `prepaid_gas`)
763    ///
764    /// # Errors
765    ///
766    /// If called as view function returns `ProhibitedInView`.
767    ///
768    /// # Cost
769    ///
770    /// `base`
771    pub fn used_gas(&mut self) -> Result<Gas> {
772        self.gas_counter.pay_base(base)?;
773        if self.context.is_view {
774            return Err(HostError::ProhibitedInView { method_name: "used_gas".to_string() }.into());
775        }
776        Ok(self.gas_counter.used_gas())
777    }
778
779    // ############
780    // # Math API #
781    // ############
782
783    /// Writes random seed into the register.
784    ///
785    /// # Errors
786    ///
787    /// If the size of the registers exceed the set limit `MemoryAccessViolation`.
788    ///
789    /// # Cost
790    ///
791    /// `base + write_register_base + write_register_byte * num_bytes`.
792    pub fn random_seed(&mut self, register_id: u64) -> Result<()> {
793        self.gas_counter.pay_base(base)?;
794        self.internal_write_register(register_id, self.context.random_seed.clone())
795    }
796
797    /// Hashes the given value using sha256 and returns it into `register_id`.
798    ///
799    /// # Errors
800    ///
801    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
802    /// the limit with `MemoryAccessViolation`.
803    ///
804    /// # Cost
805    ///
806    /// `base + write_register_base + write_register_byte * num_bytes + sha256_base + sha256_byte * num_bytes`
807    pub fn sha256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
808        self.gas_counter.pay_base(sha256_base)?;
809        let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
810        self.gas_counter.pay_per_byte(sha256_byte, value.len() as u64)?;
811
812        use sha2::Digest;
813
814        let value_hash = sha2::Sha256::digest(&value);
815        self.internal_write_register(register_id, value_hash.as_slice().to_vec())
816    }
817
818    /// Hashes the given value using keccak256 and returns it into `register_id`.
819    ///
820    /// # Errors
821    ///
822    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
823    /// the limit with `MemoryAccessViolation`.
824    ///
825    /// # Cost
826    ///
827    /// `base + write_register_base + write_register_byte * num_bytes + keccak256_base + keccak256_byte * num_bytes`
828    pub fn keccak256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
829        self.gas_counter.pay_base(keccak256_base)?;
830        let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
831        self.gas_counter.pay_per_byte(keccak256_byte, value.len() as u64)?;
832
833        use sha3::Digest;
834
835        let value_hash = sha3::Keccak256::digest(&value);
836        self.internal_write_register(register_id, value_hash.as_slice().to_vec())
837    }
838
839    /// Hashes the given value using keccak512 and returns it into `register_id`.
840    ///
841    /// # Errors
842    ///
843    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
844    /// the limit with `MemoryAccessViolation`.
845    ///
846    /// # Cost
847    ///
848    /// `base + write_register_base + write_register_byte * num_bytes + keccak512_base + keccak512_byte * num_bytes`
849    pub fn keccak512(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
850        self.gas_counter.pay_base(keccak512_base)?;
851        let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
852        self.gas_counter.pay_per_byte(keccak512_byte, value.len() as u64)?;
853
854        use sha3::Digest;
855
856        let value_hash = sha3::Keccak512::digest(&value);
857        self.internal_write_register(register_id, value_hash.as_slice().to_vec())
858    }
859
860    /// Called by gas metering injected into Wasm. Counts both towards `burnt_gas` and `used_gas`.
861    ///
862    /// # Errors
863    ///
864    /// * If passed gas amount somehow overflows internal gas counters returns `IntegerOverflow`;
865    /// * If we exceed usage limit imposed on burnt gas returns `GasLimitExceeded`;
866    /// * If we exceed the `prepaid_gas` then returns `GasExceeded`.
867    pub fn gas(&mut self, gas_amount: u32) -> Result<()> {
868        let value = Gas::from(gas_amount) * Gas::from(self.config.regular_op_cost);
869        self.gas_counter.pay_wasm_gas(value)
870    }
871
872    // ################
873    // # Promises API #
874    // ################
875
876    /// A helper function to pay gas fee for creating a new receipt without actions.
877    /// # Args:
878    /// * `sir`: whether contract call is addressed to itself;
879    /// * `data_dependencies`: other contracts that this execution will be waiting on (or rather
880    ///   their data receipts), where bool indicates whether this is sender=receiver communication.
881    ///
882    /// # Cost
883    ///
884    /// This is a convenience function that encapsulates several costs:
885    /// `burnt_gas := dispatch cost of the receipt + base dispatch cost  cost of the data receipt`
886    /// `used_gas := burnt_gas + exec cost of the receipt + base exec cost  cost of the data receipt`
887    /// Notice that we prepay all base cost upon the creation of the data dependency, we are going to
888    /// pay for the content transmitted through the dependency upon the actual creation of the
889    /// DataReceipt.
890    fn pay_gas_for_new_receipt(&mut self, sir: bool, data_dependencies: &[bool]) -> Result<()> {
891        let fees_config_cfg = &self.fees_config;
892        let mut burn_gas = fees_config_cfg.action_receipt_creation_config.send_fee(sir);
893        let mut use_gas = fees_config_cfg.action_receipt_creation_config.exec_fee();
894        for dep in data_dependencies {
895            // Both creation and execution for data receipts are considered burnt gas.
896            burn_gas = burn_gas
897                .checked_add(fees_config_cfg.data_receipt_creation_config.base_cost.send_fee(*dep))
898                .ok_or(HostError::IntegerOverflow)?
899                .checked_add(fees_config_cfg.data_receipt_creation_config.base_cost.exec_fee())
900                .ok_or(HostError::IntegerOverflow)?;
901        }
902        use_gas = use_gas.checked_add(burn_gas).ok_or(HostError::IntegerOverflow)?;
903        self.gas_counter.pay_action_accumulated(burn_gas, use_gas, ActionCosts::new_receipt)
904    }
905
906    /// A helper function to subtract balance on transfer or attached deposit for promises.
907    /// # Args:
908    /// * `amount`: the amount to deduct from the current account balance.
909    fn deduct_balance(&mut self, amount: Balance) -> Result<()> {
910        self.current_account_balance =
911            self.current_account_balance.checked_sub(amount).ok_or(HostError::BalanceExceeded)?;
912        Ok(())
913    }
914
915    /// Creates a promise that will execute a method on account with given arguments and attaches
916    /// the given amount and gas. `amount_ptr` point to slices of bytes representing `u128`.
917    ///
918    /// # Errors
919    ///
920    /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or
921    /// `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the guest
922    /// or host returns `MemoryAccessViolation`.
923    /// * If called as view function returns `ProhibitedInView`.
924    ///
925    /// # Returns
926    ///
927    /// Index of the new promise that uniquely identifies it within the current execution of the
928    /// method.
929    ///
930    /// # Cost
931    ///
932    /// Since `promise_create` is a convenience wrapper around `promise_batch_create` and
933    /// `promise_batch_action_function_call`. This also means it charges `base` cost twice.
934    pub fn promise_create(
935        &mut self,
936        account_id_len: u64,
937        account_id_ptr: u64,
938        method_name_len: u64,
939        method_name_ptr: u64,
940        arguments_len: u64,
941        arguments_ptr: u64,
942        amount_ptr: u64,
943        gas: Gas,
944    ) -> Result<u64> {
945        let new_promise_idx = self.promise_batch_create(account_id_len, account_id_ptr)?;
946        self.promise_batch_action_function_call(
947            new_promise_idx,
948            method_name_len,
949            method_name_ptr,
950            arguments_len,
951            arguments_ptr,
952            amount_ptr,
953            gas,
954        )?;
955        Ok(new_promise_idx)
956    }
957
958    /// Attaches the callback that is executed after promise pointed by `promise_idx` is complete.
959    ///
960    /// # Errors
961    ///
962    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`;
963    /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or
964    ///   `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the
965    ///   guest or host returns `MemoryAccessViolation`.
966    /// * If called as view function returns `ProhibitedInView`.
967    ///
968    /// # Returns
969    ///
970    /// Index of the new promise that uniquely identifies it within the current execution of the
971    /// method.
972    ///
973    /// # Cost
974    ///
975    /// Since `promise_create` is a convenience wrapper around `promise_batch_then` and
976    /// `promise_batch_action_function_call`. This also means it charges `base` cost twice.
977    pub fn promise_then(
978        &mut self,
979        promise_idx: u64,
980        account_id_len: u64,
981        account_id_ptr: u64,
982        method_name_len: u64,
983        method_name_ptr: u64,
984        arguments_len: u64,
985        arguments_ptr: u64,
986        amount_ptr: u64,
987        gas: u64,
988    ) -> Result<u64> {
989        let new_promise_idx =
990            self.promise_batch_then(promise_idx, account_id_len, account_id_ptr)?;
991        self.promise_batch_action_function_call(
992            new_promise_idx,
993            method_name_len,
994            method_name_ptr,
995            arguments_len,
996            arguments_ptr,
997            amount_ptr,
998            gas,
999        )?;
1000        Ok(new_promise_idx)
1001    }
1002
1003    /// Creates a new promise which completes when time all promises passed as arguments complete.
1004    /// Cannot be used with registers. `promise_idx_ptr` points to an array of `u64` elements, with
1005    /// `promise_idx_count` denoting the number of elements. The array contains indices of promises
1006    /// that need to be waited on jointly.
1007    ///
1008    /// # Errors
1009    ///
1010    /// * If `promise_ids_ptr + 8 * promise_idx_count` extend outside the guest memory returns
1011    ///   `MemoryAccessViolation`;
1012    /// * If any of the promises in the array do not correspond to existing promises returns
1013    ///   `InvalidPromiseIndex`.
1014    /// * If called as view function returns `ProhibitedInView`.
1015    /// * If the total number of receipt dependencies exceeds `max_number_input_data_dependencies`
1016    ///   limit returns `NumInputDataDependenciesExceeded`.
1017    /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1018    ///   returns `NumPromisesExceeded`.
1019    ///
1020    /// # Returns
1021    ///
1022    /// Index of the new promise that uniquely identifies it within the current execution of the
1023    /// method.
1024    ///
1025    /// # Cost
1026    ///
1027    /// `base + promise_and_base + promise_and_per_promise * num_promises + cost of reading promise ids from memory`.
1028    pub fn promise_and(
1029        &mut self,
1030        promise_idx_ptr: u64,
1031        promise_idx_count: u64,
1032    ) -> Result<PromiseIndex> {
1033        self.gas_counter.pay_base(base)?;
1034        if self.context.is_view {
1035            return Err(
1036                HostError::ProhibitedInView { method_name: "promise_and".to_string() }.into()
1037            );
1038        }
1039        self.gas_counter.pay_base(promise_and_base)?;
1040        self.gas_counter.pay_per_byte(
1041            promise_and_per_promise,
1042            promise_idx_count
1043                .checked_mul(size_of::<u64>() as u64)
1044                .ok_or(HostError::IntegerOverflow)?,
1045        )?;
1046
1047        let promise_indices = self.memory_get_vec_u64(promise_idx_ptr, promise_idx_count)?;
1048
1049        let mut receipt_dependencies = vec![];
1050        for promise_idx in &promise_indices {
1051            let promise = self
1052                .promises
1053                .get(*promise_idx as usize)
1054                .ok_or(HostError::InvalidPromiseIndex { promise_idx: *promise_idx })?;
1055            match &promise {
1056                Promise::Receipt(receipt_idx) => {
1057                    receipt_dependencies.push(*receipt_idx);
1058                }
1059                Promise::NotReceipt(receipt_indices) => {
1060                    receipt_dependencies.extend(receipt_indices.clone());
1061                }
1062            }
1063            // Checking this in the loop to prevent abuse of too many joined vectors.
1064            if receipt_dependencies.len() as u64
1065                > self.config.limit_config.max_number_input_data_dependencies
1066            {
1067                return Err(HostError::NumberInputDataDependenciesExceeded {
1068                    number_of_input_data_dependencies: receipt_dependencies.len() as u64,
1069                    limit: self.config.limit_config.max_number_input_data_dependencies,
1070                }
1071                .into());
1072            }
1073        }
1074        self.checked_push_promise(Promise::NotReceipt(receipt_dependencies))
1075    }
1076
1077    /// Creates a new promise towards given `account_id` without any actions attached to it.
1078    ///
1079    /// # Errors
1080    ///
1081    /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host
1082    /// returns `MemoryAccessViolation`.
1083    /// * If called as view function returns `ProhibitedInView`.
1084    /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1085    ///   returns `NumPromisesExceeded`.
1086    ///
1087    /// # Returns
1088    ///
1089    /// Index of the new promise that uniquely identifies it within the current execution of the
1090    /// method.
1091    ///
1092    /// # Cost
1093    ///
1094    /// `burnt_gas := base + cost of reading and decoding the account id + dispatch cost of the receipt`.
1095    /// `used_gas := burnt_gas + exec cost of the receipt`.
1096    pub fn promise_batch_create(
1097        &mut self,
1098        account_id_len: u64,
1099        account_id_ptr: u64,
1100    ) -> Result<u64> {
1101        self.gas_counter.pay_base(base)?;
1102        if self.context.is_view {
1103            return Err(HostError::ProhibitedInView {
1104                method_name: "promise_batch_create".to_string(),
1105            }
1106            .into());
1107        }
1108        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
1109        let sir = account_id == self.context.current_account_id;
1110        self.pay_gas_for_new_receipt(sir, &[])?;
1111        let new_receipt_idx = self.ext.create_receipt(vec![], account_id.clone())?;
1112        self.receipt_to_account.insert(new_receipt_idx, account_id);
1113
1114        self.checked_push_promise(Promise::Receipt(new_receipt_idx))
1115    }
1116
1117    /// Creates a new promise towards given `account_id` without any actions attached, that is
1118    /// executed after promise pointed by `promise_idx` is complete.
1119    ///
1120    /// # Errors
1121    ///
1122    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`;
1123    /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host
1124    /// returns `MemoryAccessViolation`.
1125    /// * If called as view function returns `ProhibitedInView`.
1126    /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1127    ///   returns `NumPromisesExceeded`.
1128    ///
1129    /// # Returns
1130    ///
1131    /// Index of the new promise that uniquely identifies it within the current execution of the
1132    /// method.
1133    ///
1134    /// # Cost
1135    ///
1136    /// `base + cost of reading and decoding the account id + dispatch&execution cost of the receipt
1137    ///  + dispatch&execution base cost for each data dependency`
1138    pub fn promise_batch_then(
1139        &mut self,
1140        promise_idx: u64,
1141        account_id_len: u64,
1142        account_id_ptr: u64,
1143    ) -> Result<u64> {
1144        self.gas_counter.pay_base(base)?;
1145        if self.context.is_view {
1146            return Err(HostError::ProhibitedInView {
1147                method_name: "promise_batch_then".to_string(),
1148            }
1149            .into());
1150        }
1151        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
1152        // Update the DAG and return new promise idx.
1153        let promise = self
1154            .promises
1155            .get(promise_idx as usize)
1156            .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1157        let receipt_dependencies = match &promise {
1158            Promise::Receipt(receipt_idx) => vec![*receipt_idx],
1159            Promise::NotReceipt(receipt_indices) => receipt_indices.clone(),
1160        };
1161
1162        let sir = account_id == self.context.current_account_id;
1163        let deps: Vec<_> = receipt_dependencies
1164            .iter()
1165            .map(|receipt_idx| {
1166                self.receipt_to_account
1167                    .get(receipt_idx)
1168                    .expect("promises and receipt_to_account should be consistent.")
1169                    == &account_id
1170            })
1171            .collect();
1172        self.pay_gas_for_new_receipt(sir, &deps)?;
1173
1174        let new_receipt_idx = self.ext.create_receipt(receipt_dependencies, account_id.clone())?;
1175        self.receipt_to_account.insert(new_receipt_idx, account_id);
1176
1177        self.checked_push_promise(Promise::Receipt(new_receipt_idx))
1178    }
1179
1180    /// Helper function to return the receipt index corresponding to the given promise index.
1181    /// It also pulls account ID for the given receipt and compares it with the current account ID
1182    /// to return whether the receipt's account ID is the same.
1183    fn promise_idx_to_receipt_idx_with_sir(
1184        &self,
1185        promise_idx: u64,
1186    ) -> Result<(ReceiptIndex, bool)> {
1187        let promise = self
1188            .promises
1189            .get(promise_idx as usize)
1190            .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1191        let receipt_idx = match &promise {
1192            Promise::Receipt(receipt_idx) => Ok(*receipt_idx),
1193            Promise::NotReceipt(_) => Err(HostError::CannotAppendActionToJointPromise),
1194        }?;
1195
1196        let account_id = self
1197            .receipt_to_account
1198            .get(&receipt_idx)
1199            .expect("promises and receipt_to_account should be consistent.");
1200        let sir = account_id == &self.context.current_account_id;
1201        Ok((receipt_idx, sir))
1202    }
1203
1204    /// Appends `CreateAccount` action to the batch of actions for the given promise pointed by
1205    /// `promise_idx`.
1206    ///
1207    /// # Errors
1208    ///
1209    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1210    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1211    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1212    /// * If called as view function returns `ProhibitedInView`.
1213    ///
1214    /// # Cost
1215    ///
1216    /// `burnt_gas := base + dispatch action fee`
1217    /// `used_gas := burnt_gas + exec action fee`
1218    pub fn promise_batch_action_create_account(&mut self, promise_idx: u64) -> Result<()> {
1219        self.gas_counter.pay_base(base)?;
1220        if self.context.is_view {
1221            return Err(HostError::ProhibitedInView {
1222                method_name: "promise_batch_action_create_account".to_string(),
1223            }
1224            .into());
1225        }
1226        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1227
1228        self.gas_counter.pay_action_base(
1229            &self.fees_config.action_creation_config.create_account_cost,
1230            sir,
1231            ActionCosts::create_account,
1232        )?;
1233
1234        self.ext.append_action_create_account(receipt_idx)?;
1235        Ok(())
1236    }
1237
1238    /// Appends `DeployContract` action to the batch of actions for the given promise pointed by
1239    /// `promise_idx`.
1240    ///
1241    /// # Errors
1242    ///
1243    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1244    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1245    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1246    /// * If `code_len + code_ptr` points outside the memory of the guest or host returns
1247    /// `MemoryAccessViolation`.
1248    /// * If called as view function returns `ProhibitedInView`.
1249    /// * If the contract code length exceeds `max_contract_size` returns `ContractSizeExceeded`.
1250    ///
1251    /// # Cost
1252    ///
1253    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory `
1254    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1255    pub fn promise_batch_action_deploy_contract(
1256        &mut self,
1257        promise_idx: u64,
1258        code_len: u64,
1259        code_ptr: u64,
1260    ) -> Result<()> {
1261        self.gas_counter.pay_base(base)?;
1262        if self.context.is_view {
1263            return Err(HostError::ProhibitedInView {
1264                method_name: "promise_batch_action_deploy_contract".to_string(),
1265            }
1266            .into());
1267        }
1268        let code = self.get_vec_from_memory_or_register(code_ptr, code_len)?;
1269        if code.len() as u64 > self.config.limit_config.max_contract_size {
1270            return Err(HostError::ContractSizeExceeded {
1271                size: code.len() as u64,
1272                limit: self.config.limit_config.max_contract_size,
1273            }
1274            .into());
1275        }
1276
1277        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1278
1279        let num_bytes = code.len() as u64;
1280        self.gas_counter.pay_action_base(
1281            &self.fees_config.action_creation_config.deploy_contract_cost,
1282            sir,
1283            ActionCosts::deploy_contract,
1284        )?;
1285        self.gas_counter.pay_action_per_byte(
1286            &self.fees_config.action_creation_config.deploy_contract_cost_per_byte,
1287            num_bytes,
1288            sir,
1289            ActionCosts::deploy_contract,
1290        )?;
1291
1292        self.ext.append_action_deploy_contract(receipt_idx, code)?;
1293        Ok(())
1294    }
1295
1296    /// Appends `FunctionCall` action to the batch of actions for the given promise pointed by
1297    /// `promise_idx`.
1298    ///
1299    /// # Errors
1300    ///
1301    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1302    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1303    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1304    /// * If `method_name_len + method_name_ptr` or `arguments_len + arguments_ptr` or
1305    /// `amount_ptr + 16` points outside the memory of the guest or host returns
1306    /// `MemoryAccessViolation`.
1307    /// * If called as view function returns `ProhibitedInView`.
1308    ///
1309    /// # Cost
1310    ///
1311    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory
1312    ///  + cost of reading u128, method_name and arguments from the memory`
1313    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1314    pub fn promise_batch_action_function_call(
1315        &mut self,
1316        promise_idx: u64,
1317        method_name_len: u64,
1318        method_name_ptr: u64,
1319        arguments_len: u64,
1320        arguments_ptr: u64,
1321        amount_ptr: u64,
1322        gas: Gas,
1323    ) -> Result<()> {
1324        self.gas_counter.pay_base(base)?;
1325        if self.context.is_view {
1326            return Err(HostError::ProhibitedInView {
1327                method_name: "promise_batch_action_function_call".to_string(),
1328            }
1329            .into());
1330        }
1331        let amount = self.memory_get_u128(amount_ptr)?;
1332        let method_name = self.get_vec_from_memory_or_register(method_name_ptr, method_name_len)?;
1333        if method_name.is_empty() {
1334            return Err(HostError::EmptyMethodName.into());
1335        }
1336        let arguments = self.get_vec_from_memory_or_register(arguments_ptr, arguments_len)?;
1337
1338        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1339
1340        // Input can't be large enough to overflow
1341        let num_bytes = method_name.len() as u64 + arguments.len() as u64;
1342        self.gas_counter.pay_action_base(
1343            &self.fees_config.action_creation_config.function_call_cost,
1344            sir,
1345            ActionCosts::function_call,
1346        )?;
1347        self.gas_counter.pay_action_per_byte(
1348            &self.fees_config.action_creation_config.function_call_cost_per_byte,
1349            num_bytes,
1350            sir,
1351            ActionCosts::function_call,
1352        )?;
1353        // Prepaid gas
1354        self.gas_counter.prepay_gas(gas)?;
1355
1356        self.deduct_balance(amount)?;
1357
1358        self.ext.append_action_function_call(receipt_idx, method_name, arguments, amount, gas)?;
1359        Ok(())
1360    }
1361
1362    /// Appends `Transfer` action to the batch of actions for the given promise pointed by
1363    /// `promise_idx`.
1364    ///
1365    /// # Errors
1366    ///
1367    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1368    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1369    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1370    /// * If `amount_ptr + 16` points outside the memory of the guest or host returns
1371    /// `MemoryAccessViolation`.
1372    /// * If called as view function returns `ProhibitedInView`.
1373    ///
1374    /// # Cost
1375    ///
1376    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading u128 from memory `
1377    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1378    pub fn promise_batch_action_transfer(
1379        &mut self,
1380        promise_idx: u64,
1381        amount_ptr: u64,
1382    ) -> Result<()> {
1383        self.gas_counter.pay_base(base)?;
1384        if self.context.is_view {
1385            return Err(HostError::ProhibitedInView {
1386                method_name: "promise_batch_action_transfer".to_string(),
1387            }
1388            .into());
1389        }
1390        let amount = self.memory_get_u128(amount_ptr)?;
1391
1392        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1393
1394        if self.current_protocol_version >= IMPLICIT_ACCOUNT_CREATION_PROTOCOL_VERSION {
1395            // Need to check if the receiver_id can be an implicit account.
1396            let account_id = self
1397                .receipt_to_account
1398                .get(&receipt_idx)
1399                .expect("promises and receipt_to_account should be consistent.");
1400            if is_account_id_64_len_hex(&account_id) {
1401                self.gas_counter.pay_action_base(
1402                    &self.fees_config.action_creation_config.create_account_cost,
1403                    sir,
1404                    ActionCosts::transfer,
1405                )?;
1406                self.gas_counter.pay_action_base(
1407                    &self.fees_config.action_creation_config.add_key_cost.full_access_cost,
1408                    sir,
1409                    ActionCosts::transfer,
1410                )?;
1411            }
1412        }
1413        self.gas_counter.pay_action_base(
1414            &self.fees_config.action_creation_config.transfer_cost,
1415            sir,
1416            ActionCosts::transfer,
1417        )?;
1418
1419        self.deduct_balance(amount)?;
1420
1421        self.ext.append_action_transfer(receipt_idx, amount)?;
1422        Ok(())
1423    }
1424
1425    /// Appends `Stake` action to the batch of actions for the given promise pointed by
1426    /// `promise_idx`.
1427    ///
1428    /// # Errors
1429    ///
1430    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1431    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1432    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1433    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1434    /// * If `amount_ptr + 16` or `public_key_len + public_key_ptr` points outside the memory of the
1435    /// guest or host returns `MemoryAccessViolation`.
1436    /// * If called as view function returns `ProhibitedInView`.
1437    ///
1438    /// # Cost
1439    ///
1440    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1441    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1442    pub fn promise_batch_action_stake(
1443        &mut self,
1444        promise_idx: u64,
1445        amount_ptr: u64,
1446        public_key_len: u64,
1447        public_key_ptr: u64,
1448    ) -> Result<()> {
1449        self.gas_counter.pay_base(base)?;
1450        if self.context.is_view {
1451            return Err(HostError::ProhibitedInView {
1452                method_name: "promise_batch_action_stake".to_string(),
1453            }
1454            .into());
1455        }
1456        let amount = self.memory_get_u128(amount_ptr)?;
1457        let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1458
1459        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1460
1461        self.gas_counter.pay_action_base(
1462            &self.fees_config.action_creation_config.stake_cost,
1463            sir,
1464            ActionCosts::stake,
1465        )?;
1466
1467        self.ext.append_action_stake(receipt_idx, amount, public_key)?;
1468        Ok(())
1469    }
1470
1471    /// Appends `AddKey` action to the batch of actions for the given promise pointed by
1472    /// `promise_idx`. The access key will have `FullAccess` permission.
1473    ///
1474    /// # Errors
1475    ///
1476    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1477    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1478    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1479    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1480    /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host
1481    /// returns `MemoryAccessViolation`.
1482    /// * If called as view function returns `ProhibitedInView`.
1483    ///
1484    /// # Cost
1485    ///
1486    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1487    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1488    pub fn promise_batch_action_add_key_with_full_access(
1489        &mut self,
1490        promise_idx: u64,
1491        public_key_len: u64,
1492        public_key_ptr: u64,
1493        nonce: u64,
1494    ) -> Result<()> {
1495        self.gas_counter.pay_base(base)?;
1496        if self.context.is_view {
1497            return Err(HostError::ProhibitedInView {
1498                method_name: "promise_batch_action_add_key_with_full_access".to_string(),
1499            }
1500            .into());
1501        }
1502        let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1503
1504        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1505
1506        self.gas_counter.pay_action_base(
1507            &self.fees_config.action_creation_config.add_key_cost.full_access_cost,
1508            sir,
1509            ActionCosts::add_key,
1510        )?;
1511
1512        self.ext.append_action_add_key_with_full_access(receipt_idx, public_key, nonce)?;
1513        Ok(())
1514    }
1515
1516    /// Appends `AddKey` action to the batch of actions for the given promise pointed by
1517    /// `promise_idx`. The access key will have `FunctionCall` permission.
1518    ///
1519    /// # Errors
1520    ///
1521    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1522    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1523    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1524    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1525    /// * If `public_key_len + public_key_ptr`, `allowance_ptr + 16`,
1526    /// `receiver_id_len + receiver_id_ptr` or `method_names_len + method_names_ptr` points outside
1527    /// the memory of the guest or host returns `MemoryAccessViolation`.
1528    /// * If called as view function returns `ProhibitedInView`.
1529    ///
1530    /// # Cost
1531    ///
1532    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory
1533    ///  + cost of reading u128, method_names and public key from the memory + cost of reading and parsing account name`
1534    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1535    pub fn promise_batch_action_add_key_with_function_call(
1536        &mut self,
1537        promise_idx: u64,
1538        public_key_len: u64,
1539        public_key_ptr: u64,
1540        nonce: u64,
1541        allowance_ptr: u64,
1542        receiver_id_len: u64,
1543        receiver_id_ptr: u64,
1544        method_names_len: u64,
1545        method_names_ptr: u64,
1546    ) -> Result<()> {
1547        self.gas_counter.pay_base(base)?;
1548        if self.context.is_view {
1549            return Err(HostError::ProhibitedInView {
1550                method_name: "promise_batch_action_add_key_with_function_call".to_string(),
1551            }
1552            .into());
1553        }
1554        let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1555        let allowance = self.memory_get_u128(allowance_ptr)?;
1556        let allowance = if allowance > 0 { Some(allowance) } else { None };
1557        let receiver_id = self.read_and_parse_account_id(receiver_id_ptr, receiver_id_len)?;
1558        let raw_method_names =
1559            self.get_vec_from_memory_or_register(method_names_ptr, method_names_len)?;
1560        let method_names = split_method_names(&raw_method_names)?;
1561
1562        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1563
1564        // +1 is to account for null-terminating characters.
1565        let num_bytes = method_names.iter().map(|v| v.len() as u64 + 1).sum::<u64>();
1566        self.gas_counter.pay_action_base(
1567            &self.fees_config.action_creation_config.add_key_cost.function_call_cost,
1568            sir,
1569            ActionCosts::function_call,
1570        )?;
1571        self.gas_counter.pay_action_per_byte(
1572            &self.fees_config.action_creation_config.add_key_cost.function_call_cost_per_byte,
1573            num_bytes,
1574            sir,
1575            ActionCosts::function_call,
1576        )?;
1577
1578        self.ext.append_action_add_key_with_function_call(
1579            receipt_idx,
1580            public_key,
1581            nonce,
1582            allowance,
1583            receiver_id,
1584            method_names,
1585        )?;
1586        Ok(())
1587    }
1588
1589    /// Appends `DeleteKey` action to the batch of actions for the given promise pointed by
1590    /// `promise_idx`.
1591    ///
1592    /// # Errors
1593    ///
1594    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1595    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1596    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1597    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1598    /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host
1599    /// returns `MemoryAccessViolation`.
1600    /// * If called as view function returns `ProhibitedInView`.
1601    ///
1602    /// # Cost
1603    ///
1604    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1605    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1606    pub fn promise_batch_action_delete_key(
1607        &mut self,
1608        promise_idx: u64,
1609        public_key_len: u64,
1610        public_key_ptr: u64,
1611    ) -> Result<()> {
1612        self.gas_counter.pay_base(base)?;
1613        if self.context.is_view {
1614            return Err(HostError::ProhibitedInView {
1615                method_name: "promise_batch_action_delete_key".to_string(),
1616            }
1617            .into());
1618        }
1619        let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1620
1621        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1622
1623        self.gas_counter.pay_action_base(
1624            &self.fees_config.action_creation_config.delete_key_cost,
1625            sir,
1626            ActionCosts::delete_key,
1627        )?;
1628
1629        self.ext.append_action_delete_key(receipt_idx, public_key)?;
1630        Ok(())
1631    }
1632
1633    /// Appends `DeleteAccount` action to the batch of actions for the given promise pointed by
1634    /// `promise_idx`.
1635    ///
1636    /// # Errors
1637    ///
1638    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1639    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1640    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1641    /// * If `beneficiary_id_len + beneficiary_id_ptr` points outside the memory of the guest or
1642    /// host returns `MemoryAccessViolation`.
1643    /// * If called as view function returns `ProhibitedInView`.
1644    ///
1645    /// # Cost
1646    ///
1647    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading and parsing account id from memory `
1648    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1649    pub fn promise_batch_action_delete_account(
1650        &mut self,
1651        promise_idx: u64,
1652        beneficiary_id_len: u64,
1653        beneficiary_id_ptr: u64,
1654    ) -> Result<()> {
1655        self.gas_counter.pay_base(base)?;
1656        if self.context.is_view {
1657            return Err(HostError::ProhibitedInView {
1658                method_name: "promise_batch_action_delete_account".to_string(),
1659            }
1660            .into());
1661        }
1662        let beneficiary_id =
1663            self.read_and_parse_account_id(beneficiary_id_ptr, beneficiary_id_len)?;
1664
1665        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1666
1667        self.gas_counter.pay_action_base(
1668            &self.fees_config.action_creation_config.delete_account_cost,
1669            sir,
1670            ActionCosts::delete_account,
1671        )?;
1672
1673        self.ext.append_action_delete_account(receipt_idx, beneficiary_id)?;
1674        Ok(())
1675    }
1676
1677    /// If the current function is invoked by a callback we can access the execution results of the
1678    /// promises that caused the callback. This function returns the number of complete and
1679    /// incomplete callbacks.
1680    ///
1681    /// Note, we are only going to have incomplete callbacks once we have promise_or combinator.
1682    ///
1683    ///
1684    /// * If there is only one callback returns `1`;
1685    /// * If there are multiple callbacks (e.g. created through `promise_and`) returns their number;
1686    /// * If the function was called not through the callback returns `0`.
1687    ///
1688    /// # Cost
1689    ///
1690    /// `base`
1691    pub fn promise_results_count(&mut self) -> Result<u64> {
1692        self.gas_counter.pay_base(base)?;
1693        if self.context.is_view {
1694            return Err(HostError::ProhibitedInView {
1695                method_name: "promise_results_count".to_string(),
1696            }
1697            .into());
1698        }
1699        Ok(self.promise_results.len() as _)
1700    }
1701
1702    /// If the current function is invoked by a callback we can access the execution results of the
1703    /// promises that caused the callback. This function returns the result in blob format and
1704    /// places it into the register.
1705    ///
1706    /// * If promise result is complete and successful copies its blob into the register;
1707    /// * If promise result is complete and failed or incomplete keeps register unused;
1708    ///
1709    /// # Returns
1710    ///
1711    /// * If promise result is not complete returns `0`;
1712    /// * If promise result is complete and successful returns `1`;
1713    /// * If promise result is complete and failed returns `2`.
1714    ///
1715    /// # Errors
1716    ///
1717    /// * If `result_id` does not correspond to an existing result returns `InvalidPromiseResultIndex`;
1718    /// * If copying the blob exhausts the memory limit it returns `MemoryAccessViolation`.
1719    /// * If called as view function returns `ProhibitedInView`.
1720    ///
1721    /// # Cost
1722    ///
1723    /// `base + cost of writing data into a register`
1724    pub fn promise_result(&mut self, result_idx: u64, register_id: u64) -> Result<u64> {
1725        self.gas_counter.pay_base(base)?;
1726        if self.context.is_view {
1727            return Err(
1728                HostError::ProhibitedInView { method_name: "promise_result".to_string() }.into()
1729            );
1730        }
1731        match self
1732            .promise_results
1733            .get(result_idx as usize)
1734            .ok_or(HostError::InvalidPromiseResultIndex { result_idx })?
1735        {
1736            PromiseResult::NotReady => Ok(0),
1737            PromiseResult::Successful(data) => {
1738                self.internal_write_register(register_id, data.clone())?;
1739                Ok(1)
1740            }
1741            PromiseResult::Failed => Ok(2),
1742        }
1743    }
1744
1745    /// When promise `promise_idx` finishes executing its result is considered to be the result of
1746    /// the current function.
1747    ///
1748    /// # Errors
1749    ///
1750    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1751    /// * If called as view function returns `ProhibitedInView`.
1752    ///
1753    /// # Cost
1754    ///
1755    /// `base + promise_return`
1756    pub fn promise_return(&mut self, promise_idx: u64) -> Result<()> {
1757        self.gas_counter.pay_base(base)?;
1758        self.gas_counter.pay_base(promise_return)?;
1759        if self.context.is_view {
1760            return Err(
1761                HostError::ProhibitedInView { method_name: "promise_return".to_string() }.into()
1762            );
1763        }
1764        match self
1765            .promises
1766            .get(promise_idx as usize)
1767            .ok_or(HostError::InvalidPromiseIndex { promise_idx })?
1768        {
1769            Promise::Receipt(receipt_idx) => {
1770                self.return_data = ReturnData::ReceiptIndex(*receipt_idx);
1771                Ok(())
1772            }
1773            Promise::NotReceipt(_) => Err(HostError::CannotReturnJointPromise.into()),
1774        }
1775    }
1776
1777    // #####################
1778    // # Miscellaneous API #
1779    // #####################
1780
1781    /// Sets the blob of data as the return value of the contract.
1782    ///
1783    /// # Errors
1784    ///
1785    /// * If `value_len + value_ptr` exceeds the memory container or points to an unused register it
1786    ///   returns `MemoryAccessViolation`.
1787    /// * if the length of the returned data exceeds `max_length_returned_data` returns
1788    ///   `ReturnedValueLengthExceeded`.
1789    ///
1790    /// # Cost
1791    /// `base + cost of reading return value from memory or register + dispatch&exec cost per byte of the data sent * num data receivers`
1792    pub fn value_return(&mut self, value_len: u64, value_ptr: u64) -> Result<()> {
1793        self.gas_counter.pay_base(base)?;
1794        let return_val = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
1795        let mut burn_gas: Gas = 0;
1796        let num_bytes = return_val.len() as u64;
1797        if num_bytes > self.config.limit_config.max_length_returned_data {
1798            return Err(HostError::ReturnedValueLengthExceeded {
1799                length: num_bytes,
1800                limit: self.config.limit_config.max_length_returned_data,
1801            }
1802            .into());
1803        }
1804        let data_cfg = &self.fees_config.data_receipt_creation_config;
1805        for data_receiver in &self.context.output_data_receivers {
1806            let sir = data_receiver == &self.context.current_account_id;
1807            // We deduct for execution here too, because if we later have an OR combinator
1808            // for promises then we might have some valid data receipts that arrive too late
1809            // to be picked up by the execution that waits on them (because it has started
1810            // after it receives the first data receipt) and then we need to issue a special
1811            // refund in this situation. Which we avoid by just paying for execution of
1812            // data receipt that might not be performed.
1813            // The gas here is considered burnt, cause we'll prepay for it upfront.
1814            burn_gas = burn_gas
1815                .checked_add(
1816                    data_cfg
1817                        .cost_per_byte
1818                        .send_fee(sir)
1819                        .checked_add(data_cfg.cost_per_byte.exec_fee())
1820                        .ok_or(HostError::IntegerOverflow)?
1821                        .checked_mul(num_bytes)
1822                        .ok_or(HostError::IntegerOverflow)?,
1823                )
1824                .ok_or(HostError::IntegerOverflow)?;
1825        }
1826        self.gas_counter.pay_action_accumulated(burn_gas, burn_gas, ActionCosts::value_return)?;
1827        self.return_data = ReturnData::Value(return_val);
1828        Ok(())
1829    }
1830
1831    /// Terminates the execution of the program with panic `GuestPanic`.
1832    ///
1833    /// # Cost
1834    ///
1835    /// `base`
1836    pub fn panic(&mut self) -> Result<()> {
1837        self.gas_counter.pay_base(base)?;
1838        Err(HostError::GuestPanic { panic_msg: "explicit guest panic".to_string() }.into())
1839    }
1840
1841    /// Guest panics with the UTF-8 encoded string.
1842    /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
1843    ///
1844    /// # Errors
1845    ///
1846    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1847    /// * If string is not UTF-8 returns `BadUtf8`.
1848    /// * If string is longer than `max_log_len` returns `TotalLogLengthExceeded`.
1849    ///
1850    /// # Cost
1851    /// `base + cost of reading and decoding a utf8 string`
1852    pub fn panic_utf8(&mut self, len: u64, ptr: u64) -> Result<()> {
1853        self.gas_counter.pay_base(base)?;
1854        Err(HostError::GuestPanic { panic_msg: self.get_utf8_string(len, ptr)? }.into())
1855    }
1856
1857    /// Logs the UTF-8 encoded string.
1858    /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
1859    ///
1860    /// # Errors
1861    ///
1862    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1863    /// * If string is not UTF-8 returns `BadUtf8`.
1864    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
1865    ///   `TotalLogLengthExceeded`.
1866    /// * If the total number of logs will exceed the `max_number_logs` returns
1867    ///   `NumberOfLogsExceeded`.
1868    ///
1869    /// # Cost
1870    ///
1871    /// `base + log_base + log_byte + num_bytes + utf8 decoding cost`
1872    pub fn log_utf8(&mut self, len: u64, ptr: u64) -> Result<()> {
1873        self.gas_counter.pay_base(base)?;
1874        self.check_can_add_a_log_message()?;
1875        let message = self.get_utf8_string(len, ptr)?;
1876        self.gas_counter.pay_base(log_base)?;
1877        self.gas_counter.pay_per_byte(log_byte, message.len() as u64)?;
1878        self.checked_push_log(message)
1879    }
1880
1881    /// Logs the UTF-16 encoded string. If `len == u64::MAX` then treats the string as
1882    /// null-terminated with two-byte sequence of `0x00 0x00`.
1883    ///
1884    /// # Errors
1885    ///
1886    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1887    /// * If string is not UTF-16 returns `BadUtf16`.
1888    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
1889    ///   `TotalLogLengthExceeded`.
1890    /// * If the total number of logs will exceed the `max_number_logs` returns
1891    ///   `NumberOfLogsExceeded`.
1892    ///
1893    /// # Cost
1894    ///
1895    /// `base + log_base + log_byte * num_bytes + utf16 decoding cost`
1896    pub fn log_utf16(&mut self, len: u64, ptr: u64) -> Result<()> {
1897        self.gas_counter.pay_base(base)?;
1898        self.check_can_add_a_log_message()?;
1899        let message = self.get_utf16_string(len, ptr)?;
1900        self.gas_counter.pay_base(log_base)?;
1901        // Let's not use `encode_utf16` for gas per byte here, since it's a lot of compute.
1902        self.gas_counter.pay_per_byte(log_byte, message.len() as u64)?;
1903        self.checked_push_log(message)
1904    }
1905
1906    /// Special import kept for compatibility with AssemblyScript contracts. Not called by smart
1907    /// contracts directly, but instead called by the code generated by AssemblyScript.
1908    ///
1909    /// # Errors
1910    ///
1911    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1912    /// * If string is not UTF-8 returns `BadUtf8`.
1913    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
1914    ///   `TotalLogLengthExceeded`.
1915    /// * If the total number of logs will exceed the `max_number_logs` returns
1916    ///   `NumberOfLogsExceeded`.
1917    ///
1918    /// # Cost
1919    ///
1920    /// `base +  log_base + log_byte * num_bytes + utf16 decoding cost`
1921    pub fn abort(&mut self, msg_ptr: u32, filename_ptr: u32, line: u32, col: u32) -> Result<()> {
1922        self.gas_counter.pay_base(base)?;
1923        if msg_ptr < 4 || filename_ptr < 4 {
1924            return Err(HostError::BadUTF16.into());
1925        }
1926        self.check_can_add_a_log_message()?;
1927
1928        // Underflow checked above.
1929        let msg_len = self.memory_get_u32((msg_ptr - 4) as u64)?;
1930        let filename_len = self.memory_get_u32((filename_ptr - 4) as u64)?;
1931
1932        let msg = self.get_utf16_string(msg_len as u64, msg_ptr as u64)?;
1933        let filename = self.get_utf16_string(filename_len as u64, filename_ptr as u64)?;
1934
1935        let message = format!("{}, filename: \"{}\" line: {} col: {}", msg, filename, line, col);
1936        self.gas_counter.pay_base(log_base)?;
1937        self.gas_counter.pay_per_byte(log_byte, message.as_bytes().len() as u64)?;
1938        self.checked_push_log(format!("ABORT: {}", message))?;
1939
1940        Err(HostError::GuestPanic { panic_msg: message }.into())
1941    }
1942
1943    // ###############
1944    // # Storage API #
1945    // ###############
1946
1947    /// Reads account id from the given location in memory.
1948    ///
1949    /// # Errors
1950    ///
1951    /// * If account is not UTF-8 encoded then returns `BadUtf8`;
1952    ///
1953    /// # Cost
1954    ///
1955    /// This is a helper function that encapsulates the following costs:
1956    /// cost of reading buffer from register or memory,
1957    /// `utf8_decoding_base + utf8_decoding_byte * num_bytes`.
1958    fn read_and_parse_account_id(&mut self, ptr: u64, len: u64) -> Result<AccountId> {
1959        let buf = self.get_vec_from_memory_or_register(ptr, len)?;
1960        self.gas_counter.pay_base(utf8_decoding_base)?;
1961        self.gas_counter.pay_per_byte(utf8_decoding_byte, buf.len() as u64)?;
1962        let account_id = AccountId::from_utf8(buf).map_err(|_| HostError::BadUTF8)?;
1963        Ok(account_id)
1964    }
1965
1966    /// Writes key-value into storage.
1967    /// * If key is not in use it inserts the key-value pair and does not modify the register. Returns `0`;
1968    /// * If key is in use it inserts the key-value and copies the old value into the `register_id`. Returns `1`.
1969    ///
1970    /// # Errors
1971    ///
1972    /// * If `key_len + key_ptr` or `value_len + value_ptr` exceeds the memory container or points
1973    ///   to an unused register it returns `MemoryAccessViolation`;
1974    /// * If returning the preempted value into the registers exceed the memory container it returns
1975    ///   `MemoryAccessViolation`.
1976    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
1977    /// * If the length of the value exceeds `max_length_storage_value` returns
1978    ///   `ValueLengthExceeded`.
1979    /// * If called as view function returns `ProhibitedInView``.
1980    ///
1981    /// # Cost
1982    ///
1983    /// `base + storage_write_base + storage_write_key_byte * num_key_bytes + storage_write_value_byte * num_value_bytes
1984    /// + get_vec_from_memory_or_register_cost x 2`.
1985    ///
1986    /// If a value was evicted it costs additional `storage_write_value_evicted_byte * num_evicted_bytes + internal_write_register_cost`.
1987    pub fn storage_write(
1988        &mut self,
1989        key_len: u64,
1990        key_ptr: u64,
1991        value_len: u64,
1992        value_ptr: u64,
1993        register_id: u64,
1994    ) -> Result<u64> {
1995        self.gas_counter.pay_base(base)?;
1996        if self.context.is_view {
1997            return Err(
1998                HostError::ProhibitedInView { method_name: "storage_write".to_string() }.into()
1999            );
2000        }
2001        self.gas_counter.pay_base(storage_write_base)?;
2002        let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2003        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2004            return Err(HostError::KeyLengthExceeded {
2005                length: key.len() as u64,
2006                limit: self.config.limit_config.max_length_storage_key,
2007            }
2008            .into());
2009        }
2010        let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
2011        if value.len() as u64 > self.config.limit_config.max_length_storage_value {
2012            return Err(HostError::ValueLengthExceeded {
2013                length: value.len() as u64,
2014                limit: self.config.limit_config.max_length_storage_value,
2015            }
2016            .into());
2017        }
2018        self.gas_counter.pay_per_byte(storage_write_key_byte, key.len() as u64)?;
2019        self.gas_counter.pay_per_byte(storage_write_value_byte, value.len() as u64)?;
2020        let nodes_before = self.ext.get_touched_nodes_count();
2021        let evicted_ptr = self.ext.storage_get(&key)?;
2022        let evicted =
2023            Self::deref_value(&mut self.gas_counter, storage_write_evicted_byte, evicted_ptr)?;
2024        self.gas_counter
2025            .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2026        self.ext.storage_set(&key, &value)?;
2027        let storage_config = &self.fees_config.storage_usage_config;
2028        match evicted {
2029            Some(old_value) => {
2030                // Inner value can't overflow, because the value length is limited.
2031                self.current_storage_usage = self
2032                    .current_storage_usage
2033                    .checked_sub(old_value.len() as u64)
2034                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2035                // Inner value can't overflow, because the value length is limited.
2036                self.current_storage_usage = self
2037                    .current_storage_usage
2038                    .checked_add(value.len() as u64)
2039                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2040                self.internal_write_register(register_id, old_value)?;
2041                Ok(1)
2042            }
2043            None => {
2044                // Inner value can't overflow, because the key/value length is limited.
2045                self.current_storage_usage = self
2046                    .current_storage_usage
2047                    .checked_add(
2048                        value.len() as u64
2049                            + key.len() as u64
2050                            + storage_config.num_extra_bytes_record,
2051                    )
2052                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2053                Ok(0)
2054            }
2055        }
2056    }
2057
2058    fn deref_value<'s>(
2059        gas_counter: &mut GasCounter,
2060        cost_per_byte: ExtCosts,
2061        value_ptr: Option<Box<dyn ValuePtr + 's>>,
2062    ) -> Result<Option<Vec<u8>>> {
2063        match value_ptr {
2064            Some(value_ptr) => {
2065                gas_counter.pay_per_byte(cost_per_byte, value_ptr.len() as u64)?;
2066                value_ptr.deref().map(Some)
2067            }
2068            None => Ok(None),
2069        }
2070    }
2071
2072    /// Reads the value stored under the given key.
2073    /// * If key is used copies the content of the value into the `register_id`, even if the content
2074    ///   is zero bytes. Returns `1`;
2075    /// * If key is not present then does not modify the register. Returns `0`;
2076    ///
2077    /// # Errors
2078    ///
2079    /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it
2080    ///   returns `MemoryAccessViolation`;
2081    /// * If returning the preempted value into the registers exceed the memory container it returns
2082    ///   `MemoryAccessViolation`.
2083    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2084    ///
2085    /// # Cost
2086    ///
2087    /// `base + storage_read_base + storage_read_key_byte * num_key_bytes + storage_read_value_byte + num_value_bytes
2088    ///  cost to read key from register + cost to write value into register`.
2089    pub fn storage_read(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result<u64> {
2090        self.gas_counter.pay_base(base)?;
2091        self.gas_counter.pay_base(storage_read_base)?;
2092        let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2093        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2094            return Err(HostError::KeyLengthExceeded {
2095                length: key.len() as u64,
2096                limit: self.config.limit_config.max_length_storage_key,
2097            }
2098            .into());
2099        }
2100        self.gas_counter.pay_per_byte(storage_read_key_byte, key.len() as u64)?;
2101        let nodes_before = self.ext.get_touched_nodes_count();
2102        let read = self.ext.storage_get(&key);
2103        self.gas_counter
2104            .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2105        let read = Self::deref_value(&mut self.gas_counter, storage_read_value_byte, read?)?;
2106        match read {
2107            Some(value) => {
2108                self.internal_write_register(register_id, value)?;
2109                Ok(1)
2110            }
2111            None => Ok(0),
2112        }
2113    }
2114
2115    /// Removes the value stored under the given key.
2116    /// * If key is used, removes the key-value from the trie and copies the content of the value
2117    ///   into the `register_id`, even if the content is zero bytes. Returns `1`;
2118    /// * If key is not present then does not modify the register. Returns `0`.
2119    ///
2120    /// # Errors
2121    ///
2122    /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it
2123    ///   returns `MemoryAccessViolation`;
2124    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`;
2125    /// * If returning the preempted value into the registers exceed the memory container it returns
2126    ///   `MemoryAccessViolation`.
2127    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2128    /// * If called as view function returns `ProhibitedInView``.
2129    ///
2130    /// # Cost
2131    ///
2132    /// `base + storage_remove_base + storage_remove_key_byte * num_key_bytes + storage_remove_ret_value_byte * num_value_bytes
2133    /// + cost to read the key + cost to write the value`.
2134    pub fn storage_remove(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result<u64> {
2135        self.gas_counter.pay_base(base)?;
2136        if self.context.is_view {
2137            return Err(
2138                HostError::ProhibitedInView { method_name: "storage_remove".to_string() }.into()
2139            );
2140        }
2141        self.gas_counter.pay_base(storage_remove_base)?;
2142        let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2143        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2144            return Err(HostError::KeyLengthExceeded {
2145                length: key.len() as u64,
2146                limit: self.config.limit_config.max_length_storage_key,
2147            }
2148            .into());
2149        }
2150        self.gas_counter.pay_per_byte(storage_remove_key_byte, key.len() as u64)?;
2151        let nodes_before = self.ext.get_touched_nodes_count();
2152        let removed_ptr = self.ext.storage_get(&key)?;
2153        let removed =
2154            Self::deref_value(&mut self.gas_counter, storage_remove_ret_value_byte, removed_ptr)?;
2155
2156        self.ext.storage_remove(&key)?;
2157        self.gas_counter
2158            .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2159        let storage_config = &self.fees_config.storage_usage_config;
2160        match removed {
2161            Some(value) => {
2162                // Inner value can't overflow, because the key/value length is limited.
2163                self.current_storage_usage = self
2164                    .current_storage_usage
2165                    .checked_sub(
2166                        value.len() as u64
2167                            + key.len() as u64
2168                            + storage_config.num_extra_bytes_record,
2169                    )
2170                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2171                self.internal_write_register(register_id, value)?;
2172                Ok(1)
2173            }
2174            None => Ok(0),
2175        }
2176    }
2177
2178    /// Checks if there is a key-value pair.
2179    /// * If key is used returns `1`, even if the value is zero bytes;
2180    /// * Otherwise returns `0`.
2181    ///
2182    /// # Errors
2183    ///
2184    /// * If `key_len + key_ptr` exceeds the memory container it returns `MemoryAccessViolation`.
2185    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2186    ///
2187    /// # Cost
2188    ///
2189    /// `base + storage_has_key_base + storage_has_key_byte * num_bytes + cost of reading key`
2190    pub fn storage_has_key(&mut self, key_len: u64, key_ptr: u64) -> Result<u64> {
2191        self.gas_counter.pay_base(base)?;
2192        self.gas_counter.pay_base(storage_has_key_base)?;
2193        let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2194        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2195            return Err(HostError::KeyLengthExceeded {
2196                length: key.len() as u64,
2197                limit: self.config.limit_config.max_length_storage_key,
2198            }
2199            .into());
2200        }
2201        self.gas_counter.pay_per_byte(storage_has_key_byte, key.len() as u64)?;
2202        let nodes_before = self.ext.get_touched_nodes_count();
2203        let res = self.ext.storage_has_key(&key);
2204        self.gas_counter
2205            .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2206        Ok(res? as u64)
2207    }
2208
2209    /// DEPRECATED
2210    /// Creates an iterator object inside the host. Returns the identifier that uniquely
2211    /// differentiates the given iterator from other iterators that can be simultaneously created.
2212    /// * It iterates over the keys that have the provided prefix. The order of iteration is defined
2213    ///   by the lexicographic order of the bytes in the keys;
2214    /// * If there are no keys, it creates an empty iterator, see below on empty iterators.
2215    ///
2216    /// # Errors
2217    ///
2218    /// * If `prefix_len + prefix_ptr` exceeds the memory container it returns
2219    ///   `MemoryAccessViolation`.
2220    /// * If the length of the prefix exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2221    ///
2222    /// # Cost
2223    ///
2224    /// `base + storage_iter_create_prefix_base + storage_iter_create_key_byte * num_prefix_bytes
2225    ///  cost of reading the prefix`.
2226    pub fn storage_iter_prefix(&mut self, _prefix_len: u64, _prefix_ptr: u64) -> Result<u64> {
2227        Err(VMLogicError::HostError(HostError::Deprecated {
2228            method_name: "storage_iter_prefix".to_string(),
2229        }))
2230    }
2231
2232    /// DEPRECATED
2233    /// Iterates over all key-values such that keys are between `start` and `end`, where `start` is
2234    /// inclusive and `end` is exclusive. Unless lexicographically `start < end`, it creates an
2235    /// empty iterator. Note, this definition allows for `start` or `end` keys to not actually exist
2236    /// on the given trie.
2237    ///
2238    /// # Errors
2239    ///
2240    /// * If `start_len + start_ptr` or `end_len + end_ptr` exceeds the memory container or points to
2241    ///   an unused register it returns `MemoryAccessViolation`.
2242    /// * If the length of the `start` exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2243    /// * If the length of the `end` exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2244    ///
2245    /// # Cost
2246    ///
2247    /// `base + storage_iter_create_range_base + storage_iter_create_from_byte * num_from_bytes
2248    ///  + storage_iter_create_to_byte * num_to_bytes + reading from prefix + reading to prefix`.
2249    pub fn storage_iter_range(
2250        &mut self,
2251        _start_len: u64,
2252        _start_ptr: u64,
2253        _end_len: u64,
2254        _end_ptr: u64,
2255    ) -> Result<u64> {
2256        Err(VMLogicError::HostError(HostError::Deprecated {
2257            method_name: "storage_iter_range".to_string(),
2258        }))
2259    }
2260
2261    /// DEPRECATED
2262    /// Advances iterator and saves the next key and value in the register.
2263    /// * If iterator is not empty (after calling next it points to a key-value), copies the key
2264    ///   into `key_register_id` and value into `value_register_id` and returns `1`;
2265    /// * If iterator is empty returns `0`;
2266    /// This allows us to iterate over the keys that have zero bytes stored in values.
2267    ///
2268    /// # Errors
2269    ///
2270    /// * If `key_register_id == value_register_id` returns `MemoryAccessViolation`;
2271    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`;
2272    /// * If `iterator_id` does not correspond to an existing iterator returns `InvalidIteratorId`;
2273    /// * If between the creation of the iterator and calling `storage_iter_next` the range over
2274    ///   which it iterates was modified returns `IteratorWasInvalidated`. Specifically, if
2275    ///   `storage_write` or `storage_remove` was invoked on the key key such that:
2276    ///   * in case of `storage_iter_prefix`. `key` has the given prefix and:
2277    ///     * Iterator was not called next yet.
2278    ///     * `next` was already called on the iterator and it is currently pointing at the `key`
2279    ///       `curr` such that `curr <= key`.
2280    ///   * in case of `storage_iter_range`. `start<=key<end` and:
2281    ///     * Iterator was not called `next` yet.
2282    ///     * `next` was already called on the iterator and it is currently pointing at the key
2283    ///       `curr` such that `curr<=key<end`.
2284    ///
2285    /// # Cost
2286    ///
2287    /// `base + storage_iter_next_base + storage_iter_next_key_byte * num_key_bytes + storage_iter_next_value_byte * num_value_bytes
2288    ///  + writing key to register + writing value to register`.
2289    pub fn storage_iter_next(
2290        &mut self,
2291        _iterator_id: u64,
2292        _key_register_id: u64,
2293        _value_register_id: u64,
2294    ) -> Result<u64> {
2295        Err(VMLogicError::HostError(HostError::Deprecated {
2296            method_name: "storage_iter_next".to_string(),
2297        }))
2298    }
2299
2300    /// Computes the outcome of execution.
2301    pub fn outcome(self) -> VMOutcome {
2302        VMOutcome {
2303            balance: self.current_account_balance,
2304            storage_usage: self.current_storage_usage,
2305            return_data: self.return_data,
2306            burnt_gas: self.gas_counter.burnt_gas(),
2307            used_gas: self.gas_counter.used_gas(),
2308            logs: self.logs,
2309        }
2310    }
2311
2312    /// clones the outcome of execution.
2313    pub fn clone_outcome(&self) -> VMOutcome {
2314        let logs = self.logs.clone();
2315        let return_data = self.return_data.clone();
2316        VMOutcome {
2317            balance: self.current_account_balance,
2318            storage_usage: self.current_storage_usage,
2319            return_data,
2320            burnt_gas: self.gas_counter.burnt_gas(),
2321            used_gas: self.gas_counter.used_gas(),
2322            logs,
2323        }
2324    }
2325
2326    pub fn add_contract_compile_fee(&mut self, code_len: u64) -> Result<()> {
2327        self.gas_counter.pay_per_byte(contract_compile_bytes, code_len)?;
2328        self.gas_counter.pay_base(contract_compile_base)
2329    }
2330}
2331
2332#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
2333pub struct VMOutcome {
2334    #[serde(with = "crate::serde_with::u128_dec_format")]
2335    pub balance: Balance,
2336    pub storage_usage: StorageUsage,
2337    pub return_data: ReturnData,
2338    pub burnt_gas: Gas,
2339    pub used_gas: Gas,
2340    pub logs: Vec<String>,
2341}