concordium_smart_contract_engine/v0/
mod.rs

1//! Implementation of execution of V0 contracts.
2//!
3//! The main entrypoints in this module are
4//! - [`invoke_init`] for invoking an init function to create a new instance
5//! - [`invoke_receive`] for invoking an entrypoint of an existing instance
6//!
7//! These methods are intended to be used on [`Artifact`]'s obtained using
8//! [`instantiate_with_metering`](utils::instantiate_with_metering) using
9//! [`ConcordiumAllowedImports`] for handling imports.
10//!
11//! In addition to the above methods there are auxiliary helpers
12//! - [`invoke_init_from_artifact`] and [`invoke_receive_from_artifact`] which
13//!   first parse an [`Artifact`] and then run the corresponding `invoke_*`
14//!   function.
15//! - [`invoke_init_from_source`] and [`invoke_receive_from_source`] which first
16//!   parse and validate a Wasm module, then convert it to an [`Artifact`], and
17//!   then run it using the appropriate `invoke_*` function.
18//! - [`invoke_init_with_metering_from_source`] and
19//!   [`invoke_receive_with_metering_from_source`] which first parse and
20//!   validate the Wasm module, then inject cost metering instructions, and then
21//!   convert it to an [`Artifact`] and run it using the appropriate `invoke_*`
22//!   function.
23
24#[cfg(feature = "enable-ffi")]
25pub(crate) mod ffi;
26mod types;
27
28use crate::{constants, ExecResult, InterpreterEnergy, OutOfEnergy};
29use anyhow::{anyhow, bail, ensure};
30use concordium_contracts_common::*;
31use concordium_wasm::{
32    artifact::{Artifact, RunnableCode},
33    machine::{self, ExecutionOutcome, NoInterrupt},
34    utils,
35    validate::ValidationConfig,
36    CostConfiguration,
37};
38use machine::Value;
39use std::{collections::LinkedList, convert::TryInto, io::Write};
40pub use types::*;
41
42impl Logs {
43    /// Create an empty set of logs.
44    pub fn new() -> Self {
45        Self {
46            logs: LinkedList::new(),
47        }
48    }
49
50    /// The return value is
51    ///
52    /// - 0 if data was not logged because it would exceed maximum number of
53    ///   logs
54    /// - 1 if data was logged.
55    pub fn log_event(&mut self, event: Vec<u8>, limit_num_logs: bool) -> i32 {
56        let cur_len = self.logs.len();
57        if (!limit_num_logs && cur_len <= u32::MAX as usize) || cur_len < constants::MAX_NUM_LOGS {
58            self.logs.push_back(event);
59            1
60        } else {
61            0
62        }
63    }
64
65    /// Iterate over the logs in order, i.e., the log produced first is returned
66    /// first.
67    pub fn iterate(&self) -> impl Iterator<Item = &Vec<u8>> { self.logs.iter() }
68
69    #[cfg(feature = "enable-ffi")]
70    /// Serialize for the purposes of FFI transfer.
71    pub(crate) fn to_bytes(&self) -> Vec<u8> {
72        let len = self.logs.len();
73        let mut out = Vec::with_capacity(4 * len + 4);
74        out.extend_from_slice(&(len as u32).to_be_bytes());
75        for v in self.iterate() {
76            out.extend_from_slice(&(v.len() as u32).to_be_bytes());
77            out.extend_from_slice(v);
78        }
79        out
80    }
81}
82
83#[derive(Debug, Clone, Default)]
84/// The outcomes of a `v0` contract execution. This conceptually encodes an
85/// action tree using `and` and `or` combinators for inner nodes, and contract
86/// invocations and account transfers as leaves. The Default instance of this
87/// type constructs an empty list of outcomes, corresponding to an empty actions
88/// tree.
89pub(crate) struct Outcome {
90    pub cur_state: Vec<Action>,
91}
92
93impl Outcome {
94    pub fn new() -> Outcome { Self::default() }
95
96    /// Add a new `accept` action to the list.
97    pub(crate) fn accept(&mut self) -> u32 {
98        let response = self.cur_state.len();
99        self.cur_state.push(Action::Accept);
100        response as u32
101    }
102
103    /// Add a new transfer action to the list.
104    pub(crate) fn simple_transfer(&mut self, bytes: &[u8], micro_ccd: u64) -> ExecResult<u32> {
105        let response = self.cur_state.len();
106        let addr: [u8; 32] = bytes.try_into()?;
107        let to_addr = AccountAddress(addr);
108        let data = std::rc::Rc::new(SimpleTransferAction {
109            to_addr,
110            amount: Amount::from_micro_ccd(micro_ccd),
111        });
112        self.cur_state.push(Action::SimpleTransfer {
113            data,
114        });
115        Ok(response as u32)
116    }
117
118    /// Add an action to send a message to a smart contract.
119    pub(crate) fn send(
120        &mut self,
121        addr_index: u64,
122        addr_subindex: u64,
123        receive_name_bytes: &[u8],
124        micro_ccd: u64,
125        parameter_bytes: &[u8],
126        max_parameter_size: usize,
127    ) -> ExecResult<u32> {
128        let response = self.cur_state.len();
129
130        let name_str = std::str::from_utf8(receive_name_bytes)?;
131        let rn = ReceiveName::new(name_str)?;
132        let name = rn.to_owned();
133
134        ensure!(parameter_bytes.len() <= max_parameter_size, "Parameter exceeds max size.");
135        let parameter = OwnedParameter::new_unchecked(parameter_bytes.to_vec());
136
137        let to_addr = ContractAddress {
138            index:    addr_index,
139            subindex: addr_subindex,
140        };
141        let data = std::rc::Rc::new(SendAction {
142            to_addr,
143            name,
144            amount: Amount::from_micro_ccd(micro_ccd),
145            parameter,
146        });
147        self.cur_state.push(Action::Send {
148            data,
149        });
150        Ok(response as u32)
151    }
152
153    /// Combine the given two actions using the `and` combinator. The `l` is the
154    /// left action, `r` is the right one.
155    pub(crate) fn combine_and(&mut self, l: u32, r: u32) -> ExecResult<u32> {
156        let response = self.cur_state.len() as u32;
157        ensure!(l < response && r < response, "Combining unknown actions.");
158        self.cur_state.push(Action::And {
159            l,
160            r,
161        });
162        Ok(response)
163    }
164
165    /// Combine the given two actions using the `or` combinator. The `l` is the
166    /// left action, `r` is the right one.
167    pub(crate) fn combine_or(&mut self, l: u32, r: u32) -> ExecResult<u32> {
168        let response = self.cur_state.len() as u32;
169        ensure!(l < response && r < response, "Combining unknown actions.");
170        self.cur_state.push(Action::Or {
171            l,
172            r,
173        });
174        Ok(response)
175    }
176}
177
178impl State {
179    pub fn is_empty(&self) -> bool { self.state.is_empty() }
180
181    /// Create a new copy of the state.
182    pub fn new(st: Option<&[u8]>) -> Self {
183        match st {
184            None => Self {
185                state: Vec::new(),
186            },
187            Some(bytes) => Self {
188                state: Vec::from(bytes),
189            },
190        }
191    }
192
193    pub fn len(&self) -> u32 { self.state.len() as u32 }
194
195    pub(crate) fn write_state(&mut self, offset: u32, bytes: &[u8]) -> ExecResult<u32> {
196        let length = bytes.len();
197        ensure!(offset <= self.len(), "Cannot write past the offset.");
198        let offset = offset as usize;
199        let end =
200            offset.checked_add(length).ok_or_else(|| anyhow!("Writing past the end of memory."))?;
201        let end = std::cmp::min(end, constants::MAX_CONTRACT_STATE as usize) as u32;
202        if self.len() < end {
203            self.state.resize(end as usize, 0u8);
204        }
205        let written = (&mut self.state[offset..end as usize]).write(bytes)?;
206        Ok(written as u32)
207    }
208
209    pub(crate) fn load_state(&self, offset: u32, mut bytes: &mut [u8]) -> ExecResult<u32> {
210        let offset = offset as usize;
211        ensure!(offset <= self.state.len());
212        // Write on slices overwrites the buffer and returns how many bytes were
213        // written.
214        let amt = bytes.write(&self.state[offset..])?;
215        Ok(amt as u32)
216    }
217
218    pub(crate) fn resize_state(&mut self, new_size: u32) -> u32 {
219        if new_size > constants::MAX_CONTRACT_STATE {
220            0
221        } else {
222            self.state.resize(new_size as usize, 0u8);
223            1
224        }
225    }
226}
227
228/// A Wasm host that is used for executing `init` functions that create new
229/// contract instances.
230#[doc(hidden)] // Needed in benchmarks, but generally should not be used by
231               // users of the library.
232pub struct InitHost<ParamType, Ctx> {
233    /// Remaining energy for execution.
234    pub(crate) energy: InterpreterEnergy,
235    /// Remaining amount of activation frames.
236    /// In other words, how many more functions can we call in a nested way.
237    pub(crate) activation_frames: u32,
238    /// Logs produced during execution.
239    pub(crate) logs: Logs,
240    /// The contract's state.
241    pub(crate) state: State,
242    /// The parameter to the init method.
243    pub(crate) param: ParamType,
244    /// The init context for this invocation.
245    pub(crate) init_ctx: Ctx,
246    /// Whether there is a limit on the number of logs and sizes of return
247    /// values. Limit removed in P5.
248    pub(crate) limit_logs_and_return_values: bool,
249}
250
251impl<ParamType, Ctx> InitHost<ParamType, Ctx> {
252    /// Initialize a new [`InitHost`] ready to execute contract initialization
253    /// functions.
254    pub fn init(
255        energy: InterpreterEnergy,
256        param: ParamType,
257        init_ctx: Ctx,
258        limit_logs_and_return_values: bool,
259    ) -> Self {
260        Self {
261            energy,
262            activation_frames: constants::MAX_ACTIVATION_FRAMES,
263            logs: Logs::new(),
264            state: State::new(None),
265            param,
266            init_ctx,
267            limit_logs_and_return_values,
268        }
269    }
270}
271
272/// A Wasm host that is used for executing entrypoints of existing smart
273/// contract instances. This is analogous to the [`InitHost`].
274#[derive(Debug)]
275#[doc(hidden)] // Needed in benchmarks, but generally should not be used by
276               // users of the library.
277pub struct ReceiveHost<ParamType, Ctx> {
278    /// Remaining energy for execution.
279    pub(crate) energy: InterpreterEnergy,
280    /// Remaining amount of activation frames.
281    /// In other words, how many more functions can we call in a nested way.
282    pub(crate) activation_frames: u32,
283    /// Logs produced during execution.
284    pub(crate) logs: Logs,
285    /// The contract's state.
286    pub(crate) state: State,
287    /// The parameter to the receive method.
288    pub(crate) param: ParamType,
289    /// Outcomes of the execution, i.e., the actions tree.
290    pub(crate) outcomes: Outcome,
291    /// The receive context for this call.
292    pub(crate) receive_ctx: Ctx,
293    /// The maximum parameter size.
294    /// In P1-P4 it was 1024.
295    /// In P5+ it is 65535.
296    pub(crate) max_parameter_size: usize,
297    /// Whether there is a limit on the number of logs and sizes of return
298    /// values. Limit removed in P5.
299    pub(crate) limit_logs_and_return_values: bool,
300}
301
302impl<ParamType, Ctx> ReceiveHost<ParamType, Ctx> {
303    /// Initialize a new [`ReceiveHost`].
304    pub fn init(
305        energy: InterpreterEnergy,
306        state: State,
307        param: ParamType,
308        receive_ctx: Ctx,
309        max_parameter_size: usize,
310        limit_logs_and_return_values: bool,
311    ) -> Self {
312        ReceiveHost {
313            energy,
314            activation_frames: constants::MAX_ACTIVATION_FRAMES,
315            logs: Logs::new(),
316            state,
317            param,
318            outcomes: Default::default(),
319            receive_ctx,
320            max_parameter_size,
321            limit_logs_and_return_values,
322        }
323    }
324}
325
326/// Types which can act as init contexts.
327///
328/// Used to enable partial JSON contexts when simulating contracts with
329/// cargo-concordium.
330///
331/// We have two implementations:
332///  - `InitContext`, which is used on-chain and always returns `Ok(..)`.
333///  - `InitContextOpt`, which is used during simulation with cargo-concordium
334///    and returns `Ok(..)` for fields supplied in a JSON context, and `Err(..)`
335///    otherwise.
336pub trait HasInitContext {
337    type MetadataType: HasChainMetadata;
338
339    fn metadata(&self) -> &Self::MetadataType;
340    fn init_origin(&self) -> ExecResult<&AccountAddress>;
341    fn sender_policies(&self) -> ExecResult<&[u8]>;
342}
343
344/// Generic implementation for all references to types that already implement
345/// HasInitContext. This allows using InitContext as well as &InitContext in the
346/// init host, depending on whether we want to transfer ownership of the context
347/// or not.
348impl<'a, X: HasInitContext> HasInitContext for &'a X {
349    type MetadataType = X::MetadataType;
350
351    fn metadata(&self) -> &Self::MetadataType { (*self).metadata() }
352
353    fn init_origin(&self) -> ExecResult<&AccountAddress> { (*self).init_origin() }
354
355    fn sender_policies(&self) -> ExecResult<&[u8]> { (*self).sender_policies() }
356}
357
358impl<X: AsRef<[u8]>> HasInitContext for InitContext<X> {
359    type MetadataType = ChainMetadata;
360
361    fn metadata(&self) -> &Self::MetadataType { &self.metadata }
362
363    fn init_origin(&self) -> ExecResult<&AccountAddress> { Ok(&self.init_origin) }
364
365    fn sender_policies(&self) -> ExecResult<&[u8]> { Ok(self.sender_policies.as_ref()) }
366}
367
368/// Types which can act as receive contexts.
369///
370/// Used to enable partial JSON contexts when simulating contracts with
371/// cargo-concordium.
372///
373/// We have two implementations:
374///  - `ReceiveContext`, which is used on-chain and always returns `Ok(..)`.
375///  - `ReceiveContextOpt`, which is used during simulation with
376///    cargo-concordium and returns `Ok(..)` for fields supplied in a JSON
377///    context, and `Err(..)` otherwise.
378pub trait HasReceiveContext {
379    type MetadataType: HasChainMetadata;
380
381    fn metadata(&self) -> &Self::MetadataType;
382    fn invoker(&self) -> ExecResult<&AccountAddress>;
383    fn self_address(&self) -> ExecResult<&ContractAddress>;
384    fn self_balance(&self) -> ExecResult<Amount>;
385    fn sender(&self) -> ExecResult<&Address>;
386    fn owner(&self) -> ExecResult<&AccountAddress>;
387    fn sender_policies(&self) -> ExecResult<&[u8]>;
388}
389
390/// Generic implementation for all references to types that already implement
391/// HasReceiveContext. This allows using ReceiveContext as well as
392/// &ReceiveContext in the receive host, depending on whether we want to
393/// transfer ownership of the context or not.
394impl<'a, X: HasReceiveContext> HasReceiveContext for &'a X {
395    type MetadataType = X::MetadataType;
396
397    fn metadata(&self) -> &Self::MetadataType { (*self).metadata() }
398
399    fn invoker(&self) -> ExecResult<&AccountAddress> { (*self).invoker() }
400
401    fn self_address(&self) -> ExecResult<&ContractAddress> { (*self).self_address() }
402
403    fn self_balance(&self) -> ExecResult<Amount> { (*self).self_balance() }
404
405    fn sender(&self) -> ExecResult<&Address> { (*self).sender() }
406
407    fn owner(&self) -> ExecResult<&AccountAddress> { (*self).owner() }
408
409    fn sender_policies(&self) -> ExecResult<&[u8]> { (*self).sender_policies() }
410}
411
412impl<X: AsRef<[u8]>> HasReceiveContext for ReceiveContext<X> {
413    type MetadataType = ChainMetadata;
414
415    fn metadata(&self) -> &Self::MetadataType { &self.metadata }
416
417    fn invoker(&self) -> ExecResult<&AccountAddress> { Ok(&self.invoker) }
418
419    fn self_address(&self) -> ExecResult<&ContractAddress> { Ok(&self.self_address) }
420
421    fn self_balance(&self) -> ExecResult<Amount> { Ok(self.self_balance) }
422
423    fn sender(&self) -> ExecResult<&Address> { Ok(&self.sender) }
424
425    fn owner(&self) -> ExecResult<&AccountAddress> { Ok(&self.owner) }
426
427    fn sender_policies(&self) -> ExecResult<&[u8]> { Ok(self.sender_policies.as_ref()) }
428}
429
430/// A trait implemented by types that give access to information about the chain
431/// available to smart contracts.
432pub trait HasChainMetadata {
433    /// The objective (i.e., the entire network agrees on it) time of the block
434    /// in whose context the smart contract is being executed.
435    fn slot_time(&self) -> ExecResult<SlotTime>;
436}
437
438impl HasChainMetadata for ChainMetadata {
439    fn slot_time(&self) -> ExecResult<SlotTime> { Ok(self.slot_time) }
440}
441
442/// Low-level implementations of host functions. They are written in this way so
443/// that they may be reused between init and receive functions, as well as in
444/// future versions of contract specifications.
445pub(crate) mod host {
446    use super::*;
447    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
448    pub(crate) fn get_parameter_size(
449        stack: &mut machine::RuntimeStack,
450        param_len: u32,
451    ) -> machine::RunResult<()> {
452        // the cost of this function is adequately reflected by the base cost of a
453        // function call so we do not charge extra.
454        stack.push_value(param_len);
455        Ok(())
456    }
457
458    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
459    pub(crate) fn get_parameter_section(
460        memory: &mut [u8],
461        stack: &mut machine::RuntimeStack,
462        energy: &mut InterpreterEnergy,
463        param: &[u8],
464    ) -> machine::RunResult<()> {
465        let offset = unsafe { stack.pop_u32() } as usize;
466        let length = unsafe { stack.pop_u32() };
467        let start = unsafe { stack.pop_u32() } as usize;
468        // charge energy linearly in the amount of data written.
469        energy.tick_energy(constants::copy_parameter_cost(length))?;
470        let write_end = start + length as usize; // this cannot overflow on 64-bit machines.
471        ensure!(write_end <= memory.len(), "Illegal memory access.");
472        let end = std::cmp::min(offset + length as usize, param.len());
473        ensure!(offset <= end, "Attempting to read non-existent parameter.");
474        let amt = (&mut memory[start..write_end]).write(&param[offset..end])?;
475        stack.push_value(amt as u32);
476        Ok(())
477    }
478
479    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
480    pub(crate) fn get_policy_section(
481        memory: &mut [u8],
482        stack: &mut machine::RuntimeStack,
483        energy: &mut InterpreterEnergy,
484        policies: ExecResult<&[u8]>,
485    ) -> machine::RunResult<()> {
486        let offset = unsafe { stack.pop_u32() } as usize;
487        let length = unsafe { stack.pop_u32() };
488        // charge energy linearly in the amount of data written.
489        energy.tick_energy(constants::copy_from_host_cost(length))?;
490        let start = unsafe { stack.pop_u32() } as usize;
491        let write_end = start + length as usize; // this cannot overflow on 64-bit machines.
492        ensure!(write_end <= memory.len(), "Illegal memory access.");
493        let policies_bytes = policies?;
494        let end = std::cmp::min(offset + length as usize, policies_bytes.len());
495        ensure!(offset <= end, "Attempting to read non-existent policy.");
496        let amt = (&mut memory[start..write_end]).write(&policies_bytes[offset..end])?;
497        stack.push_value(amt as u32);
498        Ok(())
499    }
500
501    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
502    pub(crate) fn log_event(
503        memory: &mut [u8],
504        stack: &mut machine::RuntimeStack,
505        energy: &mut InterpreterEnergy,
506        logs: &mut Logs,
507        limit_num_logs: bool,
508    ) -> machine::RunResult<()> {
509        let length = unsafe { stack.pop_u32() };
510        let start = unsafe { stack.pop_u32() } as usize;
511        let end = start + length as usize;
512        ensure!(end <= memory.len(), "Illegal memory access.");
513        if length <= constants::MAX_LOG_SIZE {
514            // only charge if we actually log something.
515            energy.tick_energy(constants::log_event_cost(length))?;
516            stack.push_value(logs.log_event(memory[start..end].to_vec(), limit_num_logs))
517        } else {
518            // otherwise the cost is adequately reflected by just the cost of a function
519            // call.
520            stack.push_value(-1i32)
521        }
522        Ok(())
523    }
524
525    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
526    pub(crate) fn load_state(
527        memory: &mut [u8],
528        stack: &mut machine::RuntimeStack,
529        energy: &mut InterpreterEnergy,
530        state: &mut State,
531    ) -> machine::RunResult<()> {
532        let offset = unsafe { stack.pop_u32() };
533        let length = unsafe { stack.pop_u32() };
534        let start = unsafe { stack.pop_u32() } as usize;
535        // charge energy linearly in the amount of data written.
536        energy.tick_energy(constants::copy_from_host_cost(length))?;
537        let end = start + length as usize; // this cannot overflow on 64-bit machines.
538        ensure!(end <= memory.len(), "Illegal memory access.");
539        let res = state.load_state(offset, &mut memory[start..end])?;
540        stack.push_value(res);
541        Ok(())
542    }
543
544    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
545    pub(crate) fn write_state(
546        memory: &mut [u8],
547        stack: &mut machine::RuntimeStack,
548        energy: &mut InterpreterEnergy,
549        state: &mut State,
550    ) -> machine::RunResult<()> {
551        let offset = unsafe { stack.pop_u32() };
552        let length = unsafe { stack.pop_u32() };
553        let start = unsafe { stack.pop_u32() } as usize;
554        // charge energy linearly in the amount of data written.
555        energy.tick_energy(constants::copy_to_host_cost(length))?;
556        let end = start + length as usize; // this cannot overflow on 64-bit machines.
557        ensure!(end <= memory.len(), "Illegal memory access.");
558        let res = state.write_state(offset, &memory[start..end])?;
559        stack.push_value(res);
560        Ok(())
561    }
562
563    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
564    pub(crate) fn resize_state(
565        stack: &mut machine::RuntimeStack,
566        energy: &mut InterpreterEnergy,
567        state: &mut State,
568    ) -> machine::RunResult<()> {
569        let new_size = stack.pop();
570        let new_size = unsafe { new_size.short } as u32;
571        let old_size = state.len();
572        if new_size > old_size {
573            // resizing is very similar to writing 0 to the newly allocated parts,
574            // but since we don't have to read anything we charge it more cheaply.
575            energy.tick_energy(constants::additional_state_size_cost(u64::from(
576                new_size - old_size,
577            )))?;
578        }
579        stack.push_value(state.resize_state(new_size));
580        Ok(())
581    }
582
583    #[inline(always)]
584    pub(crate) fn state_size(
585        stack: &mut machine::RuntimeStack,
586        state: &mut State,
587    ) -> machine::RunResult<()> {
588        // the cost of this function is adequately reflected by the base cost of a
589        // function call so we do not charge extra.
590        stack.push_value(state.len());
591        Ok(())
592    }
593
594    #[inline(always)]
595    pub(crate) fn get_slot_time(
596        stack: &mut machine::RuntimeStack,
597        metadata: &impl HasChainMetadata,
598    ) -> machine::RunResult<()> {
599        // the cost of this function is adequately reflected by the base cost of a
600        // function call so we do not charge extra.
601        stack.push_value(metadata.slot_time()?.timestamp_millis());
602        Ok(())
603    }
604
605    pub(crate) fn get_init_origin(
606        memory: &mut [u8],
607        stack: &mut machine::RuntimeStack,
608        init_origin: ExecResult<&AccountAddress>,
609    ) -> machine::RunResult<()> {
610        let start = unsafe { stack.pop_u32() } as usize;
611        ensure!(start + 32 <= memory.len(), "Illegal memory access for init origin.");
612        (&mut memory[start..start + 32]).write_all(init_origin?.as_ref())?;
613        Ok(())
614    }
615
616    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
617    pub(crate) fn accept(
618        stack: &mut machine::RuntimeStack,
619        energy: &mut InterpreterEnergy,
620        outcomes: &mut Outcome,
621    ) -> machine::RunResult<()> {
622        energy.tick_energy(constants::BASE_ACTION_COST)?;
623        stack.push_value(outcomes.accept());
624        Ok(())
625    }
626
627    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
628    pub(crate) fn simple_transfer(
629        memory: &mut [u8],
630        stack: &mut machine::RuntimeStack,
631        energy: &mut InterpreterEnergy,
632        outcomes: &mut Outcome,
633    ) -> machine::RunResult<()> {
634        energy.tick_energy(constants::BASE_ACTION_COST)?;
635        let amount = unsafe { stack.pop_u64() };
636        let addr_start = unsafe { stack.pop_u32() } as usize;
637        // Overflow is not possible in the next line on 64-bit machines.
638        ensure!(addr_start + 32 <= memory.len(), "Illegal memory access.");
639        stack.push_value(outcomes.simple_transfer(&memory[addr_start..addr_start + 32], amount)?);
640        Ok(())
641    }
642
643    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
644    pub(crate) fn send(
645        memory: &mut [u8],
646        stack: &mut machine::RuntimeStack,
647        energy: &mut InterpreterEnergy,
648        outcomes: &mut Outcome,
649        max_parameter_size: usize,
650    ) -> machine::RunResult<()> {
651        // all `as usize` are safe on 64-bit systems since we are converging from a u32
652        let parameter_len = unsafe { stack.pop_u32() };
653        energy.tick_energy(constants::action_send_cost(parameter_len))?;
654        let parameter_start = unsafe { stack.pop_u32() } as usize;
655        // Overflow is not possible in the next line on 64-bit machines.
656        let parameter_end = parameter_start + parameter_len as usize;
657        let amount = unsafe { stack.pop_u64() };
658        let receive_name_len = unsafe { stack.pop_u32() } as usize;
659        let receive_name_start = unsafe { stack.pop_u32() } as usize;
660        // Overflow is not possible in the next line on 64-bit machines.
661        let receive_name_end = receive_name_start + receive_name_len;
662        let addr_subindex = unsafe { stack.pop_u64() };
663        let addr_index = unsafe { stack.pop_u64() };
664        ensure!(parameter_end <= memory.len(), "Illegal memory access.");
665        ensure!(receive_name_end <= memory.len(), "Illegal memory access.");
666        let res = outcomes.send(
667            addr_index,
668            addr_subindex,
669            &memory[receive_name_start..receive_name_end],
670            amount,
671            &memory[parameter_start..parameter_end],
672            max_parameter_size,
673        )?;
674        stack.push_value(res);
675        Ok(())
676    }
677
678    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
679    pub(crate) fn combine_and(
680        stack: &mut machine::RuntimeStack,
681        energy: &mut InterpreterEnergy,
682        outcomes: &mut Outcome,
683    ) -> machine::RunResult<()> {
684        energy.tick_energy(constants::BASE_ACTION_COST)?;
685        let right = unsafe { stack.pop_u32() };
686        let left = unsafe { stack.pop_u32() };
687        let res = outcomes.combine_and(left, right)?;
688        stack.push_value(res);
689        Ok(())
690    }
691
692    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
693    pub(crate) fn combine_or(
694        stack: &mut machine::RuntimeStack,
695        energy: &mut InterpreterEnergy,
696        outcomes: &mut Outcome,
697    ) -> machine::RunResult<()> {
698        energy.tick_energy(constants::BASE_ACTION_COST)?;
699        let right = unsafe { stack.pop_u32() };
700        let left = unsafe { stack.pop_u32() };
701        let res = outcomes.combine_or(left, right)?;
702        stack.push_value(res);
703        Ok(())
704    }
705
706    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
707    pub(crate) fn get_receive_invoker(
708        memory: &mut [u8],
709        stack: &mut machine::RuntimeStack,
710        invoker: ExecResult<&AccountAddress>,
711    ) -> machine::RunResult<()> {
712        let start = unsafe { stack.pop_u32() } as usize;
713        ensure!(start + 32 <= memory.len(), "Illegal memory access for receive invoker.");
714        (&mut memory[start..start + 32]).write_all(invoker?.as_ref())?;
715        Ok(())
716    }
717
718    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
719    pub(crate) fn get_receive_self_address(
720        memory: &mut [u8],
721        stack: &mut machine::RuntimeStack,
722        self_address: ExecResult<&ContractAddress>,
723    ) -> machine::RunResult<()> {
724        let start = unsafe { stack.pop_u32() } as usize;
725        ensure!(start + 16 <= memory.len(), "Illegal memory access for receive owner.");
726        let self_address = self_address?;
727        (&mut memory[start..start + 8]).write_all(&self_address.index.to_le_bytes())?;
728        (&mut memory[start + 8..start + 16]).write_all(&self_address.subindex.to_le_bytes())?;
729        Ok(())
730    }
731
732    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
733    pub(crate) fn get_receive_self_balance(
734        stack: &mut machine::RuntimeStack,
735        self_balance: ExecResult<Amount>,
736    ) -> machine::RunResult<()> {
737        stack.push_value(self_balance?.micro_ccd);
738        Ok(())
739    }
740
741    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
742    pub(crate) fn get_receive_sender(
743        memory: &mut [u8],
744        stack: &mut machine::RuntimeStack,
745        sender: ExecResult<&Address>,
746    ) -> machine::RunResult<()> {
747        let start = unsafe { stack.pop_u32() } as usize;
748        ensure!(start < memory.len(), "Illegal memory access for receive sender.");
749        sender?
750            .serial::<&mut [u8]>(&mut &mut memory[start..])
751            .map_err(|_| anyhow!("Memory out of bounds."))?;
752        Ok(())
753    }
754
755    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
756    pub(crate) fn get_receive_owner(
757        memory: &mut [u8],
758        stack: &mut machine::RuntimeStack,
759        owner: ExecResult<&AccountAddress>,
760    ) -> machine::RunResult<()> {
761        let start = unsafe { stack.pop_u32() } as usize;
762        ensure!(start + 32 <= memory.len(), "Illegal memory access for receive owner.");
763        (&mut memory[start..start + 32]).write_all(owner?.as_ref())?;
764        Ok(())
765    }
766
767    #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
768    pub(crate) fn track_call(activation_frames: &mut u32) -> machine::RunResult<()> {
769        if let Some(fr) = activation_frames.checked_sub(1) {
770            *activation_frames = fr;
771            Ok(())
772        } else {
773            bail!("Too many nested functions.")
774        }
775    }
776
777    #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
778    pub(crate) fn track_return(activation_frames: &mut u32) { *activation_frames += 1; }
779
780    #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
781    pub(crate) fn charge_memory_alloc(
782        stack: &mut machine::RuntimeStack,
783        energy: &mut InterpreterEnergy,
784    ) -> machine::RunResult<()> {
785        energy.charge_memory_alloc(unsafe { stack.peek_u32() })
786    }
787}
788
789impl<ParamType: AsRef<[u8]>, Ctx: HasInitContext> machine::Host<ProcessedImports>
790    for InitHost<ParamType, Ctx>
791{
792    type Interrupt = NoInterrupt;
793
794    #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
795    fn tick_initial_memory(&mut self, num_pages: u32) -> machine::RunResult<()> {
796        self.energy.charge_memory_alloc(num_pages)
797    }
798
799    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
800    fn call(
801        &mut self,
802        f: &ProcessedImports,
803        memory: &mut [u8],
804        stack: &mut machine::RuntimeStack,
805    ) -> machine::RunResult<Option<NoInterrupt>> {
806        match f.tag {
807            ImportFunc::ChargeMemoryAlloc => host::charge_memory_alloc(stack, &mut self.energy)?,
808            ImportFunc::Common(cf) => match cf {
809                CommonFunc::GetParameterSize => {
810                    host::get_parameter_size(stack, self.param.as_ref().len() as u32)
811                }
812                CommonFunc::GetParameterSection => host::get_parameter_section(
813                    memory,
814                    stack,
815                    &mut self.energy,
816                    self.param.as_ref(),
817                ),
818                CommonFunc::GetPolicySection => host::get_policy_section(
819                    memory,
820                    stack,
821                    &mut self.energy,
822                    self.init_ctx.sender_policies(),
823                ),
824                CommonFunc::LogEvent => host::log_event(
825                    memory,
826                    stack,
827                    &mut self.energy,
828                    &mut self.logs,
829                    self.limit_logs_and_return_values,
830                ),
831                CommonFunc::LoadState => {
832                    host::load_state(memory, stack, &mut self.energy, &mut self.state)
833                }
834                CommonFunc::WriteState => {
835                    host::write_state(memory, stack, &mut self.energy, &mut self.state)
836                }
837                CommonFunc::ResizeState => {
838                    host::resize_state(stack, &mut self.energy, &mut self.state)
839                }
840                CommonFunc::StateSize => host::state_size(stack, &mut self.state),
841                CommonFunc::GetSlotTime => host::get_slot_time(stack, self.init_ctx.metadata()),
842            }?,
843            ImportFunc::InitOnly(InitOnlyFunc::GetInitOrigin) => {
844                host::get_init_origin(memory, stack, self.init_ctx.init_origin())?
845            }
846            ImportFunc::ReceiveOnly(_) => {
847                bail!("Not implemented for init {:#?}.", f);
848            }
849        }
850        Ok(None)
851    }
852
853    #[inline(always)]
854    fn tick_energy(&mut self, energy: u64) -> machine::RunResult<()> {
855        self.energy.tick_energy(energy)
856    }
857
858    #[inline(always)]
859    fn track_call(&mut self) -> machine::RunResult<()> {
860        host::track_call(&mut self.activation_frames)
861    }
862
863    #[inline(always)]
864    fn track_return(&mut self) { host::track_return(&mut self.activation_frames) }
865}
866
867impl<ParamType: AsRef<[u8]>, Ctx: HasReceiveContext> machine::Host<ProcessedImports>
868    for ReceiveHost<ParamType, Ctx>
869{
870    type Interrupt = NoInterrupt;
871
872    #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
873    fn tick_initial_memory(&mut self, num_pages: u32) -> machine::RunResult<()> {
874        self.energy.charge_memory_alloc(num_pages)
875    }
876
877    #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
878    fn call(
879        &mut self,
880        f: &ProcessedImports,
881        memory: &mut [u8],
882        stack: &mut machine::RuntimeStack,
883    ) -> machine::RunResult<Option<NoInterrupt>> {
884        match f.tag {
885            ImportFunc::ChargeMemoryAlloc => host::charge_memory_alloc(stack, &mut self.energy)?,
886            ImportFunc::Common(cf) => match cf {
887                CommonFunc::GetParameterSize => {
888                    host::get_parameter_size(stack, self.param.as_ref().len() as u32)
889                }
890                CommonFunc::GetParameterSection => host::get_parameter_section(
891                    memory,
892                    stack,
893                    &mut self.energy,
894                    self.param.as_ref(),
895                ),
896                CommonFunc::GetPolicySection => host::get_policy_section(
897                    memory,
898                    stack,
899                    &mut self.energy,
900                    self.receive_ctx.sender_policies(),
901                ),
902                CommonFunc::LogEvent => host::log_event(
903                    memory,
904                    stack,
905                    &mut self.energy,
906                    &mut self.logs,
907                    self.limit_logs_and_return_values,
908                ),
909                CommonFunc::LoadState => {
910                    host::load_state(memory, stack, &mut self.energy, &mut self.state)
911                }
912                CommonFunc::WriteState => {
913                    host::write_state(memory, stack, &mut self.energy, &mut self.state)
914                }
915                CommonFunc::ResizeState => {
916                    host::resize_state(stack, &mut self.energy, &mut self.state)
917                }
918                CommonFunc::StateSize => host::state_size(stack, &mut self.state),
919                CommonFunc::GetSlotTime => host::get_slot_time(stack, self.receive_ctx.metadata()),
920            }?,
921            ImportFunc::ReceiveOnly(rof) => match rof {
922                ReceiveOnlyFunc::Accept => {
923                    host::accept(stack, &mut self.energy, &mut self.outcomes)
924                }
925                ReceiveOnlyFunc::SimpleTransfer => {
926                    host::simple_transfer(memory, stack, &mut self.energy, &mut self.outcomes)
927                }
928                ReceiveOnlyFunc::Send => host::send(
929                    memory,
930                    stack,
931                    &mut self.energy,
932                    &mut self.outcomes,
933                    self.max_parameter_size,
934                ),
935                ReceiveOnlyFunc::CombineAnd => {
936                    host::combine_and(stack, &mut self.energy, &mut self.outcomes)
937                }
938                ReceiveOnlyFunc::CombineOr => {
939                    host::combine_or(stack, &mut self.energy, &mut self.outcomes)
940                }
941                ReceiveOnlyFunc::GetReceiveInvoker => {
942                    host::get_receive_invoker(memory, stack, self.receive_ctx.invoker())
943                }
944                ReceiveOnlyFunc::GetReceiveSelfAddress => {
945                    host::get_receive_self_address(memory, stack, self.receive_ctx.self_address())
946                }
947                ReceiveOnlyFunc::GetReceiveSelfBalance => {
948                    host::get_receive_self_balance(stack, self.receive_ctx.self_balance())
949                }
950                ReceiveOnlyFunc::GetReceiveSender => {
951                    host::get_receive_sender(memory, stack, self.receive_ctx.sender())
952                }
953                ReceiveOnlyFunc::GetReceiveOwner => {
954                    host::get_receive_owner(memory, stack, self.receive_ctx.owner())
955                }
956            }?,
957            ImportFunc::InitOnly(InitOnlyFunc::GetInitOrigin) => {
958                bail!("Not implemented for receive.");
959            }
960        }
961        Ok(None)
962    }
963
964    #[inline(always)]
965    fn tick_energy(&mut self, energy: u64) -> machine::RunResult<()> {
966        self.energy.tick_energy(energy)
967    }
968
969    #[inline(always)]
970    fn track_call(&mut self) -> machine::RunResult<()> {
971        host::track_call(&mut self.activation_frames)
972    }
973
974    #[inline(always)]
975    fn track_return(&mut self) { host::track_return(&mut self.activation_frames) }
976}
977
978/// Collection of information relevant to invoke an init-function.
979#[derive(Debug)]
980pub struct InitInvocation<'a> {
981    /// The amount included in the transaction.
982    pub amount:    u64,
983    /// The name of the init function to invoke.
984    pub init_name: &'a str,
985    /// A parameter to provide the init function.
986    pub parameter: Parameter<'a>,
987    /// The limit on the energy to be used for execution.
988    pub energy:    InterpreterEnergy,
989}
990
991/// Invokes an init-function from a given artifact.
992pub fn invoke_init<C: RunnableCode, Ctx: HasInitContext>(
993    artifact: &Artifact<ProcessedImports, C>,
994    init_ctx: Ctx,
995    init_invocation: InitInvocation,
996    limit_logs_and_return_values: bool,
997) -> ExecResult<InitResult> {
998    let mut host = InitHost {
999        energy: init_invocation.energy,
1000        activation_frames: constants::MAX_ACTIVATION_FRAMES,
1001        logs: Logs::new(),
1002        state: State::new(None),
1003        param: init_invocation.parameter,
1004        limit_logs_and_return_values,
1005        init_ctx,
1006    };
1007
1008    let res = match artifact
1009        .run(&mut host, init_invocation.init_name, &[Value::I64(init_invocation.amount as i64)])
1010    {
1011        Ok(ExecutionOutcome::Success {
1012            result,
1013            ..
1014        }) => result,
1015        Ok(ExecutionOutcome::Interrupted {
1016            reason,
1017            ..
1018        }) => match reason {}, // impossible case, InitHost has no interrupts
1019        Err(e) => {
1020            if e.downcast_ref::<OutOfEnergy>().is_some() {
1021                return Ok(InitResult::OutOfEnergy);
1022            } else {
1023                return Err(e);
1024            }
1025        }
1026    };
1027    let remaining_energy = host.energy.energy;
1028    // process the return value.
1029    // - 0 indicates success
1030    // - positive values are a protocol violation, so they lead to a runtime error
1031    // - negative values lead to a rejection with a specific reject reason.
1032    if let Some(Value::I32(n)) = res {
1033        if n == 0 {
1034            Ok(InitResult::Success {
1035                logs:             host.logs,
1036                state:            host.state,
1037                remaining_energy: remaining_energy.into(),
1038            })
1039        } else {
1040            Ok(InitResult::Reject {
1041                reason:           reason_from_wasm_error_code(n)?,
1042                remaining_energy: remaining_energy.into(),
1043            })
1044        }
1045    } else {
1046        bail!("Wasm module should return a value.")
1047    }
1048}
1049
1050/// Invokes an init-function from a given artifact *bytes*
1051#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1052pub fn invoke_init_from_artifact<Ctx: HasInitContext>(
1053    artifact_bytes: &[u8],
1054    amount: u64,
1055    init_ctx: Ctx,
1056    init_name: &str,
1057    parameter: Parameter,
1058    limit_logs_and_return_values: bool,
1059    energy: InterpreterEnergy,
1060) -> ExecResult<InitResult> {
1061    let artifact = utils::parse_artifact(artifact_bytes)?;
1062    invoke_init(
1063        &artifact,
1064        init_ctx,
1065        InitInvocation {
1066            amount,
1067            init_name,
1068            parameter,
1069            energy,
1070        },
1071        limit_logs_and_return_values,
1072    )
1073}
1074
1075/// Invokes an init-function from Wasm module bytes
1076#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1077pub fn invoke_init_from_source<Ctx: HasInitContext>(
1078    source_bytes: &[u8],
1079    amount: u64,
1080    init_ctx: Ctx,
1081    init_name: &str,
1082    parameter: Parameter,
1083    limit_logs_and_return_values: bool,
1084    energy: InterpreterEnergy,
1085) -> ExecResult<InitResult> {
1086    let artifact =
1087        utils::instantiate(ValidationConfig::V0, &ConcordiumAllowedImports, source_bytes)?.artifact;
1088    invoke_init(
1089        &artifact,
1090        init_ctx,
1091        InitInvocation {
1092            amount,
1093            init_name,
1094            parameter,
1095            energy,
1096        },
1097        limit_logs_and_return_values,
1098    )
1099}
1100
1101/// Same as `invoke_init_from_source`, except that the module has cost
1102/// accounting instructions inserted before the init function is called.
1103#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1104#[allow(clippy::too_many_arguments)]
1105pub fn invoke_init_with_metering_from_source<Ctx: HasInitContext>(
1106    source_bytes: &[u8],
1107    amount: u64,
1108    init_ctx: Ctx,
1109    init_name: &str,
1110    parameter: Parameter,
1111    limit_logs_and_return_values: bool,
1112    cost_config: impl CostConfiguration,
1113    energy: InterpreterEnergy,
1114) -> ExecResult<InitResult> {
1115    let artifact = utils::instantiate_with_metering(
1116        ValidationConfig::V0,
1117        cost_config,
1118        &ConcordiumAllowedImports,
1119        source_bytes,
1120    )?
1121    .artifact;
1122    invoke_init(
1123        &artifact,
1124        init_ctx,
1125        InitInvocation {
1126            amount,
1127            init_name,
1128            parameter,
1129            energy,
1130        },
1131        limit_logs_and_return_values,
1132    )
1133}
1134
1135/// Collection of information relevant to invoke a receive-function.
1136#[derive(Debug)]
1137pub struct ReceiveInvocation<'a> {
1138    /// The amount included in the transaction.
1139    pub amount:       u64,
1140    /// The name of the receive function to invoke.
1141    pub receive_name: &'a str,
1142    /// A parameter to provide the receive function.
1143    pub parameter:    Parameter<'a>,
1144    /// The limit on the energy to be used for execution.
1145    pub energy:       InterpreterEnergy,
1146}
1147
1148/// Invokes a receive-function from a given artifact
1149pub fn invoke_receive<C: RunnableCode, Ctx: HasReceiveContext>(
1150    artifact: &Artifact<ProcessedImports, C>,
1151    receive_ctx: Ctx,
1152    receive_invocation: ReceiveInvocation,
1153    current_state: &[u8],
1154    max_parameter_size: usize,
1155    limit_logs_and_return_values: bool,
1156) -> ExecResult<ReceiveResult> {
1157    let mut host = ReceiveHost {
1158        energy: receive_invocation.energy,
1159        activation_frames: constants::MAX_ACTIVATION_FRAMES,
1160        logs: Logs::new(),
1161        state: State::new(Some(current_state)),
1162        param: &receive_invocation.parameter,
1163        max_parameter_size,
1164        limit_logs_and_return_values,
1165        receive_ctx,
1166        outcomes: Outcome::new(),
1167    };
1168
1169    let res = match artifact.run(&mut host, receive_invocation.receive_name, &[Value::I64(
1170        receive_invocation.amount as i64,
1171    )]) {
1172        Ok(ExecutionOutcome::Success {
1173            result,
1174            ..
1175        }) => result,
1176        Ok(ExecutionOutcome::Interrupted {
1177            reason,
1178            ..
1179        }) => match reason {}, // impossible case, ReceiveHost has no interrupts
1180        Err(e) => {
1181            if e.downcast_ref::<OutOfEnergy>().is_some() {
1182                return Ok(ReceiveResult::OutOfEnergy);
1183            } else {
1184                return Err(e);
1185            }
1186        }
1187    };
1188    let remaining_energy = host.energy.energy;
1189    if let Some(Value::I32(n)) = res {
1190        // FIXME: We should filter out to only return the ones reachable from
1191        // the root.
1192        let mut actions = host.outcomes.cur_state;
1193        if n >= 0 && (n as usize) < actions.len() {
1194            let n = n as usize;
1195            actions.truncate(n + 1);
1196            Ok(ReceiveResult::Success {
1197                logs: host.logs,
1198                state: host.state,
1199                actions,
1200                remaining_energy: remaining_energy.into(),
1201            })
1202        } else if n >= 0 {
1203            bail!("Invalid return.")
1204        } else {
1205            Ok(ReceiveResult::Reject {
1206                reason:           reason_from_wasm_error_code(n)?,
1207                remaining_energy: remaining_energy.into(),
1208            })
1209        }
1210    } else {
1211        bail!(
1212            "Invalid return. Expected a value, but receive nothing. This should not happen for \
1213             well-formed modules"
1214        );
1215    }
1216}
1217
1218/// Returns the passed Wasm error code if it is negative.
1219/// This function should only be called on negative numbers.
1220fn reason_from_wasm_error_code(n: i32) -> ExecResult<i32> {
1221    ensure!(
1222        n < 0,
1223        "Wasm return value of {} is treated as an error. Only negative should be treated as error.",
1224        n
1225    );
1226    Ok(n)
1227}
1228
1229/// Invokes a receive-function from a given artifact *bytes*
1230#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1231pub fn invoke_receive_from_artifact<Ctx: HasReceiveContext>(
1232    artifact_bytes: &[u8],
1233    receive_ctx: Ctx,
1234    receive_invocation: ReceiveInvocation,
1235    current_state: &[u8],
1236    max_parameter_size: usize,
1237    limit_logs_and_return_values: bool,
1238) -> ExecResult<ReceiveResult> {
1239    let artifact = utils::parse_artifact(artifact_bytes)?;
1240    invoke_receive(
1241        &artifact,
1242        receive_ctx,
1243        receive_invocation,
1244        current_state,
1245        max_parameter_size,
1246        limit_logs_and_return_values,
1247    )
1248}
1249
1250/// Invokes a receive-function from Wasm module bytes
1251#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1252pub fn invoke_receive_from_source<Ctx: HasReceiveContext>(
1253    source_bytes: &[u8],
1254    receive_ctx: Ctx,
1255    receive_invocation: ReceiveInvocation,
1256    current_state: &[u8],
1257    max_parameter_size: usize,
1258    limit_logs_and_return_values: bool,
1259) -> ExecResult<ReceiveResult> {
1260    let artifact =
1261        utils::instantiate(ValidationConfig::V0, &ConcordiumAllowedImports, source_bytes)?.artifact;
1262    invoke_receive(
1263        &artifact,
1264        receive_ctx,
1265        receive_invocation,
1266        current_state,
1267        max_parameter_size,
1268        limit_logs_and_return_values,
1269    )
1270}
1271
1272/// Invokes a receive-function from Wasm module bytes, injects the module with
1273/// metering.
1274#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1275pub fn invoke_receive_with_metering_from_source<Ctx: HasReceiveContext>(
1276    source_bytes: &[u8],
1277    receive_ctx: Ctx,
1278    receive_invocation: ReceiveInvocation,
1279    current_state: &[u8],
1280    max_parameter_size: usize,
1281    limit_logs_and_return_values: bool,
1282    cost_config: impl CostConfiguration,
1283) -> ExecResult<ReceiveResult> {
1284    let artifact = utils::instantiate_with_metering(
1285        ValidationConfig::V0,
1286        cost_config,
1287        &ConcordiumAllowedImports,
1288        source_bytes,
1289    )?
1290    .artifact;
1291    invoke_receive(
1292        &artifact,
1293        receive_ctx,
1294        receive_invocation,
1295        current_state,
1296        max_parameter_size,
1297        limit_logs_and_return_values,
1298    )
1299}