unc_vm_runner/logic/
logic.rs

1use super::context::VMContext;
2use super::dependencies::{External, MemSlice, MemoryLike};
3use super::errors::{FunctionCallError, InconsistentStateError};
4use super::gas_counter::{FastGasCounter, GasCounter};
5use super::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData};
6use super::utils::split_method_names;
7use super::ValuePtr;
8use super::{HostError, VMLogicError};
9use crate::ProfileDataV3;
10use std::mem::size_of;
11use unc_crypto::Secp256K1Signature;
12use unc_parameters::vm::{Config, StorageGetMode};
13use unc_parameters::{
14    transfer_exec_fee, transfer_send_fee, ActionCosts, ExtCosts, RuntimeFeesConfig,
15};
16use unc_primitives_core::config::ViewConfig;
17use unc_primitives_core::types::{
18    AccountId, Balance, Compute, EpochHeight, Gas, GasWeight, StorageUsage,
19};
20use ExtCosts::*;
21
22pub type Result<T, E = VMLogicError> = ::std::result::Result<T, E>;
23
24#[cfg(feature = "io_trace")]
25fn base64(s: &[u8]) -> String {
26    use base64::Engine;
27    base64::engine::general_purpose::STANDARD.encode(s)
28}
29
30pub struct VMLogic<'a> {
31    /// Provides access to the components outside the Wasm runtime for operations on the trie and
32    /// receipts creation.
33    ext: &'a mut dyn External,
34    /// Part of Context API and Economics API that was extracted from the receipt.
35    context: VMContext,
36    /// All gas and economic parameters required during contract execution.
37    pub(crate) config: &'a Config,
38    /// Fees for creating (async) actions on runtime.
39    fees_config: &'a RuntimeFeesConfig,
40    /// If this method execution is invoked directly as a callback by one or more contract calls the
41    /// results of the methods that made the callback are stored in this collection.
42    promise_results: &'a [PromiseResult],
43    /// Pointer to the guest memory.
44    memory: super::vmstate::Memory<'a>,
45
46    /// Keeping track of the current account balance, which can decrease when we create promises
47    /// and attach balance to them.
48    current_account_balance: Balance,
49    /// Current amount of staking tokens, does not automatically change when staking transaction is
50    /// issued.
51    current_account_locked_balance: Balance,
52    /// Storage usage of the current account at the moment
53    current_storage_usage: StorageUsage,
54    gas_counter: GasCounter,
55    /// What method returns.
56    return_data: ReturnData,
57    /// Logs written by the runtime.
58    logs: Vec<String>,
59    /// Registers can be used by the guest to store blobs of data without moving them across
60    /// host-guest boundary.
61    registers: super::vmstate::Registers,
62
63    /// The DAG of promises, indexed by promise id.
64    promises: Vec<Promise>,
65    /// Tracks the total log length. The sum of length of all logs.
66    total_log_length: u64,
67
68    /// Stores the amount of stack space remaining
69    remaining_stack: u64,
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
84/// Helper for calling `super::vmstate::get_memory_or_register`.
85///
86/// super::vmstate::get_memory_or_register has a whole lot of wordy arguments
87/// which are always the same when invoked inside of one of VMLogic method.
88/// This macro helps with that invocation.
89macro_rules! get_memory_or_register {
90    ($logic:expr, $offset:expr, $len:expr) => {
91        super::vmstate::get_memory_or_register(
92            &mut $logic.gas_counter,
93            &$logic.memory,
94            &$logic.registers,
95            $offset,
96            $len,
97        )
98    };
99}
100
101/// A wrapper for reading public key.
102///
103/// This exists for historical reasons because we must maintain when errors are
104/// returned.  In the old days, between reading the public key and decoding it
105/// we could return unrelated error.  Because of that we cannot change the code
106/// to return deserialisation errors immediately after reading the public key.
107///
108/// This struct abstracts away the fact that we’re deserialising the key
109/// immediately.  Decoding errors are detected as soon as this object is created
110/// but they are communicated to the user only once they call [`Self::decode`].
111///
112/// Why not just keep the old ways without this noise?  By doing deserialisation
113/// immediately we’re copying the data onto the stack without having to allocate
114/// a temporary vector.
115struct PublicKeyBuffer(Result<unc_crypto::PublicKey, ()>);
116
117impl PublicKeyBuffer {
118    fn new(data: &[u8]) -> Self {
119        Self(borsh::BorshDeserialize::try_from_slice(data).map_err(|_| ()))
120    }
121
122    fn decode(self) -> Result<unc_crypto::PublicKey> {
123        self.0.map_err(|_| HostError::InvalidPublicKey.into())
124    }
125}
126
127impl<'a> VMLogic<'a> {
128    pub fn new(
129        ext: &'a mut dyn External,
130        context: VMContext,
131        config: &'a Config,
132        fees_config: &'a RuntimeFeesConfig,
133        promise_results: &'a [PromiseResult],
134        memory: &'a mut dyn MemoryLike,
135    ) -> Self {
136        // Overflow should be checked before calling VMLogic.
137        let current_account_balance = context.account_balance + context.attached_deposit;
138        let current_storage_usage = context.storage_usage;
139        let max_gas_burnt = match context.view_config {
140            Some(ViewConfig { max_gas_burnt: max_gas_burnt_view }) => max_gas_burnt_view,
141            None => config.limit_config.max_gas_burnt,
142        };
143
144        let current_account_locked_balance = context.account_locked_balance;
145        let gas_counter = GasCounter::new(
146            config.ext_costs.clone(),
147            max_gas_burnt,
148            config.regular_op_cost,
149            context.prepaid_gas,
150            context.is_view(),
151        );
152        Self {
153            ext,
154            context,
155            config,
156            fees_config,
157            promise_results,
158            memory: super::vmstate::Memory::new(memory),
159            current_account_balance,
160            current_account_locked_balance,
161            current_storage_usage,
162            gas_counter,
163            return_data: ReturnData::None,
164            logs: vec![],
165            registers: Default::default(),
166            promises: vec![],
167            total_log_length: 0,
168            remaining_stack: u64::from(config.limit_config.max_stack_height),
169        }
170    }
171
172    /// Returns reference to logs that have been created so far.
173    pub fn logs(&self) -> &[String] {
174        &self.logs
175    }
176
177    #[cfg(test)]
178    pub(super) fn gas_counter(&self) -> &GasCounter {
179        &self.gas_counter
180    }
181
182    #[cfg(test)]
183    pub(super) fn config(&self) -> &Config {
184        &self.config
185    }
186
187    #[cfg(test)]
188    pub(super) fn memory(&mut self) -> &mut super::vmstate::Memory<'a> {
189        &mut self.memory
190    }
191
192    #[cfg(test)]
193    pub(super) fn registers(&mut self) -> &mut super::vmstate::Registers {
194        &mut self.registers
195    }
196
197    // #########################
198    // # Finite-wasm internals #
199    // #########################
200    pub fn finite_wasm_gas(&mut self, gas: u64) -> Result<()> {
201        self.gas(gas)
202    }
203
204    pub fn finite_wasm_stack(&mut self, operand_size: u64, frame_size: u64) -> Result<()> {
205        self.remaining_stack =
206            match self.remaining_stack.checked_sub(operand_size.saturating_add(frame_size)) {
207                Some(s) => s,
208                None => return Err(VMLogicError::HostError(HostError::MemoryAccessViolation)),
209            };
210        self.gas(((frame_size + 7) / 8) * u64::from(self.config.regular_op_cost))?;
211        Ok(())
212    }
213
214    pub fn finite_wasm_unstack(&mut self, operand_size: u64, frame_size: u64) -> Result<()> {
215        self.remaining_stack = self
216            .remaining_stack
217            .checked_add(operand_size.saturating_add(frame_size))
218            .expect("remaining stack integer overflow");
219        Ok(())
220    }
221
222    // #################
223    // # Registers API #
224    // #################
225
226    /// Convenience function for testing.
227    #[cfg(test)]
228    pub fn wrapped_internal_write_register(&mut self, register_id: u64, data: &[u8]) -> Result<()> {
229        self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, data)
230    }
231
232    /// Writes the entire content from the register `register_id` into the memory of the guest starting with `ptr`.
233    ///
234    /// # Arguments
235    ///
236    /// * `register_id` -- a register id from where to read the data;
237    /// * `ptr` -- location on guest memory where to copy the data.
238    ///
239    /// # Errors
240    ///
241    /// * If the content extends outside the memory allocated to the guest. In Wasmer, it returns `MemoryAccessViolation` error message;
242    /// * If `register_id` is pointing to unused register returns `InvalidRegisterId` error message.
243    ///
244    /// # Undefined Behavior
245    ///
246    /// If the content of register extends outside the preallocated memory on the host side, or the pointer points to a
247    /// wrong location this function will overwrite memory that it is not supposed to overwrite causing an undefined behavior.
248    ///
249    /// # Cost
250    ///
251    /// `base + read_register_base + read_register_byte * num_bytes + write_memory_base + write_memory_byte * num_bytes`
252    pub fn read_register(&mut self, register_id: u64, ptr: u64) -> Result<()> {
253        self.gas_counter.pay_base(base)?;
254        let data = self.registers.get(&mut self.gas_counter, register_id)?;
255        self.memory.set(&mut self.gas_counter, ptr, data)
256    }
257
258    /// Returns the size of the blob stored in the given register.
259    /// * If register is used, then returns the size, which can potentially be zero;
260    /// * If register is not used, returns `u64::MAX`
261    ///
262    /// # Arguments
263    ///
264    /// * `register_id` -- a register id from where to read the data;
265    ///
266    /// # Cost
267    ///
268    /// `base`
269    pub fn register_len(&mut self, register_id: u64) -> Result<u64> {
270        self.gas_counter.pay_base(base)?;
271        Ok(self.registers.get_len(register_id).unwrap_or(u64::MAX))
272    }
273
274    /// Copies `data` from the guest memory into the register. If register is unused will initialize
275    /// it. If register has larger capacity than needed for `data` will not re-allocate it. The
276    /// register will lose the pre-existing data if any.
277    ///
278    /// # Arguments
279    ///
280    /// * `register_id` -- a register id where to write the data;
281    /// * `data_len` -- length of the data in bytes;
282    /// * `data_ptr` -- pointer in the guest memory where to read the data from.
283    ///
284    /// # Cost
285    ///
286    /// `base + read_memory_base + read_memory_bytes * num_bytes + write_register_base + write_register_bytes * num_bytes`
287    pub fn write_register(&mut self, register_id: u64, data_len: u64, data_ptr: u64) -> Result<()> {
288        self.gas_counter.pay_base(base)?;
289        let data =
290            self.memory.view(&mut self.gas_counter, MemSlice { ptr: data_ptr, len: data_len })?;
291        self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, data)
292    }
293
294    // ###################################
295    // # String reading helper functions #
296    // ###################################
297
298    /// Helper function to read and return utf8-encoding string.
299    /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
300    ///
301    /// # Errors
302    ///
303    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
304    /// * If string is not UTF-8 returns `BadUtf8`.
305    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
306    ///   `TotalLogLengthExceeded`.
307    ///
308    /// # Cost
309    ///
310    /// For not nul-terminated string:
311    /// `read_memory_base + read_memory_byte * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes`
312    ///
313    /// For nul-terminated string:
314    /// `(read_memory_base + read_memory_byte) * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes`
315    fn get_utf8_string(&mut self, len: u64, ptr: u64) -> Result<String> {
316        self.gas_counter.pay_base(utf8_decoding_base)?;
317        let mut buf;
318        let max_len =
319            self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length);
320        if len != u64::MAX {
321            if len > max_len {
322                return self.total_log_length_exceeded(len);
323            }
324            buf = self.memory.view(&mut self.gas_counter, MemSlice { ptr, len })?.into_owned();
325        } else {
326            buf = vec![];
327            for i in 0..=max_len {
328                // self.memory_get_u8 will check for u64 overflow on the first iteration (i == 0)
329                let el = self.memory.get_u8(&mut self.gas_counter, ptr + i)?;
330                if el == 0 {
331                    break;
332                }
333                if i == max_len {
334                    return self.total_log_length_exceeded(max_len.saturating_add(1));
335                }
336                buf.push(el);
337            }
338        }
339        self.gas_counter.pay_per(utf8_decoding_byte, buf.len() as _)?;
340        String::from_utf8(buf).map_err(|_| HostError::BadUTF8.into())
341    }
342
343    /// Helper function to get utf8 string, for sandbox debug log. The difference with `get_utf8_string`:
344    /// * It's only available on sandbox node
345    /// * The cost is 0
346    /// * It's up to the caller to set correct len
347    #[cfg(feature = "sandbox")]
348    fn sandbox_get_utf8_string(&mut self, len: u64, ptr: u64) -> Result<String> {
349        let buf = self.memory.view_for_free(MemSlice { ptr, len })?.into_owned();
350        String::from_utf8(buf).map_err(|_| HostError::BadUTF8.into())
351    }
352
353    /// Helper function to read UTF-16 formatted string from guest memory.
354    /// # Errors
355    ///
356    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
357    /// * If string is not UTF-16 returns `BadUtf16`.
358    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
359    ///   `TotalLogLengthExceeded`.
360    ///
361    /// # Cost
362    ///
363    /// For not nul-terminated string:
364    /// `read_memory_base + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes`
365    ///
366    /// For nul-terminated string:
367    /// `read_memory_base * num_bytes / 2 + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes`
368    fn get_utf16_string(&mut self, mut len: u64, ptr: u64) -> Result<String> {
369        self.gas_counter.pay_base(utf16_decoding_base)?;
370        let max_len =
371            self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length);
372
373        let mem_view = if len == u64::MAX {
374            len = self.get_nul_terminated_utf16_len(ptr, max_len)?;
375            self.memory.view_for_free(MemSlice { ptr, len })
376        } else {
377            self.memory.view(&mut self.gas_counter, MemSlice { ptr, len })
378        }?;
379
380        let input = stdx::as_chunks_exact(&mem_view).map_err(|_| HostError::BadUTF16)?;
381        if len > max_len {
382            return self.total_log_length_exceeded(len);
383        }
384
385        self.gas_counter.pay_per(utf16_decoding_byte, len)?;
386        char::decode_utf16(input.into_iter().copied().map(u16::from_le_bytes))
387            .collect::<Result<String, _>>()
388            .map_err(|_| HostError::BadUTF16.into())
389    }
390
391    /// Helper function to get length of NUL-terminated UTF-16 formatted string
392    /// in guest memory.
393    ///
394    /// In other words, counts how many bytes are there to first pair of NUL
395    /// bytes.
396    fn get_nul_terminated_utf16_len(&mut self, ptr: u64, max_len: u64) -> Result<u64> {
397        let mut len = 0;
398        loop {
399            if self.memory.get_u16(&mut self.gas_counter, ptr.saturating_add(len))? == 0 {
400                return Ok(len);
401            }
402            len = match len.checked_add(2) {
403                Some(len) if len <= max_len => len,
404                Some(len) => return self.total_log_length_exceeded(len),
405                None => return self.total_log_length_exceeded(u64::MAX),
406            };
407        }
408    }
409
410    // ####################################################
411    // # Helper functions to prevent code duplication API #
412    // ####################################################
413
414    /// Checks that the current log number didn't reach the limit yet, so we can add a new message.
415    fn check_can_add_a_log_message(&self) -> Result<()> {
416        if self.logs.len() as u64 >= self.config.limit_config.max_number_logs {
417            Err(HostError::NumberOfLogsExceeded { limit: self.config.limit_config.max_number_logs }
418                .into())
419        } else {
420            Ok(())
421        }
422    }
423
424    /// Adds a given promise to the vector of promises and returns a new promise index.
425    /// Throws `NumberPromisesExceeded` if the total number of promises exceeded the limit.
426    fn checked_push_promise(&mut self, promise: Promise) -> Result<PromiseIndex> {
427        let new_promise_idx = self.promises.len() as PromiseIndex;
428        self.promises.push(promise);
429        if self.promises.len() as u64
430            > self.config.limit_config.max_promises_per_function_call_action
431        {
432            Err(HostError::NumberPromisesExceeded {
433                number_of_promises: self.promises.len() as u64,
434                limit: self.config.limit_config.max_promises_per_function_call_action,
435            }
436            .into())
437        } else {
438            Ok(new_promise_idx)
439        }
440    }
441
442    fn checked_push_log(&mut self, message: String) -> Result<()> {
443        // The size of logged data can't be too large. No overflow.
444        self.total_log_length += message.len() as u64;
445        if self.total_log_length > self.config.limit_config.max_total_log_length {
446            return self.total_log_length_exceeded(0);
447        }
448        self.logs.push(message);
449        Ok(())
450    }
451
452    fn total_log_length_exceeded<T>(&self, add_len: u64) -> Result<T> {
453        Err(HostError::TotalLogLengthExceeded {
454            length: self.total_log_length.saturating_add(add_len),
455            limit: self.config.limit_config.max_total_log_length,
456        }
457        .into())
458    }
459
460    fn get_public_key(&mut self, ptr: u64, len: u64) -> Result<PublicKeyBuffer> {
461        Ok(PublicKeyBuffer::new(&get_memory_or_register!(self, ptr, len)?))
462    }
463
464    // ###############
465    // # Context API #
466    // ###############
467
468    /// Saves the account id of the current contract that we execute into the register.
469    ///
470    /// # Errors
471    ///
472    /// If the registers exceed the memory limit returns `MemoryAccessViolation`.
473    ///
474    /// # Cost
475    ///
476    /// `base + write_register_base + write_register_byte * num_bytes`
477    pub fn current_account_id(&mut self, register_id: u64) -> Result<()> {
478        self.gas_counter.pay_base(base)?;
479        self.registers.set(
480            &mut self.gas_counter,
481            &self.config.limit_config,
482            register_id,
483            self.context.current_account_id.as_bytes(),
484        )
485    }
486
487    /// All contract calls are a result of some transaction that was signed by some account using
488    /// some access key and submitted into a memory pool (either through the wallet using RPC or by
489    /// a node itself). This function returns the id of that account. Saves the bytes of the signer
490    /// account id into the register.
491    ///
492    /// # Errors
493    ///
494    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
495    /// * If called as view function returns `ProhibitedInView`.
496    ///
497    /// # Cost
498    ///
499    /// `base + write_register_base + write_register_byte * num_bytes`
500    pub fn signer_account_id(&mut self, register_id: u64) -> Result<()> {
501        self.gas_counter.pay_base(base)?;
502
503        if self.context.is_view() {
504            return Err(HostError::ProhibitedInView {
505                method_name: "signer_account_id".to_string(),
506            }
507            .into());
508        }
509        self.registers.set(
510            &mut self.gas_counter,
511            &self.config.limit_config,
512            register_id,
513            self.context.signer_account_id.as_bytes(),
514        )
515    }
516
517    /// Saves the public key fo the access key that was used by the signer into the register. In
518    /// rare situations smart contract might want to know the exact access key that was used to send
519    /// the original transaction, e.g. to increase the allowance or manipulate with the public key.
520    ///
521    /// # Errors
522    ///
523    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
524    /// * If called as view function returns `ProhibitedInView`.
525    ///
526    /// # Cost
527    ///
528    /// `base + write_register_base + write_register_byte * num_bytes`
529    pub fn signer_account_pk(&mut self, register_id: u64) -> Result<()> {
530        self.gas_counter.pay_base(base)?;
531
532        if self.context.is_view() {
533            return Err(HostError::ProhibitedInView {
534                method_name: "signer_account_pk".to_string(),
535            }
536            .into());
537        }
538        self.registers.set(
539            &mut self.gas_counter,
540            &self.config.limit_config,
541            register_id,
542            self.context.signer_account_pk.as_slice(),
543        )
544    }
545
546    /// All contract calls are a result of a receipt, this receipt might be created by a transaction
547    /// that does function invocation on the contract or another contract as a result of
548    /// cross-contract call. Saves the bytes of the predecessor account id into the register.
549    ///
550    /// # Errors
551    ///
552    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
553    /// * If called as view function returns `ProhibitedInView`.
554    ///
555    /// # Cost
556    ///
557    /// `base + write_register_base + write_register_byte * num_bytes`
558    pub fn predecessor_account_id(&mut self, register_id: u64) -> Result<()> {
559        self.gas_counter.pay_base(base)?;
560
561        if self.context.is_view() {
562            return Err(HostError::ProhibitedInView {
563                method_name: "predecessor_account_id".to_string(),
564            }
565            .into());
566        }
567        self.registers.set(
568            &mut self.gas_counter,
569            &self.config.limit_config,
570            register_id,
571            self.context.predecessor_account_id.as_bytes(),
572        )
573    }
574
575    /// Reads input to the contract call into the register. Input is expected to be in JSON-format.
576    /// If input is provided saves the bytes (potentially zero) of input into register. If input is
577    /// not provided writes 0 bytes into the register.
578    ///
579    /// # Cost
580    ///
581    /// `base + write_register_base + write_register_byte * num_bytes`
582    pub fn input(&mut self, register_id: u64) -> Result<()> {
583        self.gas_counter.pay_base(base)?;
584
585        self.registers.set(
586            &mut self.gas_counter,
587            &self.config.limit_config,
588            register_id,
589            self.context.input.as_slice(),
590        )
591    }
592
593    /// Returns the current block height.
594    ///
595    /// It’s only due to historical reasons, this host function is called
596    /// `block_index` rather than `block_height`.
597    ///
598    /// # Cost
599    ///
600    /// `base`
601    pub fn block_index(&mut self) -> Result<u64> {
602        self.gas_counter.pay_base(base)?;
603        Ok(self.context.block_height)
604    }
605
606    /// Returns the current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC).
607    ///
608    /// # Cost
609    ///
610    /// `base`
611    pub fn block_timestamp(&mut self) -> Result<u64> {
612        self.gas_counter.pay_base(base)?;
613        Ok(self.context.block_timestamp)
614    }
615
616    /// Returns the current epoch height.
617    ///
618    /// # Cost
619    ///
620    /// `base`
621    pub fn epoch_height(&mut self) -> Result<EpochHeight> {
622        self.gas_counter.pay_base(base)?;
623        Ok(self.context.epoch_height)
624    }
625
626    /// Get the stake of an account, if the account is currently a validator. Otherwise returns 0.
627    /// writes the value into the` u128` variable pointed by `stake_ptr`.
628    ///
629    /// # Cost
630    ///
631    /// `base + memory_write_base + memory_write_size * 16 + utf8_decoding_base + utf8_decoding_byte * account_id_len + validator_stake_base`.
632    pub fn validator_stake(
633        &mut self,
634        account_id_len: u64,
635        account_id_ptr: u64,
636        stake_ptr: u64,
637    ) -> Result<()> {
638        self.gas_counter.pay_base(base)?;
639        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
640        self.gas_counter.pay_base(validator_pledge_base)?;
641        let stake = self.ext.validator_stake(&account_id)?.unwrap_or_default();
642        self.memory.set_u128(&mut self.gas_counter, stake_ptr, stake)
643    }
644
645    pub fn validator_power(
646        &mut self,
647        account_id_len: u64,
648        account_id_ptr: u64,
649        power_ptr: u64,
650    ) -> Result<()> {
651        self.gas_counter.pay_base(base)?;
652        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
653        self.gas_counter.pay_base(validator_power_base)?;
654        let power = self.ext.validator_power(&account_id)?.unwrap_or_default();
655        self.memory.set_u128(&mut self.gas_counter, power_ptr, power)
656    }
657
658    /// Get the total validator stake of the current epoch.
659    /// Write the u128 value into `stake_ptr`.
660    /// writes the value into the` u128` variable pointed by `stake_ptr`.
661    ///
662    /// # Cost
663    ///
664    /// `base + memory_write_base + memory_write_size * 16 + validator_total_stake_base`
665    ///
666    pub fn validator_total_stake(&mut self, stake_ptr: u64) -> Result<()> {
667        self.gas_counter.pay_base(base)?;
668        self.gas_counter.pay_base(validator_total_pledge_base)?;
669        let total_stake = self.ext.validator_total_stake()?;
670        self.memory.set_u128(&mut self.gas_counter, stake_ptr, total_stake)
671    }
672    pub fn validator_total_power(&mut self, power_ptr: u64) -> Result<()> {
673        self.gas_counter.pay_base(base)?;
674        self.gas_counter.pay_base(validator_total_power_base)?;
675        let total_power = self.ext.validator_total_power()?;
676        self.memory.set_u128(&mut self.gas_counter, power_ptr, total_power)
677    }
678
679    /// Returns the number of bytes used by the contract if it was saved to the trie as of the
680    /// invocation. This includes:
681    /// * The data written with storage_* functions during current and previous execution;
682    /// * The bytes needed to store the access keys of the given account.
683    /// * The contract code size
684    /// * A small fixed overhead for account metadata.
685    ///
686    /// # Cost
687    ///
688    /// `base`
689    pub fn storage_usage(&mut self) -> Result<StorageUsage> {
690        self.gas_counter.pay_base(base)?;
691        Ok(self.current_storage_usage)
692    }
693
694    // #################
695    // # Economics API #
696    // #################
697
698    /// The current balance of the given account. This includes the attached_deposit that was
699    /// attached to the transaction.
700    ///
701    /// # Cost
702    ///
703    /// `base + memory_write_base + memory_write_size * 16`
704    pub fn account_balance(&mut self, balance_ptr: u64) -> Result<()> {
705        self.gas_counter.pay_base(base)?;
706        self.memory.set_u128(&mut self.gas_counter, balance_ptr, self.current_account_balance)
707    }
708
709    /// The current amount of tokens Staking due to staking.
710    ///
711    /// # Cost
712    ///
713    /// `base + memory_write_base + memory_write_size * 16`
714    pub fn account_locked_balance(&mut self, balance_ptr: u64) -> Result<()> {
715        self.gas_counter.pay_base(base)?;
716        self.memory.set_u128(
717            &mut self.gas_counter,
718            balance_ptr,
719            self.current_account_locked_balance,
720        )
721    }
722
723    /// The balance that was attached to the call that will be immediately deposited before the
724    /// contract execution starts.
725    ///
726    /// # Errors
727    ///
728    /// If called as view function returns `ProhibitedInView``.
729    ///
730    /// # Cost
731    ///
732    /// `base + memory_write_base + memory_write_size * 16`
733    pub fn attached_deposit(&mut self, balance_ptr: u64) -> Result<()> {
734        self.gas_counter.pay_base(base)?;
735
736        self.memory.set_u128(&mut self.gas_counter, balance_ptr, self.context.attached_deposit)
737    }
738
739    /// The amount of gas attached to the call that can be used to pay for the gas fees.
740    ///
741    /// # Errors
742    ///
743    /// If called as view function returns `ProhibitedInView`.
744    ///
745    /// # Cost
746    ///
747    /// `base`
748    pub fn prepaid_gas(&mut self) -> Result<Gas> {
749        self.gas_counter.pay_base(base)?;
750        if self.context.is_view() {
751            return Err(
752                HostError::ProhibitedInView { method_name: "prepaid_gas".to_string() }.into()
753            );
754        }
755        Ok(self.context.prepaid_gas)
756    }
757
758    /// The gas that was already burnt during the contract execution (cannot exceed `prepaid_gas`)
759    ///
760    /// # Errors
761    ///
762    /// If called as view function returns `ProhibitedInView`.
763    ///
764    /// # Cost
765    ///
766    /// `base`
767    pub fn used_gas(&mut self) -> Result<Gas> {
768        self.gas_counter.pay_base(base)?;
769        if self.context.is_view() {
770            return Err(HostError::ProhibitedInView { method_name: "used_gas".to_string() }.into());
771        }
772        Ok(self.gas_counter.used_gas())
773    }
774
775    // ############
776    // # Math API #
777    // ############
778
779    /// Computes multiexp on alt_bn128 curve using Pippenger's algorithm \sum_i
780    /// mul_i g_{1 i} should be equal result.
781    ///
782    /// # Arguments
783    ///
784    /// * `value` - sequence of (g1:G1, fr:Fr), where
785    ///    G1 is point (x:Fq, y:Fq) on alt_bn128,
786    ///   alt_bn128 is Y^2 = X^3 + 3 curve over Fq.
787    ///
788    ///   `value` is encoded as packed, little-endian
789    ///   `[((u256, u256), u256)]` slice.
790    ///
791    /// # Errors
792    ///
793    /// If `value_len + value_ptr` points outside the memory or the registers
794    /// use more memory than the limit, the function returns
795    /// `MemoryAccessViolation`.
796    ///
797    /// If point coordinates are not on curve, point is not in the subgroup,
798    /// scalar is not in the field or  `value.len()%96!=0`, the function returns
799    /// `AltBn128InvalidInput`.
800    ///
801    /// # Cost
802    ///
803    /// `base + write_register_base + write_register_byte * num_bytes +
804    ///  alt_bn128_g1_multiexp_base +
805    ///  alt_bn128_g1_multiexp_element * num_elements`
806    pub fn alt_bn128_g1_multiexp(
807        &mut self,
808        value_len: u64,
809        value_ptr: u64,
810        register_id: u64,
811    ) -> Result<()> {
812        self.gas_counter.pay_base(alt_bn128_g1_multiexp_base)?;
813        let data = get_memory_or_register!(self, value_ptr, value_len)?;
814
815        let elements = super::alt_bn128::split_elements(&data)?;
816        self.gas_counter.pay_per(alt_bn128_g1_multiexp_element, elements.len() as u64)?;
817
818        let res = super::alt_bn128::g1_multiexp(elements)?;
819
820        self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, res)
821    }
822
823    /// Computes sum for signed g1 group elements on alt_bn128 curve \sum_i
824    /// (-1)^{sign_i} g_{1 i} should be equal result.
825    ///
826    /// # Arguments
827    ///
828    /// * `value` - sequence of (sign:bool, g1:G1), where
829    ///    G1 is point (x:Fq, y:Fq) on alt_bn128,
830    ///    alt_bn128 is Y^2 = X^3 + 3 curve over Fq.
831    ///
832    ///   `value` is encoded as packed, little-endian
833    ///   `[(u8, (u256, u256))]` slice. `0u8` is postive sign,
834    ///   `1u8` -- negative.
835    ///
836    /// # Errors
837    ///
838    /// If `value_len + value_ptr` points outside the memory or the registers
839    /// use more memory than the limit, the function returns `MemoryAccessViolation`.
840    ///
841    /// If point coordinates are not on curve, point is not in the subgroup,
842    /// scalar is not in the field, sign is not 0 or 1, or `value.len()%65!=0`,
843    /// the function returns `AltBn128InvalidInput`.
844    ///
845    /// # Cost
846    ///
847    /// `base + write_register_base + write_register_byte * num_bytes +
848    /// alt_bn128_g1_sum_base + alt_bn128_g1_sum_element * num_elements`
849    pub fn alt_bn128_g1_sum(
850        &mut self,
851        value_len: u64,
852        value_ptr: u64,
853        register_id: u64,
854    ) -> Result<()> {
855        self.gas_counter.pay_base(alt_bn128_g1_sum_base)?;
856        let data = get_memory_or_register!(self, value_ptr, value_len)?;
857
858        let elements = super::alt_bn128::split_elements(&data)?;
859        self.gas_counter.pay_per(alt_bn128_g1_sum_element, elements.len() as u64)?;
860
861        let res = super::alt_bn128::g1_sum(elements)?;
862
863        self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, res)
864    }
865
866    /// Computes pairing check on alt_bn128 curve.
867    /// \sum_i e(g_{1 i}, g_{2 i}) should be equal one (in additive notation), e(g1, g2) is Ate pairing
868    ///
869    /// # Arguments
870    ///
871    /// * `value` - sequence of (g1:G1, g2:G2), where
872    ///   G2 is Fr-ordered subgroup point (x:Fq2, y:Fq2) on alt_bn128 twist,
873    ///   alt_bn128 twist is Y^2 = X^3 + 3/(i+9) curve over Fq2
874    ///   Fq2 is complex field element (re: Fq, im: Fq)
875    ///   G1 is point (x:Fq, y:Fq) on alt_bn128,
876    ///   alt_bn128 is Y^2 = X^3 + 3 curve over Fq
877    ///
878    ///   `value` is encoded a as packed, little-endian
879    ///   `[((u256, u256), ((u256, u256), (u256, u256)))]` slice.
880    ///
881    /// # Errors
882    ///
883    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
884    /// the function returns `MemoryAccessViolation`.
885    ///
886    /// If point coordinates are not on curve, point is not in the subgroup, scalar
887    /// is not in the field or data are wrong serialized, for example,
888    /// `value.len()%192!=0`, the function returns `AltBn128InvalidInput`.
889    ///
890    /// # Cost
891    ///
892    /// `base + write_register_base + write_register_byte * num_bytes + alt_bn128_pairing_base + alt_bn128_pairing_element * num_elements`
893    pub fn alt_bn128_pairing_check(&mut self, value_len: u64, value_ptr: u64) -> Result<u64> {
894        self.gas_counter.pay_base(alt_bn128_pairing_check_base)?;
895        let data = get_memory_or_register!(self, value_ptr, value_len)?;
896
897        let elements = super::alt_bn128::split_elements(&data)?;
898        self.gas_counter.pay_per(alt_bn128_pairing_check_element, elements.len() as u64)?;
899
900        let res = super::alt_bn128::pairing_check(elements)?;
901
902        Ok(res as u64)
903    }
904
905    /// Writes random seed into the register.
906    ///
907    /// # Errors
908    ///
909    /// If the size of the registers exceed the set limit `MemoryAccessViolation`.
910    ///
911    /// # Cost
912    ///
913    /// `base + write_register_base + write_register_byte * num_bytes`.
914    pub fn random_seed(&mut self, register_id: u64) -> Result<()> {
915        self.gas_counter.pay_base(base)?;
916        self.registers.set(
917            &mut self.gas_counter,
918            &self.config.limit_config,
919            register_id,
920            self.context.random_seed.as_slice(),
921        )
922    }
923
924    /// Hashes the given value using sha256 and returns it into `register_id`.
925    ///
926    /// # Errors
927    ///
928    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
929    /// the limit with `MemoryAccessViolation`.
930    ///
931    /// # Cost
932    ///
933    /// `base + write_register_base + write_register_byte * num_bytes + sha256_base + sha256_byte * num_bytes`
934    pub fn sha256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
935        self.gas_counter.pay_base(sha256_base)?;
936        let value = get_memory_or_register!(self, value_ptr, value_len)?;
937        self.gas_counter.pay_per(sha256_byte, value.len() as u64)?;
938
939        use sha2::Digest;
940
941        let value_hash = sha2::Sha256::digest(&value);
942        self.registers.set(
943            &mut self.gas_counter,
944            &self.config.limit_config,
945            register_id,
946            value_hash.as_slice(),
947        )
948    }
949
950    /// Hashes the given value using keccak256 and returns it into `register_id`.
951    ///
952    /// # Errors
953    ///
954    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
955    /// the limit with `MemoryAccessViolation`.
956    ///
957    /// # Cost
958    ///
959    /// `base + write_register_base + write_register_byte * num_bytes + keccak256_base + keccak256_byte * num_bytes`
960    pub fn keccak256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
961        self.gas_counter.pay_base(keccak256_base)?;
962        let value = get_memory_or_register!(self, value_ptr, value_len)?;
963        self.gas_counter.pay_per(keccak256_byte, value.len() as u64)?;
964
965        use sha3::Digest;
966
967        let value_hash = sha3::Keccak256::digest(&value);
968        self.registers.set(
969            &mut self.gas_counter,
970            &self.config.limit_config,
971            register_id,
972            value_hash.as_slice(),
973        )
974    }
975
976    /// Hashes the given value using keccak512 and returns it into `register_id`.
977    ///
978    /// # Errors
979    ///
980    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
981    /// the limit with `MemoryAccessViolation`.
982    ///
983    /// # Cost
984    ///
985    /// `base + write_register_base + write_register_byte * num_bytes + keccak512_base + keccak512_byte * num_bytes`
986    pub fn keccak512(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
987        self.gas_counter.pay_base(keccak512_base)?;
988        let value = get_memory_or_register!(self, value_ptr, value_len)?;
989        self.gas_counter.pay_per(keccak512_byte, value.len() as u64)?;
990
991        use sha3::Digest;
992
993        let value_hash = sha3::Keccak512::digest(&value);
994        self.registers.set(
995            &mut self.gas_counter,
996            &self.config.limit_config,
997            register_id,
998            value_hash.as_slice(),
999        )
1000    }
1001
1002    /// Hashes the given value using RIPEMD-160 and returns it into `register_id`.
1003    ///
1004    /// # Errors
1005    ///
1006    /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
1007    /// the limit with `MemoryAccessViolation`.
1008    ///
1009    /// # Cost
1010    ///
1011    ///  Where `message_blocks` is `(value_len + 9).div_ceil(64)`.
1012    ///
1013    /// `base + write_register_base + write_register_byte * num_bytes + ripemd160_base + ripemd160_block * message_blocks`
1014    pub fn ripemd160(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
1015        self.gas_counter.pay_base(ripemd160_base)?;
1016        let value = get_memory_or_register!(self, value_ptr, value_len)?;
1017
1018        let message_blocks = value
1019            .len()
1020            .checked_add(8)
1021            .ok_or(VMLogicError::HostError(HostError::IntegerOverflow))?
1022            / 64
1023            + 1;
1024
1025        self.gas_counter.pay_per(ripemd160_block, message_blocks as u64)?;
1026
1027        use ripemd::Digest;
1028
1029        let value_hash = ripemd::Ripemd160::digest(&value);
1030        self.registers.set(
1031            &mut self.gas_counter,
1032            &self.config.limit_config,
1033            register_id,
1034            value_hash.as_slice(),
1035        )
1036    }
1037
1038    /// Recovers an ECDSA signer address and returns it into `register_id`.
1039    ///
1040    /// Takes in an additional flag to check for malleability of the signature
1041    /// which is generally only ideal for transactions.
1042    ///
1043    /// Returns a bool indicating success or failure as a `u64`.
1044    ///
1045    /// # Malleability Flags
1046    ///
1047    /// 0 - No extra checks.
1048    /// 1 - Rejecting upper range.
1049    ///
1050    /// # Errors
1051    ///
1052    /// * If `hash_ptr`, `r_ptr`, or `s_ptr` point outside the memory or the registers use more
1053    ///   memory than the limit, then returns `MemoryAccessViolation`.
1054    ///
1055    /// # Cost
1056    ///
1057    /// `base + write_register_base + write_register_byte * 64 + ecrecover_base`
1058    pub fn ecrecover(
1059        &mut self,
1060        hash_len: u64,
1061        hash_ptr: u64,
1062        sig_len: u64,
1063        sig_ptr: u64,
1064        v: u64,
1065        malleability_flag: u64,
1066        register_id: u64,
1067    ) -> Result<u64> {
1068        self.gas_counter.pay_base(ecrecover_base)?;
1069
1070        let signature = {
1071            let vec = get_memory_or_register!(self, sig_ptr, sig_len)?;
1072            if vec.len() != 64 {
1073                return Err(VMLogicError::HostError(HostError::ECRecoverError {
1074                    msg: format!(
1075                        "The length of the signature: {}, exceeds the limit of 64 bytes",
1076                        vec.len()
1077                    ),
1078                }));
1079            }
1080
1081            let mut bytes = [0u8; 65];
1082            bytes[0..64].copy_from_slice(&vec);
1083
1084            if v < 4 {
1085                bytes[64] = v as u8;
1086                Secp256K1Signature::from(bytes)
1087            } else {
1088                return Err(VMLogicError::HostError(HostError::ECRecoverError {
1089                    msg: format!("V recovery byte 0 through 3 are valid but was provided {}", v),
1090                }));
1091            }
1092        };
1093
1094        let hash = {
1095            let vec = get_memory_or_register!(self, hash_ptr, hash_len)?;
1096            if vec.len() != 32 {
1097                return Err(VMLogicError::HostError(HostError::ECRecoverError {
1098                    msg: format!(
1099                        "The length of the hash: {}, exceeds the limit of 32 bytes",
1100                        vec.len()
1101                    ),
1102                }));
1103            }
1104
1105            let mut bytes = [0u8; 32];
1106            bytes.copy_from_slice(&vec);
1107            bytes
1108        };
1109
1110        if malleability_flag != 0 && malleability_flag != 1 {
1111            return Err(VMLogicError::HostError(HostError::ECRecoverError {
1112                msg: format!(
1113                    "Malleability flag needs to be 0 or 1, but is instead {}",
1114                    malleability_flag
1115                ),
1116            }));
1117        }
1118
1119        if !signature.check_signature_values(malleability_flag != 0) {
1120            return Ok(false as u64);
1121        }
1122
1123        if let Ok(pk) = signature.recover(hash) {
1124            self.registers.set(
1125                &mut self.gas_counter,
1126                &self.config.limit_config,
1127                register_id,
1128                pk.as_ref(),
1129            )?;
1130            return Ok(true as u64);
1131        };
1132
1133        Ok(false as u64)
1134    }
1135
1136    /// Verify an ED25519 signature given a message and a public key.
1137    ///
1138    /// Returns a bool indicating success (1) or failure (0) as a `u64`.
1139    ///
1140    /// # Errors
1141    ///
1142    /// * If the public key's size is not equal to 32, or signature size is not
1143    ///   equal to 64, returns [HostError::Ed25519VerifyInvalidInput].
1144    /// * If any of the signature, message or public key arguments are out of
1145    ///   memory bounds, returns [`HostError::MemoryAccessViolation`]
1146    ///
1147    /// # Cost
1148    ///
1149    /// Each input can either be in memory or in a register. Set the length of
1150    /// the input to `u64::MAX` to declare that the input is a register number
1151    /// and not a pointer. Each input has a gas cost input_cost(num_bytes) that
1152    /// depends on whether it is from memory or from a register. It is either
1153    /// read_memory_base + num_bytes * read_memory_byte in the former case or
1154    /// read_register_base + num_bytes * read_register_byte in the latter. This
1155    /// function is labeled as `input_cost` below.
1156    ///
1157    /// `input_cost(num_bytes_signature) + input_cost(num_bytes_message) +
1158    ///  input_cost(num_bytes_public_key) + ed25519_verify_base +
1159    ///  ed25519_verify_byte * num_bytes_message`
1160    pub fn ed25519_verify(
1161        &mut self,
1162        signature_len: u64,
1163        signature_ptr: u64,
1164        message_len: u64,
1165        message_ptr: u64,
1166        public_key_len: u64,
1167        public_key_ptr: u64,
1168    ) -> Result<u64> {
1169        use ed25519_dalek::Verifier;
1170
1171        self.gas_counter.pay_base(ed25519_verify_base)?;
1172
1173        let signature: ed25519_dalek::Signature = {
1174            let vec = get_memory_or_register!(self, signature_ptr, signature_len)?;
1175            let b = <&[u8; ed25519_dalek::SIGNATURE_LENGTH]>::try_from(&vec[..]).map_err(|_| {
1176                VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
1177                    msg: "invalid signature length".to_string(),
1178                })
1179            })?;
1180            // Sanity-check that was performed by ed25519-dalek in from_bytes before version 2,
1181            // but was removed with version 2. It is not actually any good a check, but we need
1182            // it to avoid costs changing.
1183            if b[ed25519_dalek::SIGNATURE_LENGTH - 1] & 0b1110_0000 != 0 {
1184                return Ok(false as u64);
1185            }
1186            ed25519_dalek::Signature::from_bytes(b)
1187        };
1188
1189        let message = get_memory_or_register!(self, message_ptr, message_len)?;
1190        self.gas_counter.pay_per(ed25519_verify_byte, message.len() as u64)?;
1191
1192        let public_key: ed25519_dalek::VerifyingKey = {
1193            let vec = get_memory_or_register!(self, public_key_ptr, public_key_len)?;
1194            let b =
1195                <&[u8; ed25519_dalek::PUBLIC_KEY_LENGTH]>::try_from(&vec[..]).map_err(|_| {
1196                    VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
1197                        msg: "invalid public key length".to_string(),
1198                    })
1199                })?;
1200            match ed25519_dalek::VerifyingKey::from_bytes(b) {
1201                Ok(public_key) => public_key,
1202                Err(_) => return Ok(false as u64),
1203            }
1204        };
1205
1206        match public_key.verify(&message, &signature) {
1207            Err(_) => Ok(false as u64),
1208            Ok(()) => Ok(true as u64),
1209        }
1210    }
1211
1212    /// Consume gas. Counts both towards `burnt_gas` and `used_gas`.
1213    ///
1214    /// # Errors
1215    ///
1216    /// * If passed gas amount somehow overflows internal gas counters returns `IntegerOverflow`;
1217    /// * If we exceed usage limit imposed on burnt gas returns `GasLimitExceeded`;
1218    /// * If we exceed the `prepaid_gas` then returns `GasExceeded`.
1219    pub fn gas(&mut self, gas: Gas) -> Result<()> {
1220        self.gas_counter.burn_gas(Gas::from(gas))
1221    }
1222
1223    pub fn gas_opcodes(&mut self, opcodes: u32) -> Result<()> {
1224        self.gas(opcodes as u64 * self.config.regular_op_cost as u64)
1225    }
1226
1227    /// This is the function that is exposed to WASM contracts under the name `gas`.
1228    ///
1229    /// For now it is consuming the gas for `gas` opcodes. When we switch to finite-wasm it’ll
1230    /// be made to be a no-op.
1231    ///
1232    /// This function might be intrinsified.
1233    pub fn gas_seen_from_wasm(&mut self, gas: u32) -> Result<()> {
1234        self.gas_opcodes(gas)
1235    }
1236
1237    // ################
1238    // # Promises API #
1239    // ################
1240
1241    /// A helper function to pay gas fee for creating a new receipt without actions.
1242    /// # Args:
1243    /// * `sir`: whether contract call is addressed to itself;
1244    /// * `data_dependencies`: other contracts that this execution will be waiting on (or rather
1245    ///   their data receipts), where bool indicates whether this is sender=receiver communication.
1246    ///
1247    /// # Cost
1248    ///
1249    /// This is a convenience function that encapsulates several costs:
1250    /// `burnt_gas := dispatch cost of the receipt + base dispatch cost  cost of the data receipt`
1251    /// `used_gas := burnt_gas + exec cost of the receipt + base exec cost  cost of the data receipt`
1252    /// Notice that we prepay all base cost upon the creation of the data dependency, we are going to
1253    /// pay for the content transmitted through the dependency upon the actual creation of the
1254    /// DataReceipt.
1255    fn pay_gas_for_new_receipt(&mut self, sir: bool, data_dependencies: &[bool]) -> Result<()> {
1256        let fees_config_cfg = &self.fees_config;
1257        let mut burn_gas = fees_config_cfg.fee(ActionCosts::new_action_receipt).send_fee(sir);
1258        let mut use_gas = fees_config_cfg.fee(ActionCosts::new_action_receipt).exec_fee();
1259        for dep in data_dependencies {
1260            // Both creation and execution for data receipts are considered burnt gas.
1261            burn_gas = burn_gas
1262                .checked_add(fees_config_cfg.fee(ActionCosts::new_data_receipt_base).send_fee(*dep))
1263                .ok_or(HostError::IntegerOverflow)?
1264                .checked_add(fees_config_cfg.fee(ActionCosts::new_data_receipt_base).exec_fee())
1265                .ok_or(HostError::IntegerOverflow)?;
1266        }
1267        use_gas = use_gas.checked_add(burn_gas).ok_or(HostError::IntegerOverflow)?;
1268        // This should go to `new_data_receipt_base` and `new_action_receipt` in parts.
1269        // But we have to keep charing these two together unless we make a protocol change.
1270        self.gas_counter.pay_action_accumulated(burn_gas, use_gas, ActionCosts::new_action_receipt)
1271    }
1272
1273    /// A helper function to subtract balance on transfer or attached deposit for promises.
1274    /// # Args:
1275    /// * `amount`: the amount to deduct from the current account balance.
1276    fn deduct_balance(&mut self, amount: Balance) -> Result<()> {
1277        self.current_account_balance =
1278            self.current_account_balance.checked_sub(amount).ok_or(HostError::BalanceExceeded)?;
1279        Ok(())
1280    }
1281
1282    /// Creates a promise that will execute a method on account with given arguments and attaches
1283    /// the given amount and gas. `amount_ptr` point to slices of bytes representing `u128`.
1284    ///
1285    /// # Errors
1286    ///
1287    /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or
1288    /// `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the guest
1289    /// or host returns `MemoryAccessViolation`.
1290    /// * If called as view function returns `ProhibitedInView`.
1291    ///
1292    /// # Returns
1293    ///
1294    /// Index of the new promise that uniquely identifies it within the current execution of the
1295    /// method.
1296    ///
1297    /// # Cost
1298    ///
1299    /// Since `promise_create` is a convenience wrapper around `promise_batch_create` and
1300    /// `promise_batch_action_function_call`. This also means it charges `base` cost twice.
1301    pub fn promise_create(
1302        &mut self,
1303        account_id_len: u64,
1304        account_id_ptr: u64,
1305        method_name_len: u64,
1306        method_name_ptr: u64,
1307        arguments_len: u64,
1308        arguments_ptr: u64,
1309        amount_ptr: u64,
1310        gas: Gas,
1311    ) -> Result<u64> {
1312        let new_promise_idx = self.promise_batch_create(account_id_len, account_id_ptr)?;
1313        self.promise_batch_action_function_call(
1314            new_promise_idx,
1315            method_name_len,
1316            method_name_ptr,
1317            arguments_len,
1318            arguments_ptr,
1319            amount_ptr,
1320            gas,
1321        )?;
1322        Ok(new_promise_idx)
1323    }
1324
1325    /// Attaches the callback that is executed after promise pointed by `promise_idx` is complete.
1326    ///
1327    /// # Errors
1328    ///
1329    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`;
1330    /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or
1331    ///   `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the
1332    ///   guest or host returns `MemoryAccessViolation`.
1333    /// * If called as view function returns `ProhibitedInView`.
1334    ///
1335    /// # Returns
1336    ///
1337    /// Index of the new promise that uniquely identifies it within the current execution of the
1338    /// method.
1339    ///
1340    /// # Cost
1341    ///
1342    /// Since `promise_create` is a convenience wrapper around `promise_batch_then` and
1343    /// `promise_batch_action_function_call`. This also means it charges `base` cost twice.
1344    pub fn promise_then(
1345        &mut self,
1346        promise_idx: u64,
1347        account_id_len: u64,
1348        account_id_ptr: u64,
1349        method_name_len: u64,
1350        method_name_ptr: u64,
1351        arguments_len: u64,
1352        arguments_ptr: u64,
1353        amount_ptr: u64,
1354        gas: u64,
1355    ) -> Result<u64> {
1356        let new_promise_idx =
1357            self.promise_batch_then(promise_idx, account_id_len, account_id_ptr)?;
1358        self.promise_batch_action_function_call(
1359            new_promise_idx,
1360            method_name_len,
1361            method_name_ptr,
1362            arguments_len,
1363            arguments_ptr,
1364            amount_ptr,
1365            gas,
1366        )?;
1367        Ok(new_promise_idx)
1368    }
1369
1370    /// Creates a new promise which completes when time all promises passed as arguments complete.
1371    /// Cannot be used with registers. `promise_idx_ptr` points to an array of `u64` elements, with
1372    /// `promise_idx_count` denoting the number of elements. The array contains indices of promises
1373    /// that need to be waited on jointly.
1374    ///
1375    /// # Errors
1376    ///
1377    /// * If `promise_ids_ptr + 8 * promise_idx_count` extend outside the guest memory returns
1378    ///   `MemoryAccessViolation`;
1379    /// * If any of the promises in the array do not correspond to existing promises returns
1380    ///   `InvalidPromiseIndex`.
1381    /// * If called as view function returns `ProhibitedInView`.
1382    /// * If the total number of receipt dependencies exceeds `max_number_input_data_dependencies`
1383    ///   limit returns `NumInputDataDependenciesExceeded`.
1384    /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1385    ///   returns `NumPromisesExceeded`.
1386    ///
1387    /// # Returns
1388    ///
1389    /// Index of the new promise that uniquely identifies it within the current execution of the
1390    /// method.
1391    ///
1392    /// # Cost
1393    ///
1394    /// `base + promise_and_base + promise_and_per_promise * num_promises + cost of reading promise ids from memory`.
1395    pub fn promise_and(
1396        &mut self,
1397        promise_idx_ptr: u64,
1398        promise_idx_count: u64,
1399    ) -> Result<PromiseIndex> {
1400        self.gas_counter.pay_base(base)?;
1401        if self.context.is_view() {
1402            return Err(
1403                HostError::ProhibitedInView { method_name: "promise_and".to_string() }.into()
1404            );
1405        }
1406        self.gas_counter.pay_base(promise_and_base)?;
1407        let memory_len = promise_idx_count
1408            .checked_mul(size_of::<u64>() as u64)
1409            .ok_or(HostError::IntegerOverflow)?;
1410        self.gas_counter.pay_per(promise_and_per_promise, memory_len)?;
1411
1412        // Read indices as little endian u64.
1413        let promise_indices = self
1414            .memory
1415            .view(&mut self.gas_counter, MemSlice { ptr: promise_idx_ptr, len: memory_len })?;
1416        let promise_indices = stdx::as_chunks_exact::<{ size_of::<u64>() }, u8>(&promise_indices)
1417            .unwrap()
1418            .into_iter()
1419            .map(|bytes| u64::from_le_bytes(*bytes));
1420
1421        let mut receipt_dependencies = vec![];
1422        for promise_idx in promise_indices {
1423            let promise = self
1424                .promises
1425                .get(promise_idx as usize)
1426                .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1427            match &promise {
1428                Promise::Receipt(receipt_idx) => {
1429                    receipt_dependencies.push(*receipt_idx);
1430                }
1431                Promise::NotReceipt(receipt_indices) => {
1432                    receipt_dependencies.extend(receipt_indices.clone());
1433                }
1434            }
1435            // Checking this in the loop to prevent abuse of too many joined vectors.
1436            if receipt_dependencies.len() as u64
1437                > self.config.limit_config.max_number_input_data_dependencies
1438            {
1439                return Err(HostError::NumberInputDataDependenciesExceeded {
1440                    number_of_input_data_dependencies: receipt_dependencies.len() as u64,
1441                    limit: self.config.limit_config.max_number_input_data_dependencies,
1442                }
1443                .into());
1444            }
1445        }
1446        self.checked_push_promise(Promise::NotReceipt(receipt_dependencies))
1447    }
1448
1449    /// Creates a new promise towards given `account_id` without any actions attached to it.
1450    ///
1451    /// # Errors
1452    ///
1453    /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host
1454    /// returns `MemoryAccessViolation`.
1455    /// * If called as view function returns `ProhibitedInView`.
1456    /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1457    ///   returns `NumPromisesExceeded`.
1458    ///
1459    /// # Returns
1460    ///
1461    /// Index of the new promise that uniquely identifies it within the current execution of the
1462    /// method.
1463    ///
1464    /// # Cost
1465    ///
1466    /// `burnt_gas := base + cost of reading and decoding the account id + dispatch cost of the receipt`.
1467    /// `used_gas := burnt_gas + exec cost of the receipt`.
1468    pub fn promise_batch_create(
1469        &mut self,
1470        account_id_len: u64,
1471        account_id_ptr: u64,
1472    ) -> Result<u64> {
1473        self.gas_counter.pay_base(base)?;
1474        if self.context.is_view() {
1475            return Err(HostError::ProhibitedInView {
1476                method_name: "promise_batch_create".to_string(),
1477            }
1478            .into());
1479        }
1480        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
1481        let sir = account_id == self.context.current_account_id;
1482        self.pay_gas_for_new_receipt(sir, &[])?;
1483        let new_receipt_idx = self.ext.create_receipt(vec![], account_id)?;
1484
1485        self.checked_push_promise(Promise::Receipt(new_receipt_idx))
1486    }
1487
1488    /// Creates a new promise towards given `account_id` without any actions attached, that is
1489    /// executed after promise pointed by `promise_idx` is complete.
1490    ///
1491    /// # Errors
1492    ///
1493    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`;
1494    /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host
1495    /// returns `MemoryAccessViolation`.
1496    /// * If called as view function returns `ProhibitedInView`.
1497    /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1498    ///   returns `NumPromisesExceeded`.
1499    ///
1500    /// # Returns
1501    ///
1502    /// Index of the new promise that uniquely identifies it within the current execution of the
1503    /// method.
1504    ///
1505    /// # Cost
1506    ///
1507    /// `base + cost of reading and decoding the account id + dispatch&execution cost of the receipt
1508    ///  + dispatch&execution base cost for each data dependency`
1509    pub fn promise_batch_then(
1510        &mut self,
1511        promise_idx: u64,
1512        account_id_len: u64,
1513        account_id_ptr: u64,
1514    ) -> Result<u64> {
1515        self.gas_counter.pay_base(base)?;
1516        if self.context.is_view() {
1517            return Err(HostError::ProhibitedInView {
1518                method_name: "promise_batch_then".to_string(),
1519            }
1520            .into());
1521        }
1522        let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
1523        // Update the DAG and return new promise idx.
1524        let promise = self
1525            .promises
1526            .get(promise_idx as usize)
1527            .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1528        let receipt_dependencies = match &promise {
1529            Promise::Receipt(receipt_idx) => vec![*receipt_idx],
1530            Promise::NotReceipt(receipt_indices) => receipt_indices.clone(),
1531        };
1532
1533        let sir = account_id == self.context.current_account_id;
1534        let deps: Vec<_> = receipt_dependencies
1535            .iter()
1536            .map(|&receipt_idx| self.ext.get_receipt_receiver(receipt_idx) == &account_id)
1537            .collect();
1538        self.pay_gas_for_new_receipt(sir, &deps)?;
1539
1540        let new_receipt_idx = self.ext.create_receipt(receipt_dependencies, account_id)?;
1541
1542        self.checked_push_promise(Promise::Receipt(new_receipt_idx))
1543    }
1544
1545    /// Helper function to return the receipt index corresponding to the given promise index.
1546    /// It also pulls account ID for the given receipt and compares it with the current account ID
1547    /// to return whether the receipt's account ID is the same.
1548    fn promise_idx_to_receipt_idx_with_sir(
1549        &self,
1550        promise_idx: u64,
1551    ) -> Result<(ReceiptIndex, bool)> {
1552        let promise = self
1553            .promises
1554            .get(promise_idx as usize)
1555            .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1556        let receipt_idx = match &promise {
1557            Promise::Receipt(receipt_idx) => Ok(*receipt_idx),
1558            Promise::NotReceipt(_) => Err(HostError::CannotAppendActionToJointPromise),
1559        }?;
1560
1561        let account_id = self.ext.get_receipt_receiver(receipt_idx);
1562        let sir = account_id == &self.context.current_account_id;
1563        Ok((receipt_idx, sir))
1564    }
1565
1566    /// Appends `CreateAccount` action to the batch of actions for the given promise pointed by
1567    /// `promise_idx`.
1568    ///
1569    /// # Errors
1570    ///
1571    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1572    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1573    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1574    /// * If called as view function returns `ProhibitedInView`.
1575    ///
1576    /// # Cost
1577    ///
1578    /// `burnt_gas := base + dispatch action fee`
1579    /// `used_gas := burnt_gas + exec action fee`
1580    pub fn promise_batch_action_create_account(&mut self, promise_idx: u64) -> Result<()> {
1581        self.gas_counter.pay_base(base)?;
1582        if self.context.is_view() {
1583            return Err(HostError::ProhibitedInView {
1584                method_name: "promise_batch_action_create_account".to_string(),
1585            }
1586            .into());
1587        }
1588        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1589
1590        self.pay_action_base(ActionCosts::create_account, sir)?;
1591
1592        self.ext.append_action_create_account(receipt_idx)?;
1593        Ok(())
1594    }
1595
1596    /// Appends `DeployContract` action to the batch of actions for the given promise pointed by
1597    /// `promise_idx`.
1598    ///
1599    /// # Errors
1600    ///
1601    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1602    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1603    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1604    /// * If `code_len + code_ptr` points outside the memory of the guest or host returns
1605    /// `MemoryAccessViolation`.
1606    /// * If called as view function returns `ProhibitedInView`.
1607    /// * If the contract code length exceeds `max_contract_size` returns `ContractSizeExceeded`.
1608    ///
1609    /// # Cost
1610    ///
1611    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory `
1612    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1613    pub fn promise_batch_action_deploy_contract(
1614        &mut self,
1615        promise_idx: u64,
1616        code_len: u64,
1617        code_ptr: u64,
1618    ) -> Result<()> {
1619        self.gas_counter.pay_base(base)?;
1620        if self.context.is_view() {
1621            return Err(HostError::ProhibitedInView {
1622                method_name: "promise_batch_action_deploy_contract".to_string(),
1623            }
1624            .into());
1625        }
1626        let code = get_memory_or_register!(self, code_ptr, code_len)?;
1627        let code_len = code.len() as u64;
1628        let limit = self.config.limit_config.max_contract_size;
1629        if code_len > limit {
1630            return Err(HostError::ContractSizeExceeded { size: code_len, limit }.into());
1631        }
1632        let code = code.into_owned();
1633
1634        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1635
1636        self.pay_action_base(ActionCosts::deploy_contract_base, sir)?;
1637        self.pay_action_per_byte(ActionCosts::deploy_contract_byte, code_len, sir)?;
1638
1639        self.ext.append_action_deploy_contract(receipt_idx, code)?;
1640        Ok(())
1641    }
1642
1643    /// Appends `FunctionCall` action to the batch of actions for the given promise pointed by
1644    /// `promise_idx`.
1645    ///
1646    /// # Errors
1647    ///
1648    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1649    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1650    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1651    /// * If `method_name_len + method_name_ptr` or `arguments_len + arguments_ptr` or
1652    /// `amount_ptr + 16` points outside the memory of the guest or host returns
1653    /// `MemoryAccessViolation`.
1654    /// * If called as view function returns `ProhibitedInView`.
1655    ///
1656    /// # Cost
1657    ///
1658    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory
1659    ///  + cost of reading u128, method_name and arguments from the memory`
1660    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1661    pub fn promise_batch_action_function_call(
1662        &mut self,
1663        promise_idx: u64,
1664        method_name_len: u64,
1665        method_name_ptr: u64,
1666        arguments_len: u64,
1667        arguments_ptr: u64,
1668        amount_ptr: u64,
1669        gas: Gas,
1670    ) -> Result<()> {
1671        self.promise_batch_action_function_call_weight(
1672            promise_idx,
1673            method_name_len,
1674            method_name_ptr,
1675            arguments_len,
1676            arguments_ptr,
1677            amount_ptr,
1678            gas,
1679            0,
1680        )
1681    }
1682
1683    /// Appends `FunctionCall` action to the batch of actions for the given promise pointed by
1684    /// `promise_idx`. This function allows not specifying a specific gas value and allowing the
1685    /// runtime to assign remaining gas based on a weight.
1686    ///
1687    /// # Gas
1688    ///
1689    /// Gas can be specified using a static amount, a weight of remaining prepaid gas, or a mixture
1690    /// of both. To omit a static gas amount, `0` can be passed for the `gas` parameter.
1691    /// To omit assigning remaining gas, `0` can be passed as the `gas_weight` parameter.
1692    ///
1693    /// The gas weight parameter works as the following:
1694    ///
1695    /// All unused prepaid gas from the current function call is split among all function calls
1696    /// which supply this gas weight. The amount attached to each respective call depends on the
1697    /// value of the weight.
1698    ///
1699    /// For example, if 40 gas is leftover from the current method call and three functions specify
1700    /// the weights 1, 5, 2 then 5, 25, 10 gas will be added to each function call respectively,
1701    /// using up all remaining available gas.
1702    ///
1703    /// If the `gas_weight` parameter is set as a large value, the amount of distributed gas
1704    /// to each action can be 0 or a very low value because the amount of gas per weight is
1705    /// based on the floor division of the amount of gas by the sum of weights.
1706    ///
1707    /// Any remaining gas will be distributed to the last scheduled function call with a weight
1708    /// specified.
1709    ///
1710    /// # Errors
1711    ///
1712    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1713    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1714    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1715    /// * If `method_name_len + method_name_ptr` or `arguments_len + arguments_ptr` or
1716    /// `amount_ptr + 16` points outside the memory of the guest or host returns
1717    /// `MemoryAccessViolation`.
1718    /// * If called as view function returns `ProhibitedInView`.
1719    pub fn promise_batch_action_function_call_weight(
1720        &mut self,
1721        promise_idx: u64,
1722        method_name_len: u64,
1723        method_name_ptr: u64,
1724        arguments_len: u64,
1725        arguments_ptr: u64,
1726        amount_ptr: u64,
1727        gas: Gas,
1728        gas_weight: u64,
1729    ) -> Result<()> {
1730        self.gas_counter.pay_base(base)?;
1731        if self.context.is_view() {
1732            return Err(HostError::ProhibitedInView {
1733                method_name: "promise_batch_action_function_call".to_string(),
1734            }
1735            .into());
1736        }
1737        let amount = self.memory.get_u128(&mut self.gas_counter, amount_ptr)?;
1738        let method_name = get_memory_or_register!(self, method_name_ptr, method_name_len)?;
1739        if method_name.is_empty() {
1740            return Err(HostError::EmptyMethodName.into());
1741        }
1742        let arguments = get_memory_or_register!(self, arguments_ptr, arguments_len)?;
1743
1744        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1745
1746        let method_name = method_name.into_owned();
1747        let arguments = arguments.into_owned();
1748        // Input can't be large enough to overflow
1749        let num_bytes = method_name.len() as u64 + arguments.len() as u64;
1750        self.pay_action_base(ActionCosts::function_call_base, sir)?;
1751        self.pay_action_per_byte(ActionCosts::function_call_byte, num_bytes, sir)?;
1752        // Prepaid gas
1753        self.gas_counter.prepay_gas(gas)?;
1754
1755        self.deduct_balance(amount)?;
1756
1757        self.ext.append_action_function_call_weight(
1758            receipt_idx,
1759            method_name,
1760            arguments,
1761            amount,
1762            gas,
1763            GasWeight(gas_weight),
1764        )
1765    }
1766
1767    /// Appends `Transfer` action to the batch of actions for the given promise pointed by
1768    /// `promise_idx`.
1769    ///
1770    /// # Errors
1771    ///
1772    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1773    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1774    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1775    /// * If `amount_ptr + 16` points outside the memory of the guest or host returns
1776    /// `MemoryAccessViolation`.
1777    /// * If called as view function returns `ProhibitedInView`.
1778    ///
1779    /// # Cost
1780    ///
1781    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading u128 from memory `
1782    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1783    pub fn promise_batch_action_transfer(
1784        &mut self,
1785        promise_idx: u64,
1786        amount_ptr: u64,
1787    ) -> Result<()> {
1788        self.gas_counter.pay_base(base)?;
1789        if self.context.is_view() {
1790            return Err(HostError::ProhibitedInView {
1791                method_name: "promise_batch_action_transfer".to_string(),
1792            }
1793            .into());
1794        }
1795        let amount = self.memory.get_u128(&mut self.gas_counter, amount_ptr)?;
1796
1797        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1798        let receiver_id = self.ext.get_receipt_receiver(receipt_idx);
1799        let send_fee = transfer_send_fee(
1800            self.fees_config,
1801            sir,
1802            self.config.implicit_account_creation,
1803            self.config.eth_accounts,
1804            receiver_id.get_account_type(),
1805        );
1806        let exec_fee = transfer_exec_fee(
1807            self.fees_config,
1808            self.config.implicit_account_creation,
1809            self.config.eth_accounts,
1810            receiver_id.get_account_type(),
1811        );
1812        let burn_gas = send_fee;
1813        let use_gas = burn_gas.checked_add(exec_fee).ok_or(HostError::IntegerOverflow)?;
1814        self.gas_counter.pay_action_accumulated(burn_gas, use_gas, ActionCosts::transfer)?;
1815
1816        self.deduct_balance(amount)?;
1817
1818        self.ext.append_action_transfer(receipt_idx, amount)?;
1819        Ok(())
1820    }
1821
1822    /// Appends `Stake` action to the batch of actions for the given promise pointed by
1823    /// `promise_idx`.
1824    ///
1825    /// # Errors
1826    ///
1827    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1828    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1829    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1830    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1831    /// * If `amount_ptr + 16` or `public_key_len + public_key_ptr` points outside the memory of the
1832    /// guest or host returns `MemoryAccessViolation`.
1833    /// * If called as view function returns `ProhibitedInView`.
1834    ///
1835    /// # Cost
1836    ///
1837    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1838    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1839    pub fn promise_batch_action_stake(
1840        &mut self,
1841        promise_idx: u64,
1842        amount_ptr: u64,
1843        public_key_len: u64,
1844        public_key_ptr: u64,
1845    ) -> Result<()> {
1846        self.gas_counter.pay_base(base)?;
1847        if self.context.is_view() {
1848            return Err(HostError::ProhibitedInView {
1849                method_name: "promise_batch_action_stake".to_string(),
1850            }
1851            .into());
1852        }
1853        let amount = self.memory.get_u128(&mut self.gas_counter, amount_ptr)?;
1854        let public_key = self.get_public_key(public_key_ptr, public_key_len)?;
1855        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1856        self.pay_action_base(ActionCosts::pledge, sir)?;
1857        self.ext.append_action_pledge(receipt_idx, amount, public_key.decode()?);
1858        Ok(())
1859    }
1860
1861    /// Appends `AddKey` action to the batch of actions for the given promise pointed by
1862    /// `promise_idx`. The access key will have `FullAccess` permission.
1863    ///
1864    /// # Errors
1865    ///
1866    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1867    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1868    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1869    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1870    /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host
1871    /// returns `MemoryAccessViolation`.
1872    /// * If called as view function returns `ProhibitedInView`.
1873    ///
1874    /// # Cost
1875    ///
1876    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1877    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1878    pub fn promise_batch_action_add_key_with_full_access(
1879        &mut self,
1880        promise_idx: u64,
1881        public_key_len: u64,
1882        public_key_ptr: u64,
1883        nonce: u64,
1884    ) -> Result<()> {
1885        self.gas_counter.pay_base(base)?;
1886        if self.context.is_view() {
1887            return Err(HostError::ProhibitedInView {
1888                method_name: "promise_batch_action_add_key_with_full_access".to_string(),
1889            }
1890            .into());
1891        }
1892        let public_key = self.get_public_key(public_key_ptr, public_key_len)?;
1893        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1894        self.pay_action_base(ActionCosts::add_full_access_key, sir)?;
1895        self.ext.append_action_add_key_with_full_access(receipt_idx, public_key.decode()?, nonce);
1896        Ok(())
1897    }
1898
1899    /// Appends `AddKey` action to the batch of actions for the given promise pointed by
1900    /// `promise_idx`. The access key will have `FunctionCall` permission.
1901    ///
1902    /// # Errors
1903    ///
1904    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1905    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1906    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1907    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1908    /// * If `public_key_len + public_key_ptr`, `allowance_ptr + 16`,
1909    /// `receiver_id_len + receiver_id_ptr` or `method_names_len + method_names_ptr` points outside
1910    /// the memory of the guest or host returns `MemoryAccessViolation`.
1911    /// * If called as view function returns `ProhibitedInView`.
1912    ///
1913    /// # Cost
1914    ///
1915    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory
1916    ///  + cost of reading u128, method_names and public key from the memory + cost of reading and parsing account name`
1917    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1918    pub fn promise_batch_action_add_key_with_function_call(
1919        &mut self,
1920        promise_idx: u64,
1921        public_key_len: u64,
1922        public_key_ptr: u64,
1923        nonce: u64,
1924        allowance_ptr: u64,
1925        receiver_id_len: u64,
1926        receiver_id_ptr: u64,
1927        method_names_len: u64,
1928        method_names_ptr: u64,
1929    ) -> Result<()> {
1930        self.gas_counter.pay_base(base)?;
1931        if self.context.is_view() {
1932            return Err(HostError::ProhibitedInView {
1933                method_name: "promise_batch_action_add_key_with_function_call".to_string(),
1934            }
1935            .into());
1936        }
1937        let public_key = self.get_public_key(public_key_ptr, public_key_len)?;
1938        let allowance = self.memory.get_u128(&mut self.gas_counter, allowance_ptr)?;
1939        let allowance = if allowance > 0 { Some(allowance) } else { None };
1940        let receiver_id = self.read_and_parse_account_id(receiver_id_ptr, receiver_id_len)?;
1941        let raw_method_names = get_memory_or_register!(self, method_names_ptr, method_names_len)?;
1942        let method_names = split_method_names(&raw_method_names)?;
1943
1944        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1945
1946        // +1 is to account for null-terminating characters.
1947        let num_bytes = method_names.iter().map(|v| v.len() as u64 + 1).sum::<u64>();
1948        self.pay_action_base(ActionCosts::add_function_call_key_base, sir)?;
1949        self.pay_action_per_byte(ActionCosts::add_function_call_key_byte, num_bytes, sir)?;
1950
1951        self.ext.append_action_add_key_with_function_call(
1952            receipt_idx,
1953            public_key.decode()?,
1954            nonce,
1955            allowance,
1956            receiver_id,
1957            method_names,
1958        )?;
1959        Ok(())
1960    }
1961
1962    /// Appends `DeleteKey` action to the batch of actions for the given promise pointed by
1963    /// `promise_idx`.
1964    ///
1965    /// # Errors
1966    ///
1967    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1968    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1969    /// `promise_and` returns `CannotAppendActionToJointPromise`.
1970    /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1971    /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host
1972    /// returns `MemoryAccessViolation`.
1973    /// * If called as view function returns `ProhibitedInView`.
1974    ///
1975    /// # Cost
1976    ///
1977    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1978    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1979    pub fn promise_batch_action_delete_key(
1980        &mut self,
1981        promise_idx: u64,
1982        public_key_len: u64,
1983        public_key_ptr: u64,
1984    ) -> Result<()> {
1985        self.gas_counter.pay_base(base)?;
1986        if self.context.is_view() {
1987            return Err(HostError::ProhibitedInView {
1988                method_name: "promise_batch_action_delete_key".to_string(),
1989            }
1990            .into());
1991        }
1992        let public_key = self.get_public_key(public_key_ptr, public_key_len)?;
1993        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1994        self.pay_action_base(ActionCosts::delete_key, sir)?;
1995        self.ext.append_action_delete_key(receipt_idx, public_key.decode()?);
1996        Ok(())
1997    }
1998
1999    /// Appends `DeleteAccount` action to the batch of actions for the given promise pointed by
2000    /// `promise_idx`.
2001    ///
2002    /// # Errors
2003    ///
2004    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
2005    /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
2006    /// `promise_and` returns `CannotAppendActionToJointPromise`.
2007    /// * If `beneficiary_id_len + beneficiary_id_ptr` points outside the memory of the guest or
2008    /// host returns `MemoryAccessViolation`.
2009    /// * If called as view function returns `ProhibitedInView`.
2010    ///
2011    /// # Cost
2012    ///
2013    /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading and parsing account id from memory `
2014    /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes + fees for transferring funds to the beneficiary`
2015    pub fn promise_batch_action_delete_account(
2016        &mut self,
2017        promise_idx: u64,
2018        beneficiary_id_len: u64,
2019        beneficiary_id_ptr: u64,
2020    ) -> Result<()> {
2021        self.gas_counter.pay_base(base)?;
2022        if self.context.is_view() {
2023            return Err(HostError::ProhibitedInView {
2024                method_name: "promise_batch_action_delete_account".to_string(),
2025            }
2026            .into());
2027        }
2028        let beneficiary_id =
2029            self.read_and_parse_account_id(beneficiary_id_ptr, beneficiary_id_len)?;
2030
2031        let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
2032        self.pay_action_base(ActionCosts::delete_account, sir)?;
2033
2034        self.ext.append_action_delete_account(receipt_idx, beneficiary_id)?;
2035        Ok(())
2036    }
2037
2038    /// If the current function is invoked by a callback we can access the execution results of the
2039    /// promises that caused the callback. This function returns the number of complete and
2040    /// incomplete callbacks.
2041    ///
2042    /// Note, we are only going to have incomplete callbacks once we have promise_or combinator.
2043    ///
2044    ///
2045    /// * If there is only one callback returns `1`;
2046    /// * If there are multiple callbacks (e.g. created through `promise_and`) returns their number;
2047    /// * If the function was called not through the callback returns `0`.
2048    ///
2049    /// # Cost
2050    ///
2051    /// `base`
2052    pub fn promise_results_count(&mut self) -> Result<u64> {
2053        self.gas_counter.pay_base(base)?;
2054        if self.context.is_view() {
2055            return Err(HostError::ProhibitedInView {
2056                method_name: "promise_results_count".to_string(),
2057            }
2058            .into());
2059        }
2060        Ok(self.promise_results.len() as _)
2061    }
2062
2063    /// If the current function is invoked by a callback we can access the execution results of the
2064    /// promises that caused the callback. This function returns the result in blob format and
2065    /// places it into the register.
2066    ///
2067    /// * If promise result is complete and successful copies its blob into the register;
2068    /// * If promise result is complete and failed or incomplete keeps register unused;
2069    ///
2070    /// # Returns
2071    ///
2072    /// * If promise result is not complete returns `0`;
2073    /// * If promise result is complete and successful returns `1`;
2074    /// * If promise result is complete and failed returns `2`.
2075    ///
2076    /// # Errors
2077    ///
2078    /// * If `result_id` does not correspond to an existing result returns `InvalidPromiseResultIndex`;
2079    /// * If copying the blob exhausts the memory limit it returns `MemoryAccessViolation`.
2080    /// * If called as view function returns `ProhibitedInView`.
2081    ///
2082    /// # Cost
2083    ///
2084    /// `base + cost of writing data into a register`
2085    pub fn promise_result(&mut self, result_idx: u64, register_id: u64) -> Result<u64> {
2086        self.gas_counter.pay_base(base)?;
2087        if self.context.is_view() {
2088            return Err(
2089                HostError::ProhibitedInView { method_name: "promise_result".to_string() }.into()
2090            );
2091        }
2092        match self
2093            .promise_results
2094            .get(result_idx as usize)
2095            .ok_or(HostError::InvalidPromiseResultIndex { result_idx })?
2096        {
2097            PromiseResult::NotReady => Ok(0),
2098            PromiseResult::Successful(data) => {
2099                self.registers.set(
2100                    &mut self.gas_counter,
2101                    &self.config.limit_config,
2102                    register_id,
2103                    data.as_slice(),
2104                )?;
2105                Ok(1)
2106            }
2107            PromiseResult::Failed => Ok(2),
2108        }
2109    }
2110
2111    /// When promise `promise_idx` finishes executing its result is considered to be the result of
2112    /// the current function.
2113    ///
2114    /// # Errors
2115    ///
2116    /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
2117    /// * If called as view function returns `ProhibitedInView`.
2118    ///
2119    /// # Cost
2120    ///
2121    /// `base + promise_return`
2122    pub fn promise_return(&mut self, promise_idx: u64) -> Result<()> {
2123        self.gas_counter.pay_base(base)?;
2124        self.gas_counter.pay_base(promise_return)?;
2125        if self.context.is_view() {
2126            return Err(
2127                HostError::ProhibitedInView { method_name: "promise_return".to_string() }.into()
2128            );
2129        }
2130        match self
2131            .promises
2132            .get(promise_idx as usize)
2133            .ok_or(HostError::InvalidPromiseIndex { promise_idx })?
2134        {
2135            Promise::Receipt(receipt_idx) => {
2136                self.return_data = ReturnData::ReceiptIndex(*receipt_idx);
2137                Ok(())
2138            }
2139            Promise::NotReceipt(_) => Err(HostError::CannotReturnJointPromise.into()),
2140        }
2141    }
2142
2143    // #####################
2144    // # Miscellaneous API #
2145    // #####################
2146
2147    /// Sets the blob of data as the return value of the contract.
2148    ///
2149    /// # Errors
2150    ///
2151    /// * If `value_len + value_ptr` exceeds the memory container or points to an unused register it
2152    ///   returns `MemoryAccessViolation`.
2153    /// * if the length of the returned data exceeds `max_length_returned_data` returns
2154    ///   `ReturnedValueLengthExceeded`.
2155    ///
2156    /// # Cost
2157    /// `base + cost of reading return value from memory or register + dispatch&exec cost per byte of the data sent * num data receivers`
2158    pub fn value_return(&mut self, value_len: u64, value_ptr: u64) -> Result<()> {
2159        self.gas_counter.pay_base(base)?;
2160        let return_val = get_memory_or_register!(self, value_ptr, value_len)?;
2161        let mut burn_gas: Gas = 0;
2162        let num_bytes = return_val.len() as u64;
2163        if num_bytes > self.config.limit_config.max_length_returned_data {
2164            return Err(HostError::ReturnedValueLengthExceeded {
2165                length: num_bytes,
2166                limit: self.config.limit_config.max_length_returned_data,
2167            }
2168            .into());
2169        }
2170        for data_receiver in &self.context.output_data_receivers {
2171            let sir = data_receiver == &self.context.current_account_id;
2172            // We deduct for execution here too, because if we later have an OR combinator
2173            // for promises then we might have some valid data receipts that arrive too late
2174            // to be picked up by the execution that waits on them (because it has started
2175            // after it receives the first data receipt) and then we need to issue a special
2176            // refund in this situation. Which we avoid by just paying for execution of
2177            // data receipt that might not be performed.
2178            // The gas here is considered burnt, cause we'll prepay for it upfront.
2179            burn_gas = burn_gas
2180                .checked_add(
2181                    self.fees_config
2182                        .fee(ActionCosts::new_data_receipt_byte)
2183                        .send_fee(sir)
2184                        .checked_add(
2185                            self.fees_config.fee(ActionCosts::new_data_receipt_byte).exec_fee(),
2186                        )
2187                        .ok_or(HostError::IntegerOverflow)?
2188                        .checked_mul(num_bytes)
2189                        .ok_or(HostError::IntegerOverflow)?,
2190                )
2191                .ok_or(HostError::IntegerOverflow)?;
2192        }
2193        self.gas_counter.pay_action_accumulated(
2194            burn_gas,
2195            burn_gas,
2196            ActionCosts::new_data_receipt_byte,
2197        )?;
2198        self.return_data = ReturnData::Value(return_val.into_owned());
2199        Ok(())
2200    }
2201
2202    /// Terminates the execution of the program with panic `GuestPanic`.
2203    ///
2204    /// # Cost
2205    ///
2206    /// `base`
2207    pub fn panic(&mut self) -> Result<()> {
2208        self.gas_counter.pay_base(base)?;
2209        Err(HostError::GuestPanic { panic_msg: "explicit guest panic".to_string() }.into())
2210    }
2211
2212    /// Guest panics with the UTF-8 encoded string.
2213    /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
2214    ///
2215    /// # Errors
2216    ///
2217    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
2218    /// * If string is not UTF-8 returns `BadUtf8`.
2219    /// * If string is longer than `max_log_len` returns `TotalLogLengthExceeded`.
2220    ///
2221    /// # Cost
2222    /// `base + cost of reading and decoding a utf8 string`
2223    pub fn panic_utf8(&mut self, len: u64, ptr: u64) -> Result<()> {
2224        self.gas_counter.pay_base(base)?;
2225        Err(HostError::GuestPanic { panic_msg: self.get_utf8_string(len, ptr)? }.into())
2226    }
2227
2228    /// Logs the UTF-8 encoded string.
2229    /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
2230    ///
2231    /// # Errors
2232    ///
2233    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
2234    /// * If string is not UTF-8 returns `BadUtf8`.
2235    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
2236    ///   `TotalLogLengthExceeded`.
2237    /// * If the total number of logs will exceed the `max_number_logs` returns
2238    ///   `NumberOfLogsExceeded`.
2239    ///
2240    /// # Cost
2241    ///
2242    /// `base + log_base + log_byte + num_bytes + utf8 decoding cost`
2243    pub fn log_utf8(&mut self, len: u64, ptr: u64) -> Result<()> {
2244        self.gas_counter.pay_base(base)?;
2245        self.check_can_add_a_log_message()?;
2246        let message = self.get_utf8_string(len, ptr)?;
2247        self.gas_counter.pay_base(log_base)?;
2248        self.gas_counter.pay_per(log_byte, message.len() as u64)?;
2249        self.checked_push_log(message)
2250    }
2251
2252    /// Logs the UTF-16 encoded string. If `len == u64::MAX` then treats the string as
2253    /// null-terminated with two-byte sequence of `0x00 0x00`.
2254    ///
2255    /// # Errors
2256    ///
2257    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
2258    /// * If string is not UTF-16 returns `BadUtf16`.
2259    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
2260    ///   `TotalLogLengthExceeded`.
2261    /// * If the total number of logs will exceed the `max_number_logs` returns
2262    ///   `NumberOfLogsExceeded`.
2263    ///
2264    /// # Cost
2265    ///
2266    /// `base + log_base + log_byte * num_bytes + utf16 decoding cost`
2267    pub fn log_utf16(&mut self, len: u64, ptr: u64) -> Result<()> {
2268        self.gas_counter.pay_base(base)?;
2269        self.check_can_add_a_log_message()?;
2270        let message = self.get_utf16_string(len, ptr)?;
2271        self.gas_counter.pay_base(log_base)?;
2272        // Let's not use `encode_utf16` for gas per byte here, since it's a lot of compute.
2273        self.gas_counter.pay_per(log_byte, message.len() as u64)?;
2274        self.checked_push_log(message)
2275    }
2276
2277    /// Special import kept for compatibility with AssemblyScript contracts. Not called by smart
2278    /// contracts directly, but instead called by the code generated by AssemblyScript.
2279    ///
2280    /// # Errors
2281    ///
2282    /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
2283    /// * If string is not UTF-8 returns `BadUtf8`.
2284    /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
2285    ///   `TotalLogLengthExceeded`.
2286    /// * If the total number of logs will exceed the `max_number_logs` returns
2287    ///   `NumberOfLogsExceeded`.
2288    ///
2289    /// # Cost
2290    ///
2291    /// `base +  log_base + log_byte * num_bytes + utf16 decoding cost`
2292    pub fn abort(&mut self, msg_ptr: u32, filename_ptr: u32, line: u32, col: u32) -> Result<()> {
2293        self.gas_counter.pay_base(base)?;
2294        if msg_ptr < 4 || filename_ptr < 4 {
2295            return Err(HostError::BadUTF16.into());
2296        }
2297        self.check_can_add_a_log_message()?;
2298
2299        // Underflow checked above.
2300        let msg_len = self.memory.get_u32(&mut self.gas_counter, (msg_ptr - 4) as u64)?;
2301        let filename_len = self.memory.get_u32(&mut self.gas_counter, (filename_ptr - 4) as u64)?;
2302
2303        let msg = self.get_utf16_string(msg_len as u64, msg_ptr as u64)?;
2304        let filename = self.get_utf16_string(filename_len as u64, filename_ptr as u64)?;
2305
2306        let message = format!("{}, filename: \"{}\" line: {} col: {}", msg, filename, line, col);
2307        self.gas_counter.pay_base(log_base)?;
2308        self.gas_counter.pay_per(log_byte, message.as_bytes().len() as u64)?;
2309        self.checked_push_log(format!("ABORT: {}", message))?;
2310
2311        Err(HostError::GuestPanic { panic_msg: message }.into())
2312    }
2313
2314    // ###############
2315    // # Storage API #
2316    // ###############
2317
2318    /// Reads account id from the given location in memory.
2319    ///
2320    /// # Errors
2321    ///
2322    /// * If account is not UTF-8 encoded then returns `BadUtf8`;
2323    /// * If account is not valid then returns `InvalidAccountId`.
2324    ///
2325    /// # Cost
2326    ///
2327    /// This is a helper function that encapsulates the following costs:
2328    /// cost of reading buffer from register or memory,
2329    /// `utf8_decoding_base + utf8_decoding_byte * num_bytes`.
2330    fn read_and_parse_account_id(&mut self, ptr: u64, len: u64) -> Result<AccountId> {
2331        let buf = get_memory_or_register!(self, ptr, len)?;
2332        self.gas_counter.pay_base(utf8_decoding_base)?;
2333        self.gas_counter.pay_per(utf8_decoding_byte, buf.len() as u64)?;
2334
2335        // We return an illegally constructed AccountId here for the sake of ensuring
2336        // backwards compatibility. For paths previously involving validation, like receipts
2337        // we retain validation further down the line in node-runtime/verifier.rs#fn(validate_receipt)
2338        // mimicing previous behaviour.
2339        let account_id = String::from_utf8(buf.into_owned())
2340            .map(
2341                #[allow(deprecated)]
2342                AccountId::new_unvalidated,
2343            )
2344            .map_err(|_| HostError::BadUTF8)?;
2345        Ok(account_id)
2346    }
2347
2348    /// Writes key-value into storage.
2349    /// * If key is not in use it inserts the key-value pair and does not modify the register. Returns `0`;
2350    /// * If key is in use it inserts the key-value and copies the old value into the `register_id`. Returns `1`.
2351    ///
2352    /// # Errors
2353    ///
2354    /// * If `key_len + key_ptr` or `value_len + value_ptr` exceeds the memory container or points
2355    ///   to an unused register it returns `MemoryAccessViolation`;
2356    /// * If returning the preempted value into the registers exceed the memory container it returns
2357    ///   `MemoryAccessViolation`.
2358    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2359    /// * If the length of the value exceeds `max_length_storage_value` returns
2360    ///   `ValueLengthExceeded`.
2361    /// * If called as view function returns `ProhibitedInView``.
2362    ///
2363    /// # Cost
2364    ///
2365    /// `base + storage_write_base + storage_write_key_byte * num_key_bytes + storage_write_value_byte * num_value_bytes
2366    /// + get_vec_from_memory_or_register_cost x 2`.
2367    ///
2368    /// If a value was evicted it costs additional `storage_write_value_evicted_byte * num_evicted_bytes + internal_write_register_cost`.
2369    pub fn storage_write(
2370        &mut self,
2371        key_len: u64,
2372        key_ptr: u64,
2373        value_len: u64,
2374        value_ptr: u64,
2375        register_id: u64,
2376    ) -> Result<u64> {
2377        self.gas_counter.pay_base(base)?;
2378        if self.context.is_view() {
2379            return Err(
2380                HostError::ProhibitedInView { method_name: "storage_write".to_string() }.into()
2381            );
2382        }
2383        self.gas_counter.pay_base(storage_write_base)?;
2384        let key = get_memory_or_register!(self, key_ptr, key_len)?;
2385        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2386            return Err(HostError::KeyLengthExceeded {
2387                length: key.len() as u64,
2388                limit: self.config.limit_config.max_length_storage_key,
2389            }
2390            .into());
2391        }
2392        let value = get_memory_or_register!(self, value_ptr, value_len)?;
2393        if value.len() as u64 > self.config.limit_config.max_length_storage_value {
2394            return Err(HostError::ValueLengthExceeded {
2395                length: value.len() as u64,
2396                limit: self.config.limit_config.max_length_storage_value,
2397            }
2398            .into());
2399        }
2400        self.gas_counter.pay_per(storage_write_key_byte, key.len() as u64)?;
2401        self.gas_counter.pay_per(storage_write_value_byte, value.len() as u64)?;
2402        let nodes_before = self.ext.get_trie_nodes_count();
2403        // For storage write, we need to first perform a read on the key to calculate the TTN cost.
2404        // This storage_get must be performed through trie instead of through FlatStorage
2405        let evicted_ptr = self.ext.storage_get(&key, StorageGetMode::Trie)?;
2406        let evicted =
2407            Self::deref_value(&mut self.gas_counter, storage_write_evicted_byte, evicted_ptr)?;
2408        let nodes_delta = self
2409            .ext
2410            .get_trie_nodes_count()
2411            .checked_sub(&nodes_before)
2412            .ok_or(InconsistentStateError::IntegerOverflow)?;
2413
2414        #[cfg(feature = "io_trace")]
2415        tracing::trace!(
2416            target = "io_tracer",
2417            storage_op = "write",
2418            key = base64(&key),
2419            size = value_len,
2420            evicted_len = evicted.as_ref().map(Vec::len),
2421            tn_mem_reads = nodes_delta.mem_reads,
2422            tn_db_reads = nodes_delta.db_reads,
2423        );
2424
2425        self.gas_counter.add_trie_fees(&nodes_delta)?;
2426        self.ext.storage_set(&key, &value)?;
2427        let storage_config = &self.fees_config.storage_usage_config;
2428        match evicted {
2429            Some(old_value) => {
2430                // Inner value can't overflow, because the value length is limited.
2431                self.current_storage_usage = self
2432                    .current_storage_usage
2433                    .checked_sub(old_value.len() as u64)
2434                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2435                // Inner value can't overflow, because the value length is limited.
2436                self.current_storage_usage = self
2437                    .current_storage_usage
2438                    .checked_add(value.len() as u64)
2439                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2440                self.registers.set(
2441                    &mut self.gas_counter,
2442                    &self.config.limit_config,
2443                    register_id,
2444                    old_value,
2445                )?;
2446                Ok(1)
2447            }
2448            None => {
2449                // Inner value can't overflow, because the key/value length is limited.
2450                self.current_storage_usage = self
2451                    .current_storage_usage
2452                    .checked_add(
2453                        value.len() as u64
2454                            + key.len() as u64
2455                            + storage_config.num_extra_bytes_record,
2456                    )
2457                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2458                Ok(0)
2459            }
2460        }
2461    }
2462
2463    fn deref_value<'s>(
2464        gas_counter: &mut GasCounter,
2465        cost_per_byte: ExtCosts,
2466        value_ptr: Option<Box<dyn ValuePtr + 's>>,
2467    ) -> Result<Option<Vec<u8>>> {
2468        match value_ptr {
2469            Some(value_ptr) => {
2470                gas_counter.pay_per(cost_per_byte, value_ptr.len() as u64)?;
2471                value_ptr.deref().map(Some)
2472            }
2473            None => Ok(None),
2474        }
2475    }
2476
2477    /// Reads the value stored under the given key.
2478    /// * If key is used copies the content of the value into the `register_id`, even if the content
2479    ///   is zero bytes. Returns `1`;
2480    /// * If key is not present then does not modify the register. Returns `0`;
2481    ///
2482    /// # Errors
2483    ///
2484    /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it
2485    ///   returns `MemoryAccessViolation`;
2486    /// * If returning the preempted value into the registers exceed the memory container it returns
2487    ///   `MemoryAccessViolation`.
2488    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2489    ///
2490    /// # Cost
2491    ///
2492    /// `base + storage_read_base + storage_read_key_byte * num_key_bytes + storage_read_value_byte + num_value_bytes
2493    ///  cost to read key from register + cost to write value into register`.
2494    pub fn storage_read(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result<u64> {
2495        self.gas_counter.pay_base(base)?;
2496        self.gas_counter.pay_base(storage_read_base)?;
2497        let key = get_memory_or_register!(self, key_ptr, key_len)?;
2498        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2499            return Err(HostError::KeyLengthExceeded {
2500                length: key.len() as u64,
2501                limit: self.config.limit_config.max_length_storage_key,
2502            }
2503            .into());
2504        }
2505        self.gas_counter.pay_per(storage_read_key_byte, key.len() as u64)?;
2506        let nodes_before = self.ext.get_trie_nodes_count();
2507        let read = self.ext.storage_get(&key, self.config.storage_get_mode);
2508        let nodes_delta = self
2509            .ext
2510            .get_trie_nodes_count()
2511            .checked_sub(&nodes_before)
2512            .ok_or(InconsistentStateError::IntegerOverflow)?;
2513        self.gas_counter.add_trie_fees(&nodes_delta)?;
2514        let read = Self::deref_value(&mut self.gas_counter, storage_read_value_byte, read?)?;
2515
2516        #[cfg(feature = "io_trace")]
2517        tracing::trace!(
2518            target = "io_tracer",
2519            storage_op = "read",
2520            key = base64(&key),
2521            size = read.as_ref().map(Vec::len),
2522            tn_db_reads = nodes_delta.db_reads,
2523            tn_mem_reads = nodes_delta.mem_reads,
2524        );
2525
2526        match read {
2527            Some(value) => {
2528                self.registers.set(
2529                    &mut self.gas_counter,
2530                    &self.config.limit_config,
2531                    register_id,
2532                    value,
2533                )?;
2534                Ok(1)
2535            }
2536            None => Ok(0),
2537        }
2538    }
2539
2540    /// Removes the value stored under the given key.
2541    /// * If key is used, removes the key-value from the trie and copies the content of the value
2542    ///   into the `register_id`, even if the content is zero bytes. Returns `1`;
2543    /// * If key is not present then does not modify the register. Returns `0`.
2544    ///
2545    /// # Errors
2546    ///
2547    /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it
2548    ///   returns `MemoryAccessViolation`;
2549    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`;
2550    /// * If returning the preempted value into the registers exceed the memory container it returns
2551    ///   `MemoryAccessViolation`.
2552    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2553    /// * If called as view function returns `ProhibitedInView``.
2554    ///
2555    /// # Cost
2556    ///
2557    /// `base + storage_remove_base + storage_remove_key_byte * num_key_bytes + storage_remove_ret_value_byte * num_value_bytes
2558    /// + cost to read the key + cost to write the value`.
2559    pub fn storage_remove(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result<u64> {
2560        self.gas_counter.pay_base(base)?;
2561        if self.context.is_view() {
2562            return Err(
2563                HostError::ProhibitedInView { method_name: "storage_remove".to_string() }.into()
2564            );
2565        }
2566        self.gas_counter.pay_base(storage_remove_base)?;
2567        let key = get_memory_or_register!(self, key_ptr, key_len)?;
2568        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2569            return Err(HostError::KeyLengthExceeded {
2570                length: key.len() as u64,
2571                limit: self.config.limit_config.max_length_storage_key,
2572            }
2573            .into());
2574        }
2575        self.gas_counter.pay_per(storage_remove_key_byte, key.len() as u64)?;
2576        let nodes_before = self.ext.get_trie_nodes_count();
2577        // To delete a key, we need to first perform a read on the key to calculate the TTN cost.
2578        // This storage_get must be performed through trie instead of through FlatStorage
2579        let removed_ptr = self.ext.storage_get(&key, StorageGetMode::Trie)?;
2580        let removed =
2581            Self::deref_value(&mut self.gas_counter, storage_remove_ret_value_byte, removed_ptr)?;
2582
2583        self.ext.storage_remove(&key)?;
2584        let nodes_delta = self
2585            .ext
2586            .get_trie_nodes_count()
2587            .checked_sub(&nodes_before)
2588            .ok_or(InconsistentStateError::IntegerOverflow)?;
2589
2590        #[cfg(feature = "io_trace")]
2591        tracing::trace!(
2592            target = "io_tracer",
2593            storage_op = "remove",
2594            key = base64(&key),
2595            evicted_len = removed.as_ref().map(Vec::len),
2596            tn_mem_reads = nodes_delta.mem_reads,
2597            tn_db_reads = nodes_delta.db_reads,
2598        );
2599
2600        self.gas_counter.add_trie_fees(&nodes_delta)?;
2601        let storage_config = &self.fees_config.storage_usage_config;
2602        match removed {
2603            Some(value) => {
2604                // Inner value can't overflow, because the key/value length is limited.
2605                self.current_storage_usage = self
2606                    .current_storage_usage
2607                    .checked_sub(
2608                        value.len() as u64
2609                            + key.len() as u64
2610                            + storage_config.num_extra_bytes_record,
2611                    )
2612                    .ok_or(InconsistentStateError::IntegerOverflow)?;
2613                self.registers.set(
2614                    &mut self.gas_counter,
2615                    &self.config.limit_config,
2616                    register_id,
2617                    value,
2618                )?;
2619                Ok(1)
2620            }
2621            None => Ok(0),
2622        }
2623    }
2624
2625    /// Checks if there is a key-value pair.
2626    /// * If key is used returns `1`, even if the value is zero bytes;
2627    /// * Otherwise returns `0`.
2628    ///
2629    /// # Errors
2630    ///
2631    /// * If `key_len + key_ptr` exceeds the memory container it returns `MemoryAccessViolation`.
2632    /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2633    ///
2634    /// # Cost
2635    ///
2636    /// `base + storage_has_key_base + storage_has_key_byte * num_bytes + cost of reading key`
2637    pub fn storage_has_key(&mut self, key_len: u64, key_ptr: u64) -> Result<u64> {
2638        self.gas_counter.pay_base(base)?;
2639        self.gas_counter.pay_base(storage_has_key_base)?;
2640        let key = get_memory_or_register!(self, key_ptr, key_len)?;
2641        if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2642            return Err(HostError::KeyLengthExceeded {
2643                length: key.len() as u64,
2644                limit: self.config.limit_config.max_length_storage_key,
2645            }
2646            .into());
2647        }
2648        self.gas_counter.pay_per(storage_has_key_byte, key.len() as u64)?;
2649        let nodes_before = self.ext.get_trie_nodes_count();
2650        let res = self.ext.storage_has_key(&key, self.config.storage_get_mode);
2651        let nodes_delta = self
2652            .ext
2653            .get_trie_nodes_count()
2654            .checked_sub(&nodes_before)
2655            .ok_or(InconsistentStateError::IntegerOverflow)?;
2656
2657        #[cfg(feature = "io_trace")]
2658        tracing::trace!(
2659            target = "io_tracer",
2660            storage_op = "exists",
2661            key = base64(&key),
2662            tn_mem_reads = nodes_delta.mem_reads,
2663            tn_db_reads = nodes_delta.db_reads,
2664        );
2665
2666        self.gas_counter.add_trie_fees(&nodes_delta)?;
2667        Ok(res? as u64)
2668    }
2669
2670    /// Debug print given utf-8 string to node log. It's only available in Sandbox node
2671    ///
2672    /// # Errors
2673    ///
2674    /// * If string is not UTF-8 returns `BadUtf8`
2675    /// * If the log is over available memory in wasm runner, returns `MemoryAccessViolation`
2676    ///
2677    /// # Cost
2678    ///
2679    /// 0
2680    #[cfg(feature = "sandbox")]
2681    pub fn sandbox_debug_log(&mut self, len: u64, ptr: u64) -> Result<()> {
2682        let message = self.sandbox_get_utf8_string(len, ptr)?;
2683        tracing::debug!(target: "sandbox", message = &message[..]);
2684        Ok(())
2685    }
2686
2687    /// DEPRECATED
2688    /// Creates an iterator object inside the host. Returns the identifier that uniquely
2689    /// differentiates the given iterator from other iterators that can be simultaneously created.
2690    /// * It iterates over the keys that have the provided prefix. The order of iteration is defined
2691    ///   by the lexicographic order of the bytes in the keys;
2692    /// * If there are no keys, it creates an empty iterator, see below on empty iterators.
2693    ///
2694    /// # Errors
2695    ///
2696    /// * If `prefix_len + prefix_ptr` exceeds the memory container it returns
2697    ///   `MemoryAccessViolation`.
2698    /// * If the length of the prefix exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2699    ///
2700    /// # Cost
2701    ///
2702    /// `base + storage_iter_create_prefix_base + storage_iter_create_key_byte * num_prefix_bytes
2703    ///  cost of reading the prefix`.
2704    pub fn storage_iter_prefix(&mut self, _prefix_len: u64, _prefix_ptr: u64) -> Result<u64> {
2705        Err(VMLogicError::HostError(HostError::Deprecated {
2706            method_name: "storage_iter_prefix".to_string(),
2707        }))
2708    }
2709
2710    /// DEPRECATED
2711    /// Iterates over all key-values such that keys are between `start` and `end`, where `start` is
2712    /// inclusive and `end` is exclusive. Unless lexicographically `start < end`, it creates an
2713    /// empty iterator. Note, this definition allows for `start` or `end` keys to not actually exist
2714    /// on the given trie.
2715    ///
2716    /// # Errors
2717    ///
2718    /// * If `start_len + start_ptr` or `end_len + end_ptr` exceeds the memory container or points to
2719    ///   an unused register it returns `MemoryAccessViolation`.
2720    /// * If the length of the `start` exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2721    /// * If the length of the `end` exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2722    ///
2723    /// # Cost
2724    ///
2725    /// `base + storage_iter_create_range_base + storage_iter_create_from_byte * num_from_bytes
2726    ///  + storage_iter_create_to_byte * num_to_bytes + reading from prefix + reading to prefix`.
2727    pub fn storage_iter_range(
2728        &mut self,
2729        _start_len: u64,
2730        _start_ptr: u64,
2731        _end_len: u64,
2732        _end_ptr: u64,
2733    ) -> Result<u64> {
2734        Err(VMLogicError::HostError(HostError::Deprecated {
2735            method_name: "storage_iter_range".to_string(),
2736        }))
2737    }
2738
2739    /// DEPRECATED
2740    /// Advances iterator and saves the next key and value in the register.
2741    /// * If iterator is not empty (after calling next it points to a key-value), copies the key
2742    ///   into `key_register_id` and value into `value_register_id` and returns `1`;
2743    /// * If iterator is empty returns `0`;
2744    /// This allows us to iterate over the keys that have zero bytes stored in values.
2745    ///
2746    /// # Errors
2747    ///
2748    /// * If `key_register_id == value_register_id` returns `MemoryAccessViolation`;
2749    /// * If the registers exceed the memory limit returns `MemoryAccessViolation`;
2750    /// * If `iterator_id` does not correspond to an existing iterator returns `InvalidIteratorId`;
2751    /// * If between the creation of the iterator and calling `storage_iter_next` the range over
2752    ///   which it iterates was modified returns `IteratorWasInvalidated`. Specifically, if
2753    ///   `storage_write` or `storage_remove` was invoked on the key key such that:
2754    ///   * in case of `storage_iter_prefix`. `key` has the given prefix and:
2755    ///     * Iterator was not called next yet.
2756    ///     * `next` was already called on the iterator and it is currently pointing at the `key`
2757    ///       `curr` such that `curr <= key`.
2758    ///   * in case of `storage_iter_range`. `start<=key<end` and:
2759    ///     * Iterator was not called `next` yet.
2760    ///     * `next` was already called on the iterator and it is currently pointing at the key
2761    ///       `curr` such that `curr<=key<end`.
2762    ///
2763    /// # Cost
2764    ///
2765    /// `base + storage_iter_next_base + storage_iter_next_key_byte * num_key_bytes + storage_iter_next_value_byte * num_value_bytes
2766    ///  + writing key to register + writing value to register`.
2767    pub fn storage_iter_next(
2768        &mut self,
2769        _iterator_id: u64,
2770        _key_register_id: u64,
2771        _value_register_id: u64,
2772    ) -> Result<u64> {
2773        Err(VMLogicError::HostError(HostError::Deprecated {
2774            method_name: "storage_iter_next".to_string(),
2775        }))
2776    }
2777
2778    /// Computes the outcome of the execution.
2779    ///
2780    /// If `FunctionCallWeight` protocol feature (127) is enabled, unused gas will be
2781    /// distributed to functions that specify a gas weight. If there are no functions with
2782    /// a gas weight, the outcome will contain unused gas as usual.
2783    pub fn compute_outcome(self) -> VMOutcome {
2784        let burnt_gas = self.gas_counter.burnt_gas();
2785        let used_gas = self.gas_counter.used_gas();
2786
2787        let mut profile = self.gas_counter.profile_data();
2788        profile.compute_wasm_instruction_cost(burnt_gas);
2789        let compute_usage = profile.total_compute_usage(&self.config.ext_costs);
2790
2791        VMOutcome {
2792            balance: self.current_account_balance,
2793            storage_usage: self.current_storage_usage,
2794            return_data: self.return_data,
2795            burnt_gas,
2796            used_gas,
2797            compute_usage,
2798            logs: self.logs,
2799            profile,
2800            aborted: None,
2801        }
2802    }
2803
2804    /// Add a cost for loading the contract code in the VM.
2805    ///
2806    /// This cost does not consider the structure of the contract code, only the
2807    /// size. This is currently the only loading fee. A fee that takes the code
2808    /// structure into consideration could be added. But since that would have
2809    /// to happen after loading, we cannot pre-charge it. This is the main
2810    /// motivation to (only) have this simple fee.
2811    pub fn add_contract_loading_fee(&mut self, code_len: u64) -> Result<()> {
2812        self.gas_counter.pay_per(contract_loading_bytes, code_len)?;
2813        self.gas_counter.pay_base(contract_loading_base)
2814    }
2815
2816    /// Gets pointer to the fast gas counter.
2817    pub fn gas_counter_pointer(&mut self) -> *mut FastGasCounter {
2818        self.gas_counter.gas_counter_raw_ptr()
2819    }
2820
2821    /// Properly handles gas limit exceeded error.
2822    pub fn process_gas_limit(&mut self) -> HostError {
2823        let new_burn_gas = self.gas_counter.burnt_gas();
2824        let new_used_gas = self.gas_counter.used_gas();
2825        self.gas_counter.process_gas_limit(new_burn_gas, new_used_gas)
2826    }
2827
2828    /// A helper function to pay base cost gas fee for batching an action.
2829    pub fn pay_action_base(&mut self, action: ActionCosts, sir: bool) -> Result<()> {
2830        let base_fee = self.fees_config.fee(action);
2831        let burn_gas = base_fee.send_fee(sir);
2832        let use_gas =
2833            burn_gas.checked_add(base_fee.exec_fee()).ok_or(HostError::IntegerOverflow)?;
2834        self.gas_counter.pay_action_accumulated(burn_gas, use_gas, action)
2835    }
2836
2837    /// A helper function to pay per byte gas fee for batching an action.
2838    pub fn pay_action_per_byte(
2839        &mut self,
2840        action: ActionCosts,
2841        num_bytes: u64,
2842        sir: bool,
2843    ) -> Result<()> {
2844        let per_byte_fee = self.fees_config.fee(action);
2845        let burn_gas =
2846            num_bytes.checked_mul(per_byte_fee.send_fee(sir)).ok_or(HostError::IntegerOverflow)?;
2847        let use_gas = burn_gas
2848            .checked_add(
2849                num_bytes.checked_mul(per_byte_fee.exec_fee()).ok_or(HostError::IntegerOverflow)?,
2850            )
2851            .ok_or(HostError::IntegerOverflow)?;
2852        self.gas_counter.pay_action_accumulated(burn_gas, use_gas, action)
2853    }
2854
2855    /// VM independent setup before loading the executable.
2856    ///
2857    /// Does VM independent checks that happen after the instantiation of
2858    /// VMLogic but before loading the executable. This includes pre-charging gas
2859    /// costs for loading the executable, which depends on the size of the WASM code.
2860    pub fn before_loading_executable(
2861        &mut self,
2862        method_name: &str,
2863        wasm_code_bytes: usize,
2864    ) -> std::result::Result<(), super::errors::FunctionCallError> {
2865        if method_name.is_empty() {
2866            let error = super::errors::FunctionCallError::MethodResolveError(
2867                super::errors::MethodResolveError::MethodEmptyName,
2868            );
2869            return Err(error);
2870        }
2871        if self.config.fix_contract_loading_cost {
2872            if self.add_contract_loading_fee(wasm_code_bytes as u64).is_err() {
2873                let error =
2874                    super::errors::FunctionCallError::HostError(super::HostError::GasExceeded);
2875                return Err(error);
2876            }
2877        }
2878        Ok(())
2879    }
2880
2881    /// Legacy code to preserve old gas charging behaviour in old protocol versions.
2882    pub fn after_loading_executable(
2883        &mut self,
2884        wasm_code_bytes: usize,
2885    ) -> std::result::Result<(), super::errors::FunctionCallError> {
2886        if !self.config.fix_contract_loading_cost {
2887            if self.add_contract_loading_fee(wasm_code_bytes as u64).is_err() {
2888                return Err(super::errors::FunctionCallError::HostError(
2889                    super::HostError::GasExceeded,
2890                ));
2891            }
2892        }
2893        Ok(())
2894    }
2895}
2896
2897#[derive(PartialEq)]
2898pub struct VMOutcome {
2899    pub balance: Balance,
2900    pub storage_usage: StorageUsage,
2901    pub return_data: ReturnData,
2902    pub burnt_gas: Gas,
2903    pub used_gas: Gas,
2904    pub compute_usage: Compute,
2905    pub logs: Vec<String>,
2906    /// Data collected from making a contract call
2907    pub profile: ProfileDataV3,
2908    pub aborted: Option<FunctionCallError>,
2909}
2910
2911impl VMOutcome {
2912    /// Consumes the `VMLogic` object and computes the final outcome with the
2913    /// given error that stopped execution from finishing successfully.
2914    pub fn abort(logic: VMLogic, error: FunctionCallError) -> VMOutcome {
2915        let mut outcome = logic.compute_outcome();
2916        outcome.aborted = Some(error);
2917        outcome
2918    }
2919
2920    /// Consumes the `VMLogic` object and computes the final outcome for a
2921    /// successful execution.
2922    pub fn ok(logic: VMLogic) -> VMOutcome {
2923        logic.compute_outcome()
2924    }
2925
2926    /// Creates an outcome with a no-op outcome.
2927    pub fn nop_outcome(error: FunctionCallError) -> VMOutcome {
2928        VMOutcome {
2929            // Note: Balance and storage fields are ignored on a failed outcome.
2930            balance: 0,
2931            storage_usage: 0,
2932            // Note: Fields below are added or merged when processing the
2933            // outcome. With 0 or the empty set, those are no-ops.
2934            return_data: ReturnData::None,
2935            burnt_gas: 0,
2936            used_gas: 0,
2937            compute_usage: 0,
2938            logs: Vec::new(),
2939            profile: ProfileDataV3::default(),
2940            aborted: Some(error),
2941        }
2942    }
2943
2944    /// Like `Self::abort()` but without feature `FixContractLoadingCost` it
2945    /// will return a NOP outcome. This is used for backwards-compatibility only.
2946    pub fn abort_but_nop_outcome_in_old_protocol(
2947        logic: VMLogic,
2948        error: FunctionCallError,
2949    ) -> VMOutcome {
2950        if logic.config.fix_contract_loading_cost {
2951            Self::abort(logic, error)
2952        } else {
2953            Self::nop_outcome(error)
2954        }
2955    }
2956}
2957
2958impl std::fmt::Debug for VMOutcome {
2959    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2960        let return_data_str = match &self.return_data {
2961            ReturnData::None => "None".to_string(),
2962            ReturnData::ReceiptIndex(_) => "Receipt".to_string(),
2963            ReturnData::Value(v) => format!("Value [{} bytes]", v.len()),
2964        };
2965        write!(
2966            f,
2967            "VMOutcome: balance {} storage_usage {} return data {} burnt gas {} used gas {}",
2968            self.balance, self.storage_usage, return_data_str, self.burnt_gas, self.used_gas
2969        )?;
2970        if let Some(err) = &self.aborted {
2971            write!(f, " failed with {err}")?;
2972        }
2973        Ok(())
2974    }
2975}