casper_execution_engine/runtime/
mod.rs

1//! This module contains executor state of the WASM code.
2mod args;
3mod auction_internal;
4pub mod cryptography;
5mod externals;
6mod handle_payment_internal;
7mod host_function_flag;
8mod mint_internal;
9pub mod stack;
10mod utils;
11pub(crate) mod wasm_prep;
12
13use std::{
14    cmp,
15    collections::{BTreeMap, BTreeSet},
16    convert::{TryFrom, TryInto},
17    iter::FromIterator,
18};
19
20use casper_wasm::elements::Module;
21use casper_wasmi::{MemoryRef, Trap, TrapCode};
22use tracing::{debug, error, warn};
23
24#[cfg(feature = "test-support")]
25use casper_wasmi::RuntimeValue;
26use itertools::Itertools;
27use num_rational::Ratio;
28
29use casper_storage::{
30    global_state::{error::Error as GlobalStateError, state::StateReader},
31    system::{auction::Auction, handle_payment::HandlePayment, mint::Mint},
32    tracking_copy::TrackingCopyExt,
33};
34use casper_types::{
35    account::{
36        Account, AccountHash, AddKeyFailure, RemoveKeyFailure, SetThresholdFailure,
37        UpdateKeyFailure,
38    },
39    addressable_entity::{
40        self, ActionThresholds, ActionType, AddressableEntity, AddressableEntityHash,
41        AssociatedKeys, ContractRuntimeTag, EntityEntryPoint, EntryPointAccess, EntryPointType,
42        EntryPoints, MessageTopicError, MessageTopics, NamedKeyAddr, NamedKeyValue, Parameter,
43        Weight, DEFAULT_ENTRY_POINT_NAME,
44    },
45    bytesrepr::{self, Bytes, FromBytes, ToBytes},
46    contract_messages::{
47        Message, MessageAddr, MessagePayload, MessageTopicOperation, MessageTopicSummary,
48    },
49    contracts::{
50        ContractHash, ContractPackage, ContractPackageHash, ContractPackageStatus,
51        ContractVersions, DisabledVersions, NamedKeys,
52    },
53    system::{
54        self,
55        auction::{self, DelegatorKind, EraInfo},
56        handle_payment, mint, CallStackElement, Caller, CallerInfo, SystemEntityType, AUCTION,
57        HANDLE_PAYMENT, MINT, STANDARD_PAYMENT,
58    },
59    AccessRights, ApiError, BlockGlobalAddr, BlockTime, ByteCode, ByteCodeAddr, ByteCodeHash,
60    ByteCodeKind, CLTyped, CLValue, ContextAccessRights, Contract, ContractWasm, EntityAddr,
61    EntityKind, EntityVersion, EntityVersionKey, EntityVersions, Gas, GrantedAccess, Group, Groups,
62    HashAddr, HostFunction, HostFunctionCost, InitiatorAddr, Key, NamedArg, Package, PackageHash,
63    PackageStatus, Phase, PublicKey, RuntimeArgs, RuntimeFootprint, StoredValue, Transfer,
64    TransferResult, TransferV2, TransferredTo, URef, DICTIONARY_ITEM_KEY_MAX_LENGTH, U512,
65};
66
67use crate::{
68    execution::ExecError, runtime::host_function_flag::HostFunctionFlag,
69    runtime_context::RuntimeContext,
70};
71pub use stack::{RuntimeStack, RuntimeStackFrame, RuntimeStackOverflow};
72pub use wasm_prep::{
73    cycles_for_instruction, preprocess, PreprocessingError, WasmValidationError,
74    DEFAULT_BR_TABLE_MAX_SIZE, DEFAULT_MAX_GLOBALS, DEFAULT_MAX_PARAMETER_COUNT,
75    DEFAULT_MAX_TABLE_SIZE,
76};
77
78#[derive(Debug)]
79enum CallContractIdentifier {
80    Contract {
81        contract_hash: HashAddr,
82    },
83    ContractPackage {
84        contract_package_hash: HashAddr,
85        version: Option<EntityVersion>,
86    },
87    PackageVersion {
88        contract_package_hash: HashAddr,
89        major_version: u32,
90        version: EntityVersion,
91    },
92}
93
94#[repr(u8)]
95enum CallerInformation {
96    Initiator = 0,
97    Immediate = 1,
98    FullCallChain = 2,
99}
100
101impl TryFrom<u8> for CallerInformation {
102    type Error = ApiError;
103
104    fn try_from(value: u8) -> Result<Self, Self::Error> {
105        match value {
106            0 => Ok(CallerInformation::Initiator),
107            1 => Ok(CallerInformation::Immediate),
108            2 => Ok(CallerInformation::FullCallChain),
109            _ => Err(ApiError::InvalidCallerInfoRequest),
110        }
111    }
112}
113
114/// Represents the runtime properties of a WASM execution.
115pub struct Runtime<'a, R> {
116    context: RuntimeContext<'a, R>,
117    memory: Option<MemoryRef>,
118    module: Option<Module>,
119    host_buffer: Option<CLValue>,
120    stack: Option<RuntimeStack>,
121    host_function_flag: HostFunctionFlag,
122}
123
124impl<'a, R> Runtime<'a, R>
125where
126    R: StateReader<Key, StoredValue, Error = GlobalStateError>,
127{
128    /// Creates a new runtime instance.
129    pub(crate) fn new(context: RuntimeContext<'a, R>) -> Self {
130        Runtime {
131            context,
132            memory: None,
133            module: None,
134            host_buffer: None,
135            stack: None,
136            host_function_flag: HostFunctionFlag::default(),
137        }
138    }
139
140    /// Creates a new runtime instance by cloning the config, and host function flag from `self`.
141    fn new_invocation_runtime(
142        &self,
143        context: RuntimeContext<'a, R>,
144        module: Module,
145        memory: MemoryRef,
146        stack: RuntimeStack,
147    ) -> Self {
148        Self::check_preconditions(&stack);
149        Runtime {
150            context,
151            memory: Some(memory),
152            module: Some(module),
153            host_buffer: None,
154            stack: Some(stack),
155            host_function_flag: self.host_function_flag.clone(),
156        }
157    }
158
159    /// Creates a new runtime instance with a stack from `self`.
160    pub(crate) fn new_with_stack(
161        &self,
162        context: RuntimeContext<'a, R>,
163        stack: RuntimeStack,
164    ) -> Self {
165        Self::check_preconditions(&stack);
166        Runtime {
167            context,
168            memory: None,
169            module: None,
170            host_buffer: None,
171            stack: Some(stack),
172            host_function_flag: self.host_function_flag.clone(),
173        }
174    }
175
176    /// Preconditions that would render the system inconsistent if violated. Those are strictly
177    /// programming errors.
178    fn check_preconditions(stack: &RuntimeStack) {
179        if stack.is_empty() {
180            error!("Call stack should not be empty while creating a new Runtime instance");
181            debug_assert!(false);
182        }
183
184        if stack.first_frame().unwrap().contract_hash().is_some() {
185            error!("First element of the call stack should always represent a Session call");
186            debug_assert!(false);
187        }
188    }
189
190    /// Returns the context.
191    pub(crate) fn context(&self) -> &RuntimeContext<'a, R> {
192        &self.context
193    }
194
195    fn gas(&mut self, amount: Gas) -> Result<(), ExecError> {
196        self.context.charge_gas(amount)
197    }
198
199    /// Returns current gas counter.
200    fn gas_counter(&self) -> Gas {
201        self.context.gas_counter()
202    }
203
204    /// Sets new gas counter value.
205    fn set_gas_counter(&mut self, new_gas_counter: Gas) {
206        self.context.set_gas_counter(new_gas_counter);
207    }
208
209    /// Charge for a system contract call.
210    ///
211    /// This method does not charge for system contract calls if the immediate caller is a system
212    /// contract or if we're currently within the scope of a host function call. This avoids
213    /// misleading gas charges if one system contract calls other system contract (e.g. auction
214    /// contract calls into mint to create new purses).
215    pub(crate) fn charge_system_contract_call<T>(&mut self, amount: T) -> Result<(), ExecError>
216    where
217        T: Into<Gas>,
218    {
219        if self.is_system_immediate_caller()? || self.host_function_flag.is_in_host_function_scope()
220        {
221            return Ok(());
222        }
223
224        self.context.charge_system_contract_call(amount)
225    }
226
227    fn checked_memory_slice<Ret>(
228        &self,
229        offset: usize,
230        size: usize,
231        func: impl FnOnce(&[u8]) -> Ret,
232    ) -> Result<Ret, ExecError> {
233        // This is mostly copied from a private function `MemoryInstance::checked_memory_region`
234        // that calls a user defined function with a validated slice of memory. This allows
235        // usage patterns that does not involve copying data onto heap first i.e. deserialize
236        // values without copying data first, etc.
237        // NOTE: Depending on the VM backend used in future, this may change, as not all VMs may
238        // support direct memory access.
239        self.try_get_memory()?
240            .with_direct_access(|buffer| {
241                let end = offset.checked_add(size).ok_or_else(|| {
242                    casper_wasmi::Error::Memory(format!(
243                        "trying to access memory block of size {} from offset {}",
244                        size, offset
245                    ))
246                })?;
247
248                if end > buffer.len() {
249                    return Err(casper_wasmi::Error::Memory(format!(
250                        "trying to access region [{}..{}] in memory [0..{}]",
251                        offset,
252                        end,
253                        buffer.len(),
254                    )));
255                }
256
257                Ok(func(&buffer[offset..end]))
258            })
259            .map_err(Into::into)
260    }
261
262    /// Returns bytes from the WASM memory instance.
263    #[inline]
264    fn bytes_from_mem(&self, ptr: u32, size: usize) -> Result<Vec<u8>, ExecError> {
265        self.checked_memory_slice(ptr as usize, size, |data| data.to_vec())
266    }
267
268    /// Returns a deserialized type from the WASM memory instance.
269    #[inline]
270    fn t_from_mem<T: FromBytes>(&self, ptr: u32, size: u32) -> Result<T, ExecError> {
271        let result = self.checked_memory_slice(ptr as usize, size as usize, |data| {
272            bytesrepr::deserialize_from_slice(data)
273        })?;
274        Ok(result?)
275    }
276
277    /// Reads key (defined as `key_ptr` and `key_size` tuple) from Wasm memory.
278    #[inline]
279    fn key_from_mem(&mut self, key_ptr: u32, key_size: u32) -> Result<Key, ExecError> {
280        self.t_from_mem(key_ptr, key_size)
281    }
282
283    /// Reads `CLValue` (defined as `cl_value_ptr` and `cl_value_size` tuple) from Wasm memory.
284    #[inline]
285    fn cl_value_from_mem(
286        &mut self,
287        cl_value_ptr: u32,
288        cl_value_size: u32,
289    ) -> Result<CLValue, ExecError> {
290        self.t_from_mem(cl_value_ptr, cl_value_size)
291    }
292
293    /// Returns a deserialized string from the WASM memory instance.
294    #[inline]
295    fn string_from_mem(&self, ptr: u32, size: u32) -> Result<String, Trap> {
296        self.t_from_mem(ptr, size).map_err(Trap::from)
297    }
298
299    fn get_module_from_entry_points(
300        &mut self,
301        entry_points: &EntryPoints,
302    ) -> Result<Vec<u8>, ExecError> {
303        let module = self.try_get_module()?.clone();
304        let entry_point_names: Vec<&str> = entry_points.keys().map(|s| s.as_str()).collect();
305        let module_bytes = wasm_prep::get_module_from_entry_points(entry_point_names, module)?;
306        Ok(module_bytes)
307    }
308
309    #[allow(clippy::wrong_self_convention)]
310    fn is_valid_uref(&self, uref_ptr: u32, uref_size: u32) -> Result<bool, Trap> {
311        let uref: URef = self.t_from_mem(uref_ptr, uref_size)?;
312        Ok(self.context.validate_uref(&uref).is_ok())
313    }
314
315    /// Load the uref known by the given name into the Wasm memory
316    fn load_key(
317        &mut self,
318        name_ptr: u32,
319        name_size: u32,
320        output_ptr: u32,
321        output_size: usize,
322        bytes_written_ptr: u32,
323    ) -> Result<Result<(), ApiError>, Trap> {
324        let name = self.string_from_mem(name_ptr, name_size)?;
325
326        // Get a key and serialize it
327        let key = match self.context.named_keys_get(&name) {
328            Some(key) => key,
329            None => {
330                return Ok(Err(ApiError::MissingKey));
331            }
332        };
333
334        let key_bytes = match key.to_bytes() {
335            Ok(bytes) => bytes,
336            Err(error) => return Ok(Err(error.into())),
337        };
338
339        // `output_size` has to be greater or equal to the actual length of serialized Key bytes
340        if output_size < key_bytes.len() {
341            return Ok(Err(ApiError::BufferTooSmall));
342        }
343
344        // Set serialized Key bytes into the output buffer
345        if let Err(error) = self.try_get_memory()?.set(output_ptr, &key_bytes) {
346            return Err(ExecError::Interpreter(error.into()).into());
347        }
348
349        // SAFETY: For all practical purposes following conversion is assumed to be safe
350        let bytes_size: u32 = key_bytes
351            .len()
352            .try_into()
353            .expect("Keys should not serialize to many bytes");
354        let size_bytes = bytes_size.to_le_bytes(); // Wasm is little-endian
355        if let Err(error) = self.try_get_memory()?.set(bytes_written_ptr, &size_bytes) {
356            return Err(ExecError::Interpreter(error.into()).into());
357        }
358
359        Ok(Ok(()))
360    }
361
362    fn has_key(&mut self, name_ptr: u32, name_size: u32) -> Result<i32, Trap> {
363        let name = self.string_from_mem(name_ptr, name_size)?;
364        if self.context.named_keys_contains_key(&name) {
365            Ok(0)
366        } else {
367            Ok(1)
368        }
369    }
370
371    fn put_key(
372        &mut self,
373        name_ptr: u32,
374        name_size: u32,
375        key_ptr: u32,
376        key_size: u32,
377    ) -> Result<(), Trap> {
378        let name = self.string_from_mem(name_ptr, name_size)?;
379        let key = self.key_from_mem(key_ptr, key_size)?;
380
381        if let Some(payment_purse) = self.context.maybe_payment_purse() {
382            if Key::URef(payment_purse).normalize() == key.normalize() {
383                warn!("attempt to put_key payment purse");
384                return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
385                    handle_payment::Error::AttemptToPersistPaymentPurse as u8,
386                ))));
387            }
388        }
389        self.context.put_key(name, key).map_err(Into::into)
390    }
391
392    fn remove_key(&mut self, name_ptr: u32, name_size: u32) -> Result<(), Trap> {
393        let name = self.string_from_mem(name_ptr, name_size)?;
394        self.context.remove_key(&name)?;
395        Ok(())
396    }
397
398    /// Writes runtime context's account main purse to dest_ptr in the Wasm memory.
399    fn get_main_purse(&mut self, dest_ptr: u32) -> Result<(), Trap> {
400        let purse = self.context.get_main_purse()?;
401        let purse_bytes = purse.into_bytes().map_err(ExecError::BytesRepr)?;
402        self.try_get_memory()?
403            .set(dest_ptr, &purse_bytes)
404            .map_err(|e| ExecError::Interpreter(e.into()).into())
405    }
406
407    /// Writes caller (deploy) account public key to output_size_ptr in the Wasm
408    /// memory.
409    fn get_caller(&mut self, output_size_ptr: u32) -> Result<Result<(), ApiError>, Trap> {
410        if !self.can_write_to_host_buffer() {
411            // Exit early if the host buffer is already occupied
412            return Ok(Err(ApiError::HostBufferFull));
413        }
414        let value = CLValue::from_t(self.context.get_initiator()).map_err(ExecError::CLValue)?;
415        let value_size = value.inner_bytes().len();
416
417        // Save serialized public key into host buffer
418        if let Err(error) = self.write_host_buffer(value) {
419            return Ok(Err(error));
420        }
421
422        // Write output
423        let output_size_bytes = value_size.to_le_bytes(); // Wasm is little-endian
424        if let Err(error) = self
425            .try_get_memory()?
426            .set(output_size_ptr, &output_size_bytes)
427        {
428            return Err(ExecError::Interpreter(error.into()).into());
429        }
430        Ok(Ok(()))
431    }
432
433    /// Gets the immediate caller of the current execution
434    fn get_immediate_caller(&self) -> Option<&RuntimeStackFrame> {
435        self.stack.as_ref().and_then(|stack| stack.previous_frame())
436    }
437
438    /// Checks if immediate caller is of session type of the same account as the provided account
439    /// hash.
440    fn is_allowed_session_caller(&self, provided_account_hash: &AccountHash) -> bool {
441        if self.context.get_initiator() == PublicKey::System.to_account_hash() {
442            return true;
443        }
444
445        if let Some(Caller::Initiator { account_hash }) = self.get_immediate_caller() {
446            return account_hash == provided_account_hash;
447        }
448        false
449    }
450
451    /// Writes runtime context's phase to dest_ptr in the Wasm memory.
452    fn get_phase(&mut self, dest_ptr: u32) -> Result<(), Trap> {
453        let phase = self.context.phase();
454        let bytes = phase.into_bytes().map_err(ExecError::BytesRepr)?;
455        self.try_get_memory()?
456            .set(dest_ptr, &bytes)
457            .map_err(|e| ExecError::Interpreter(e.into()).into())
458    }
459
460    /// Writes requested field from runtime context's block info to dest_ptr in the Wasm memory.
461    fn get_block_info(&self, field_idx: u8, dest_ptr: u32) -> Result<(), Trap> {
462        if field_idx == 0 {
463            // original functionality
464            return self.get_blocktime(dest_ptr);
465        }
466        let block_info = self.context.get_block_info();
467
468        let mut data: Vec<u8> = vec![];
469        if field_idx == 1 {
470            data = block_info
471                .block_height()
472                .into_bytes()
473                .map_err(ExecError::BytesRepr)?;
474        }
475        if field_idx == 2 {
476            data = block_info
477                .parent_block_hash()
478                .into_bytes()
479                .map_err(ExecError::BytesRepr)?;
480        }
481        if field_idx == 3 {
482            data = block_info
483                .state_hash()
484                .into_bytes()
485                .map_err(ExecError::BytesRepr)?;
486        }
487        if field_idx == 4 {
488            data = self
489                .context
490                .protocol_version()
491                .into_bytes()
492                .map_err(ExecError::BytesRepr)?;
493        }
494        if field_idx == 5 {
495            data = self
496                .context
497                .engine_config()
498                .enable_entity
499                .into_bytes()
500                .map_err(ExecError::BytesRepr)?;
501        }
502        if data.is_empty() {
503            Err(ExecError::InvalidImputedOperation.into())
504        } else {
505            Ok(self
506                .try_get_memory()?
507                .set(dest_ptr, &data)
508                .map_err(|e| ExecError::Interpreter(e.into()))?)
509        }
510    }
511
512    /// Writes current blocktime to dest_ptr in Wasm memory.
513    fn get_blocktime(&self, dest_ptr: u32) -> Result<(), Trap> {
514        let block_info = self.context.get_block_info();
515        let blocktime = block_info
516            .block_time()
517            .into_bytes()
518            .map_err(ExecError::BytesRepr)?;
519        self.try_get_memory()?
520            .set(dest_ptr, &blocktime)
521            .map_err(|e| ExecError::Interpreter(e.into()).into())
522    }
523
524    /// Load the uref known by the given name into the Wasm memory
525    fn load_call_stack(
526        &mut self,
527        // (Output) Pointer to number of elements in the call stack.
528        call_stack_len_ptr: u32,
529        // (Output) Pointer to size in bytes of the serialized call stack.
530        result_size_ptr: u32,
531    ) -> Result<Result<(), ApiError>, Trap> {
532        if !self.can_write_to_host_buffer() {
533            // Exit early if the host buffer is already occupied
534            return Ok(Err(ApiError::HostBufferFull));
535        }
536        let call_stack: Vec<CallStackElement> = match self.try_get_stack() {
537            Ok(stack) => {
538                let caller = stack.call_stack_elements();
539                caller.iter().map_into().collect_vec()
540            }
541            Err(_error) => return Ok(Err(ApiError::Unhandled)),
542        };
543        let call_stack_len: u32 = match call_stack.len().try_into() {
544            Ok(value) => value,
545            Err(_) => return Ok(Err(ApiError::OutOfMemory)),
546        };
547        let call_stack_len_bytes = call_stack_len.to_le_bytes();
548
549        if let Err(error) = self
550            .try_get_memory()?
551            .set(call_stack_len_ptr, &call_stack_len_bytes)
552        {
553            return Err(ExecError::Interpreter(error.into()).into());
554        }
555
556        if call_stack_len == 0 {
557            return Ok(Ok(()));
558        }
559
560        let call_stack_cl_value = CLValue::from_t(call_stack).map_err(ExecError::CLValue)?;
561
562        let call_stack_cl_value_bytes_len: u32 =
563            match call_stack_cl_value.inner_bytes().len().try_into() {
564                Ok(value) => value,
565                Err(_) => return Ok(Err(ApiError::OutOfMemory)),
566            };
567
568        if let Err(error) = self.write_host_buffer(call_stack_cl_value) {
569            return Ok(Err(error));
570        }
571
572        let call_stack_cl_value_bytes_len_bytes = call_stack_cl_value_bytes_len.to_le_bytes();
573
574        if let Err(error) = self
575            .try_get_memory()?
576            .set(result_size_ptr, &call_stack_cl_value_bytes_len_bytes)
577        {
578            return Err(ExecError::Interpreter(error.into()).into());
579        }
580
581        Ok(Ok(()))
582    }
583
584    /// Returns information about the call stack based on a given action.
585    fn load_caller_information(
586        &mut self,
587        information: u8,
588        // (Output) Pointer to number of elements in the call stack.
589        call_stack_len_ptr: u32,
590        // (Output) Pointer to size in bytes of the serialized call stack.
591        result_size_ptr: u32,
592    ) -> Result<Result<(), ApiError>, Trap> {
593        if !self.can_write_to_host_buffer() {
594            // Exit early if the host buffer is already occupied
595            return Ok(Err(ApiError::HostBufferFull));
596        }
597
598        let caller_info = match CallerInformation::try_from(information) {
599            Ok(info) => info,
600            Err(error) => return Ok(Err(error)),
601        };
602
603        let caller = match caller_info {
604            CallerInformation::Initiator => {
605                let initiator_account_hash = self.context.get_initiator();
606                let caller = Caller::initiator(initiator_account_hash);
607                match CallerInfo::try_from(caller) {
608                    Ok(caller_info) => {
609                        vec![caller_info]
610                    }
611                    Err(_) => return Ok(Err(ApiError::CLTypeMismatch)),
612                }
613            }
614            CallerInformation::Immediate => match self.get_immediate_caller() {
615                Some(frame) => match CallerInfo::try_from(*frame) {
616                    Ok(immediate_info) => {
617                        vec![immediate_info]
618                    }
619                    Err(_) => return Ok(Err(ApiError::CLTypeMismatch)),
620                },
621                None => return Ok(Err(ApiError::Unhandled)),
622            },
623            CallerInformation::FullCallChain => match self.try_get_stack() {
624                Ok(call_stack) => {
625                    let call_stack = call_stack.call_stack_elements().clone();
626
627                    let mut ret = vec![];
628                    for caller in call_stack {
629                        match CallerInfo::try_from(caller) {
630                            Ok(info) => ret.push(info),
631                            Err(_) => return Ok(Err(ApiError::CLTypeMismatch)),
632                        }
633                    }
634                    ret
635                }
636                Err(_) => return Ok(Err(ApiError::Unhandled)),
637            },
638        };
639
640        let call_stack_len: u32 = match caller.len().try_into() {
641            Ok(value) => value,
642            Err(_) => return Ok(Err(ApiError::OutOfMemory)),
643        };
644        let call_stack_len_bytes = call_stack_len.to_le_bytes();
645
646        if let Err(error) = self
647            .try_get_memory()?
648            .set(call_stack_len_ptr, &call_stack_len_bytes)
649        {
650            return Err(ExecError::Interpreter(error.into()).into());
651        }
652
653        if call_stack_len == 0 {
654            return Ok(Ok(()));
655        }
656
657        let call_stack_cl_value = CLValue::from_t(caller).map_err(ExecError::CLValue)?;
658
659        let call_stack_cl_value_bytes_len: u32 =
660            match call_stack_cl_value.inner_bytes().len().try_into() {
661                Ok(value) => value,
662                Err(_) => return Ok(Err(ApiError::OutOfMemory)),
663            };
664
665        if let Err(error) = self.write_host_buffer(call_stack_cl_value) {
666            return Ok(Err(error));
667        }
668
669        let call_stack_cl_value_bytes_len_bytes = call_stack_cl_value_bytes_len.to_le_bytes();
670
671        if let Err(error) = self
672            .try_get_memory()?
673            .set(result_size_ptr, &call_stack_cl_value_bytes_len_bytes)
674        {
675            return Err(ExecError::Interpreter(error.into()).into());
676        }
677
678        Ok(Ok(()))
679    }
680
681    /// Return some bytes from the memory and terminate the current `sub_call`. Note that the return
682    /// type is `Trap`, indicating that this function will always kill the current Wasm instance.
683    fn ret(&mut self, value_ptr: u32, value_size: usize) -> Trap {
684        self.host_buffer = None;
685
686        let mem_get =
687            self.checked_memory_slice(value_ptr as usize, value_size, |data| data.to_vec());
688
689        match mem_get {
690            Ok(buf) => {
691                // Set the result field in the runtime and return the proper element of the `Error`
692                // enum indicating that the reason for exiting the module was a call to ret.
693                self.host_buffer = bytesrepr::deserialize_from_slice(buf).ok();
694
695                let urefs = match &self.host_buffer {
696                    Some(buf) => utils::extract_urefs(buf),
697                    None => Ok(vec![]),
698                };
699                match urefs {
700                    Ok(urefs) => {
701                        for uref in &urefs {
702                            if let Err(error) = self.context.validate_uref(uref) {
703                                return Trap::from(error);
704                            }
705                        }
706                        ExecError::Ret(urefs).into()
707                    }
708                    Err(e) => e.into(),
709                }
710            }
711            Err(e) => e.into(),
712        }
713    }
714
715    /// Checks if a [`HashAddr`] corresponds to a system contract.
716    fn is_system_contract(&self, hash_addr: HashAddr) -> Result<bool, ExecError> {
717        self.context.is_system_addressable_entity(&hash_addr)
718    }
719
720    fn get_named_argument<T: FromBytes + CLTyped>(
721        args: &RuntimeArgs,
722        name: &str,
723    ) -> Result<T, ExecError> {
724        let arg: CLValue = args
725            .get(name)
726            .cloned()
727            .ok_or(ExecError::Revert(ApiError::MissingArgument))?;
728        arg.into_t()
729            .map_err(|_| ExecError::Revert(ApiError::InvalidArgument))
730    }
731
732    fn try_get_named_argument<T: FromBytes + CLTyped>(
733        args: &RuntimeArgs,
734        name: &str,
735    ) -> Result<Option<T>, ExecError> {
736        match args.get(name) {
737            Some(arg) => {
738                let arg = arg
739                    .clone()
740                    .into_t()
741                    .map_err(|_| ExecError::Revert(ApiError::InvalidArgument))?;
742                Ok(Some(arg))
743            }
744            None => Ok(None),
745        }
746    }
747
748    fn reverter<T: Into<ApiError>>(error: T) -> ExecError {
749        let api_error: ApiError = error.into();
750        // NOTE: This is special casing needed to keep the native system contracts propagate
751        // GasLimit properly to the user. Once support for wasm system contract will be dropped this
752        // won't be necessary anymore.
753        match api_error {
754            ApiError::Mint(mint_error) if mint_error == mint::Error::GasLimit as u8 => {
755                ExecError::GasLimit
756            }
757            ApiError::AuctionError(auction_error)
758                if auction_error == auction::Error::GasLimit as u8 =>
759            {
760                ExecError::GasLimit
761            }
762            ApiError::HandlePayment(handle_payment_error)
763                if handle_payment_error == handle_payment::Error::GasLimit as u8 =>
764            {
765                ExecError::GasLimit
766            }
767            api_error => ExecError::Revert(api_error),
768        }
769    }
770
771    /// Calls host mint contract.
772    fn call_host_mint(
773        &mut self,
774        entry_point_name: &str,
775        runtime_args: &RuntimeArgs,
776        access_rights: ContextAccessRights,
777        stack: RuntimeStack,
778    ) -> Result<CLValue, ExecError> {
779        let gas_counter = self.gas_counter();
780
781        let mint_hash = self.context.get_system_contract(MINT)?;
782        let mint_addr = EntityAddr::new_system(mint_hash.value());
783        let mint_key = if self.context.engine_config().enable_entity {
784            Key::AddressableEntity(EntityAddr::System(mint_hash.value()))
785        } else {
786            Key::Hash(mint_hash.value())
787        };
788
789        let mint_named_keys = self
790            .context
791            .state()
792            .borrow_mut()
793            .get_named_keys(mint_addr)?;
794
795        let mut named_keys = mint_named_keys;
796
797        let runtime_context = self.context.new_from_self(
798            mint_key,
799            EntryPointType::Called,
800            &mut named_keys,
801            access_rights,
802            runtime_args.to_owned(),
803        );
804
805        let mut mint_runtime = self.new_with_stack(runtime_context, stack);
806
807        let engine_config = self.context.engine_config();
808        let system_config = engine_config.system_config();
809        let mint_costs = system_config.mint_costs();
810
811        let result = match entry_point_name {
812            // Type: `fn mint(amount: U512) -> Result<URef, ExecError>`
813            mint::METHOD_MINT => (|| {
814                mint_runtime.charge_system_contract_call(mint_costs.mint)?;
815
816                let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
817                let result: Result<URef, mint::Error> = mint_runtime.mint(amount);
818                if let Err(mint::Error::GasLimit) = result {
819                    return Err(ExecError::GasLimit);
820                }
821                CLValue::from_t(result).map_err(Self::reverter)
822            })(),
823            mint::METHOD_REDUCE_TOTAL_SUPPLY => (|| {
824                mint_runtime.charge_system_contract_call(mint_costs.reduce_total_supply)?;
825
826                let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
827                let result: Result<(), mint::Error> = mint_runtime.reduce_total_supply(amount);
828                CLValue::from_t(result).map_err(Self::reverter)
829            })(),
830            mint::METHOD_BURN => (|| {
831                mint_runtime.charge_system_contract_call(mint_costs.burn)?;
832
833                let purse: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?;
834                let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
835                let result: Result<(), mint::Error> = mint_runtime.burn(purse, amount);
836                CLValue::from_t(result).map_err(Self::reverter)
837            })(),
838            // Type: `fn create() -> URef`
839            mint::METHOD_CREATE => (|| {
840                mint_runtime.charge_system_contract_call(mint_costs.create)?;
841
842                let uref = mint_runtime.mint(U512::zero()).map_err(Self::reverter)?;
843                CLValue::from_t(uref).map_err(Self::reverter)
844            })(),
845            // Type: `fn balance(purse: URef) -> Option<U512>`
846            mint::METHOD_BALANCE => (|| {
847                mint_runtime.charge_system_contract_call(mint_costs.balance)?;
848
849                let uref: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?;
850
851                let maybe_balance: Option<U512> =
852                    mint_runtime.balance(uref).map_err(Self::reverter)?;
853                CLValue::from_t(maybe_balance).map_err(Self::reverter)
854            })(),
855            // Type: `fn transfer(maybe_to: Option<AccountHash>, source: URef, target: URef, amount:
856            // U512, id: Option<u64>) -> Result<(), ExecError>`
857            mint::METHOD_TRANSFER => (|| {
858                mint_runtime.charge_system_contract_call(mint_costs.transfer)?;
859
860                let maybe_to: Option<AccountHash> =
861                    Self::get_named_argument(runtime_args, mint::ARG_TO)?;
862                let source: URef = Self::get_named_argument(runtime_args, mint::ARG_SOURCE)?;
863                let target: URef = Self::get_named_argument(runtime_args, mint::ARG_TARGET)?;
864                let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
865                let id: Option<u64> = Self::get_named_argument(runtime_args, mint::ARG_ID)?;
866                let result: Result<(), mint::Error> =
867                    mint_runtime.transfer(maybe_to, source, target, amount, id);
868
869                CLValue::from_t(result).map_err(Self::reverter)
870            })(),
871            // Type: `fn read_base_round_reward() -> Result<U512, ExecError>`
872            mint::METHOD_READ_BASE_ROUND_REWARD => (|| {
873                mint_runtime.charge_system_contract_call(mint_costs.read_base_round_reward)?;
874
875                let result: U512 = mint_runtime
876                    .read_base_round_reward()
877                    .map_err(Self::reverter)?;
878                CLValue::from_t(result).map_err(Self::reverter)
879            })(),
880            mint::METHOD_MINT_INTO_EXISTING_PURSE => (|| {
881                mint_runtime.charge_system_contract_call(mint_costs.mint_into_existing_purse)?;
882
883                let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
884                let existing_purse: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?;
885
886                let result: Result<(), mint::Error> =
887                    mint_runtime.mint_into_existing_purse(existing_purse, amount);
888                CLValue::from_t(result).map_err(Self::reverter)
889            })(),
890            _ => {
891                // Code should never reach this point as existence of the entrypoint is validated
892                // before reaching this point.
893                Ok(CLValue::unit())
894            }
895        };
896
897        // Charge just for the amount that particular entry point cost - using gas cost from the
898        // isolated runtime might have a recursive costs whenever system contract calls other system
899        // contract.
900        self.gas(
901            mint_runtime
902                .gas_counter()
903                .checked_sub(gas_counter)
904                .unwrap_or(gas_counter),
905        )?;
906
907        // Result still contains a result, but the entrypoints logic does not exit early on errors.
908        let ret = result?;
909
910        // Update outer spending approved limit.
911        self.context
912            .set_remaining_spending_limit(mint_runtime.context.remaining_spending_limit());
913
914        let urefs = utils::extract_urefs(&ret)?;
915        self.context.access_rights_extend(&urefs);
916        {
917            let transfers = self.context.transfers_mut();
918            mint_runtime.context.transfers().clone_into(transfers);
919        }
920        Ok(ret)
921    }
922
923    /// Calls host `handle_payment` contract.
924    fn call_host_handle_payment(
925        &mut self,
926        entry_point_name: &str,
927        runtime_args: &RuntimeArgs,
928        access_rights: ContextAccessRights,
929        stack: RuntimeStack,
930    ) -> Result<CLValue, ExecError> {
931        let gas_counter = self.gas_counter();
932
933        let handle_payment_hash = self.context.get_system_contract(HANDLE_PAYMENT)?;
934        let handle_payment_key = if self.context.engine_config().enable_entity {
935            Key::AddressableEntity(EntityAddr::System(handle_payment_hash.value()))
936        } else {
937            Key::Hash(handle_payment_hash.value())
938        };
939
940        let handle_payment_named_keys = self
941            .context
942            .state()
943            .borrow_mut()
944            .get_named_keys(EntityAddr::System(handle_payment_hash.value()))?;
945
946        let mut named_keys = handle_payment_named_keys;
947
948        let runtime_context = self.context.new_from_self(
949            handle_payment_key,
950            EntryPointType::Called,
951            &mut named_keys,
952            access_rights,
953            runtime_args.to_owned(),
954        );
955
956        let mut runtime = self.new_with_stack(runtime_context, stack);
957
958        let engine_config = self.context.engine_config();
959        let system_config = engine_config.system_config();
960        let handle_payment_costs = system_config.handle_payment_costs();
961
962        let result = match entry_point_name {
963            handle_payment::METHOD_GET_PAYMENT_PURSE => {
964                runtime.charge_system_contract_call(handle_payment_costs.get_payment_purse)?;
965                match self.context.maybe_payment_purse() {
966                    Some(payment_purse) => CLValue::from_t(payment_purse).map_err(Self::reverter),
967                    None => {
968                        let payment_purse = runtime.get_payment_purse().map_err(Self::reverter)?;
969                        self.context.set_payment_purse(payment_purse);
970                        CLValue::from_t(payment_purse).map_err(Self::reverter)
971                    }
972                }
973            }
974            handle_payment::METHOD_SET_REFUND_PURSE => (|| {
975                runtime.charge_system_contract_call(handle_payment_costs.set_refund_purse)?;
976
977                let purse: URef =
978                    Self::get_named_argument(runtime_args, handle_payment::ARG_PURSE)?;
979                runtime.set_refund_purse(purse).map_err(Self::reverter)?;
980                CLValue::from_t(()).map_err(Self::reverter)
981            })(),
982            handle_payment::METHOD_GET_REFUND_PURSE => (|| {
983                runtime.charge_system_contract_call(handle_payment_costs.get_refund_purse)?;
984
985                let maybe_purse = runtime.get_refund_purse().map_err(Self::reverter)?;
986                CLValue::from_t(maybe_purse).map_err(Self::reverter)
987            })(),
988            _ => {
989                // Code should never reach here as existence of the entrypoint is validated before
990                // reaching this point.
991                Ok(CLValue::unit())
992            }
993        };
994
995        self.gas(
996            runtime
997                .gas_counter()
998                .checked_sub(gas_counter)
999                .unwrap_or(gas_counter),
1000        )?;
1001
1002        let ret = result?;
1003
1004        let urefs = utils::extract_urefs(&ret)?;
1005        self.context.access_rights_extend(&urefs);
1006        {
1007            let transfers = self.context.transfers_mut();
1008            runtime.context.transfers().clone_into(transfers);
1009        }
1010        Ok(ret)
1011    }
1012
1013    /// Calls host auction contract.
1014    fn call_host_auction(
1015        &mut self,
1016        entry_point_name: &str,
1017        runtime_args: &RuntimeArgs,
1018        access_rights: ContextAccessRights,
1019        stack: RuntimeStack,
1020    ) -> Result<CLValue, ExecError> {
1021        let gas_counter = self.gas_counter();
1022
1023        let auction_hash = self.context.get_system_contract(AUCTION)?;
1024        let auction_key = if self.context.engine_config().enable_entity {
1025            Key::AddressableEntity(EntityAddr::System(auction_hash.value()))
1026        } else {
1027            Key::Hash(auction_hash.value())
1028        };
1029
1030        let auction_named_keys = self
1031            .context
1032            .state()
1033            .borrow_mut()
1034            .get_named_keys(EntityAddr::System(auction_hash.value()))?;
1035
1036        let mut named_keys = auction_named_keys;
1037
1038        let runtime_context = self.context.new_from_self(
1039            auction_key,
1040            EntryPointType::Called,
1041            &mut named_keys,
1042            access_rights,
1043            runtime_args.to_owned(),
1044        );
1045
1046        let mut runtime = self.new_with_stack(runtime_context, stack);
1047
1048        let engine_config = self.context.engine_config();
1049        let system_config = engine_config.system_config();
1050        let auction_costs = system_config.auction_costs();
1051
1052        let result = match entry_point_name {
1053            auction::METHOD_GET_ERA_VALIDATORS => (|| {
1054                runtime.charge_system_contract_call::<u64>(auction_costs.get_era_validators)?;
1055
1056                let result = runtime.get_era_validators().map_err(Self::reverter)?;
1057
1058                CLValue::from_t(result).map_err(Self::reverter)
1059            })(),
1060
1061            auction::METHOD_ADD_BID => (|| {
1062                runtime.charge_system_contract_call(auction_costs.add_bid)?;
1063                let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
1064                let delegation_rate =
1065                    Self::get_named_argument(runtime_args, auction::ARG_DELEGATION_RATE)?;
1066                let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
1067
1068                let global_minimum_delegation_amount =
1069                    self.context.engine_config().minimum_delegation_amount();
1070                let minimum_delegation_amount = Self::try_get_named_argument(
1071                    runtime_args,
1072                    auction::ARG_MINIMUM_DELEGATION_AMOUNT,
1073                )?
1074                .unwrap_or(global_minimum_delegation_amount);
1075
1076                let global_maximum_delegation_amount =
1077                    self.context.engine_config().maximum_delegation_amount();
1078                let maximum_delegation_amount = Self::try_get_named_argument(
1079                    runtime_args,
1080                    auction::ARG_MAXIMUM_DELEGATION_AMOUNT,
1081                )?
1082                .unwrap_or(global_maximum_delegation_amount);
1083
1084                if minimum_delegation_amount < global_minimum_delegation_amount
1085                    || maximum_delegation_amount > global_maximum_delegation_amount
1086                    || minimum_delegation_amount > maximum_delegation_amount
1087                {
1088                    return Err(ExecError::Revert(ApiError::InvalidDelegationAmountLimits));
1089                }
1090                let reserved_slots =
1091                    Self::try_get_named_argument(runtime_args, auction::ARG_RESERVED_SLOTS)?
1092                        .unwrap_or(0);
1093
1094                let max_delegators_per_validator =
1095                    self.context.engine_config().max_delegators_per_validator();
1096
1097                let minimum_bid_amount = self.context().engine_config().minimum_bid_amount();
1098
1099                let result = runtime
1100                    .add_bid(
1101                        public_key,
1102                        delegation_rate,
1103                        amount,
1104                        minimum_delegation_amount,
1105                        maximum_delegation_amount,
1106                        minimum_bid_amount,
1107                        max_delegators_per_validator,
1108                        reserved_slots,
1109                    )
1110                    .map_err(Self::reverter)?;
1111
1112                CLValue::from_t(result).map_err(Self::reverter)
1113            })(),
1114
1115            auction::METHOD_WITHDRAW_BID => (|| {
1116                runtime.charge_system_contract_call(auction_costs.withdraw_bid)?;
1117
1118                let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
1119                let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
1120                let min_bid_amount = self.context.engine_config().minimum_bid_amount();
1121
1122                let result = runtime
1123                    .withdraw_bid(public_key, amount, min_bid_amount)
1124                    .map_err(Self::reverter)?;
1125                CLValue::from_t(result).map_err(Self::reverter)
1126            })(),
1127
1128            auction::METHOD_DELEGATE => (|| {
1129                runtime.charge_system_contract_call(auction_costs.delegate)?;
1130
1131                let delegator = {
1132                    match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
1133                        Ok(pk) => DelegatorKind::PublicKey(pk),
1134                        Err(_) => {
1135                            let uref: URef = match Self::get_named_argument(
1136                                runtime_args,
1137                                auction::ARG_DELEGATOR_PURSE,
1138                            ) {
1139                                Ok(uref) => uref,
1140                                Err(err) => {
1141                                    debug!(%err, "failed to get delegator purse argument");
1142                                    return Err(err);
1143                                }
1144                            };
1145                            DelegatorKind::Purse(uref.addr())
1146                        }
1147                    }
1148                };
1149                let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
1150                let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
1151
1152                let max_delegators_per_validator =
1153                    self.context.engine_config().max_delegators_per_validator();
1154
1155                let result = runtime
1156                    .delegate(delegator, validator, amount, max_delegators_per_validator)
1157                    .map_err(Self::reverter)?;
1158
1159                CLValue::from_t(result).map_err(Self::reverter)
1160            })(),
1161
1162            auction::METHOD_UNDELEGATE => (|| {
1163                runtime.charge_system_contract_call(auction_costs.undelegate)?;
1164
1165                let delegator = {
1166                    match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
1167                        Ok(pk) => DelegatorKind::PublicKey(pk),
1168                        Err(_) => {
1169                            let uref: URef = match Self::get_named_argument(
1170                                runtime_args,
1171                                auction::ARG_DELEGATOR_PURSE,
1172                            ) {
1173                                Ok(uref) => uref,
1174                                Err(err) => {
1175                                    debug!(%err, "failed to get delegator purse argument");
1176                                    return Err(err);
1177                                }
1178                            };
1179                            DelegatorKind::Purse(uref.addr())
1180                        }
1181                    }
1182                };
1183                let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
1184                let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
1185
1186                let result = runtime
1187                    .undelegate(delegator, validator, amount)
1188                    .map_err(Self::reverter)?;
1189
1190                CLValue::from_t(result).map_err(Self::reverter)
1191            })(),
1192
1193            auction::METHOD_REDELEGATE => (|| {
1194                runtime.charge_system_contract_call(auction_costs.redelegate)?;
1195
1196                let delegator = {
1197                    match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
1198                        Ok(pk) => DelegatorKind::PublicKey(pk),
1199                        Err(_) => {
1200                            let uref: URef = match Self::get_named_argument(
1201                                runtime_args,
1202                                auction::ARG_DELEGATOR_PURSE,
1203                            ) {
1204                                Ok(uref) => uref,
1205                                Err(err) => {
1206                                    debug!(%err, "failed to get delegator purse argument");
1207                                    return Err(err);
1208                                }
1209                            };
1210                            DelegatorKind::Purse(uref.addr())
1211                        }
1212                    }
1213                };
1214                let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
1215                let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
1216                let new_validator =
1217                    Self::get_named_argument(runtime_args, auction::ARG_NEW_VALIDATOR)?;
1218
1219                let result = runtime
1220                    .redelegate(delegator, validator, amount, new_validator)
1221                    .map_err(Self::reverter)?;
1222
1223                CLValue::from_t(result).map_err(Self::reverter)
1224            })(),
1225
1226            auction::METHOD_RUN_AUCTION => (|| {
1227                runtime.charge_system_contract_call(auction_costs.run_auction)?;
1228
1229                let era_end_timestamp_millis =
1230                    Self::get_named_argument(runtime_args, auction::ARG_ERA_END_TIMESTAMP_MILLIS)?;
1231                let evicted_validators =
1232                    Self::get_named_argument(runtime_args, auction::ARG_EVICTED_VALIDATORS)?;
1233
1234                let max_delegators_per_validator =
1235                    self.context.engine_config().max_delegators_per_validator();
1236                let minimum_bid_amount = self.context.engine_config().minimum_bid_amount();
1237                runtime
1238                    .run_auction(
1239                        era_end_timestamp_millis,
1240                        evicted_validators,
1241                        max_delegators_per_validator,
1242                        true,
1243                        Ratio::new_raw(U512::from(1), U512::from(5)),
1244                        minimum_bid_amount,
1245                    )
1246                    .map_err(Self::reverter)?;
1247
1248                CLValue::from_t(()).map_err(Self::reverter)
1249            })(),
1250
1251            // Type: `fn slash(validator_public_keys: &[PublicKey]) -> Result<(), ExecError>`
1252            auction::METHOD_SLASH => (|| {
1253                runtime.charge_system_contract_call(auction_costs.slash)?;
1254
1255                let validator_public_keys =
1256                    Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR_PUBLIC_KEYS)?;
1257                runtime
1258                    .slash(validator_public_keys)
1259                    .map_err(Self::reverter)?;
1260                CLValue::from_t(()).map_err(Self::reverter)
1261            })(),
1262
1263            // Type: `fn distribute(reward_factors: BTreeMap<PublicKey, u512>) -> Result<(),
1264            // ExecError>`
1265            auction::METHOD_DISTRIBUTE => (|| {
1266                runtime.charge_system_contract_call(auction_costs.distribute)?;
1267                let rewards = Self::get_named_argument(runtime_args, auction::ARG_REWARDS_MAP)?;
1268                runtime.distribute(rewards).map_err(Self::reverter)?;
1269                CLValue::from_t(()).map_err(Self::reverter)
1270            })(),
1271
1272            // Type: `fn read_era_id() -> Result<EraId, ExecError>`
1273            auction::METHOD_READ_ERA_ID => (|| {
1274                runtime.charge_system_contract_call(auction_costs.read_era_id)?;
1275
1276                let result = runtime.read_era_id().map_err(Self::reverter)?;
1277                CLValue::from_t(result).map_err(Self::reverter)
1278            })(),
1279
1280            auction::METHOD_ACTIVATE_BID => (|| {
1281                runtime.charge_system_contract_call(auction_costs.activate_bid)?;
1282
1283                let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
1284
1285                runtime
1286                    .activate_bid(validator, engine_config.minimum_bid_amount())
1287                    .map_err(Self::reverter)?;
1288
1289                CLValue::from_t(()).map_err(Self::reverter)
1290            })(),
1291            auction::METHOD_CHANGE_BID_PUBLIC_KEY => (|| {
1292                runtime.charge_system_contract_call(auction_costs.change_bid_public_key)?;
1293
1294                let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
1295                let new_public_key =
1296                    Self::get_named_argument(runtime_args, auction::ARG_NEW_PUBLIC_KEY)?;
1297
1298                runtime
1299                    .change_bid_public_key(public_key, new_public_key)
1300                    .map_err(Self::reverter)?;
1301
1302                CLValue::from_t(()).map_err(Self::reverter)
1303            })(),
1304            auction::METHOD_ADD_RESERVATIONS => (|| {
1305                runtime.charge_system_contract_call(auction_costs.add_reservations)?;
1306
1307                let reservations =
1308                    Self::get_named_argument(runtime_args, auction::ARG_RESERVATIONS)?;
1309
1310                runtime
1311                    .add_reservations(reservations)
1312                    .map_err(Self::reverter)?;
1313
1314                CLValue::from_t(()).map_err(Self::reverter)
1315            })(),
1316            auction::METHOD_CANCEL_RESERVATIONS => (|| {
1317                runtime.charge_system_contract_call(auction_costs.cancel_reservations)?;
1318
1319                let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
1320                let delegators = Self::get_named_argument(runtime_args, auction::ARG_DELEGATORS)?;
1321                let max_delegators_per_validator =
1322                    self.context.engine_config().max_delegators_per_validator();
1323
1324                runtime
1325                    .cancel_reservations(validator, delegators, max_delegators_per_validator)
1326                    .map_err(Self::reverter)?;
1327
1328                CLValue::from_t(()).map_err(Self::reverter)
1329            })(),
1330            _ => {
1331                // Code should never reach here as existence of the entrypoint is validated before
1332                // reaching this point.
1333                Ok(CLValue::unit())
1334            }
1335        };
1336
1337        // Charge for the gas spent during execution in an isolated runtime.
1338        self.gas(
1339            runtime
1340                .gas_counter()
1341                .checked_sub(gas_counter)
1342                .unwrap_or(gas_counter),
1343        )?;
1344
1345        // Result still contains a result, but the entrypoints logic does not exit early on errors.
1346        let ret = result?;
1347
1348        let urefs = utils::extract_urefs(&ret)?;
1349        self.context.access_rights_extend(&urefs);
1350        {
1351            let transfers = self.context.transfers_mut();
1352            runtime.context.transfers().clone_into(transfers);
1353        }
1354
1355        Ok(ret)
1356    }
1357
1358    /// Call a contract by pushing a stack element onto the frame.
1359    pub(crate) fn call_contract_with_stack(
1360        &mut self,
1361        contract_hash: AddressableEntityHash,
1362        entry_point_name: &str,
1363        args: RuntimeArgs,
1364        stack: RuntimeStack,
1365    ) -> Result<CLValue, ExecError> {
1366        self.stack = Some(stack);
1367
1368        self.call_contract(contract_hash, entry_point_name, args)
1369    }
1370
1371    pub(crate) fn execute_module_bytes(
1372        &mut self,
1373        module_bytes: &Bytes,
1374        stack: RuntimeStack,
1375    ) -> Result<CLValue, ExecError> {
1376        let protocol_version = self.context.protocol_version();
1377        let engine_config = self.context.engine_config();
1378        let wasm_config = engine_config.wasm_config();
1379        #[cfg(feature = "test-support")]
1380        let max_stack_height = wasm_config.v1().max_stack_height();
1381        let module = preprocess(*wasm_config, module_bytes)?;
1382        let (instance, memory) =
1383            utils::instance_and_memory(module.clone(), protocol_version, engine_config)?;
1384        self.memory = Some(memory);
1385        self.module = Some(module);
1386        self.stack = Some(stack);
1387        self.context.set_args(utils::attenuate_uref_in_args(
1388            self.context.args().clone(),
1389            self.context
1390                .runtime_footprint()
1391                .borrow()
1392                .main_purse()
1393                .expect("line 1183")
1394                .addr(),
1395            AccessRights::WRITE,
1396        )?);
1397
1398        let result = instance.invoke_export(DEFAULT_ENTRY_POINT_NAME, &[], self);
1399
1400        let error = match result {
1401            Err(error) => error,
1402            // If `Ok` and the `host_buffer` is `None`, the contract's execution succeeded but did
1403            // not explicitly call `runtime::ret()`.  Treat as though the execution
1404            // returned the unit type `()` as per Rust functions which don't specify a
1405            // return value.
1406            Ok(_) => {
1407                return Ok(self.take_host_buffer().unwrap_or(CLValue::from_t(())?));
1408            }
1409        };
1410
1411        #[cfg(feature = "test-support")]
1412        dump_runtime_stack_info(instance, max_stack_height);
1413
1414        if let Some(host_error) = error.as_host_error() {
1415            // If the "error" was in fact a trap caused by calling `ret` then
1416            // this is normal operation and we should return the value captured
1417            // in the Runtime result field.
1418            let downcasted_error = host_error.downcast_ref::<ExecError>();
1419            return match downcasted_error {
1420                Some(ExecError::Ret(ref _ret_urefs)) => self
1421                    .take_host_buffer()
1422                    .ok_or(ExecError::ExpectedReturnValue),
1423                Some(error) => Err(error.clone()),
1424                None => Err(ExecError::Interpreter(host_error.to_string())),
1425            };
1426        }
1427        Err(ExecError::Interpreter(error.into()))
1428    }
1429
1430    /// Calls contract living under a `key`, with supplied `args`.
1431    pub fn call_contract(
1432        &mut self,
1433        contract_hash: AddressableEntityHash,
1434        entry_point_name: &str,
1435        args: RuntimeArgs,
1436    ) -> Result<CLValue, ExecError> {
1437        let contract_hash = contract_hash.value();
1438        let identifier = CallContractIdentifier::Contract { contract_hash };
1439
1440        self.execute_contract(identifier, entry_point_name, args)
1441    }
1442
1443    /// Calls `version` of the contract living at `key`, invoking `method` with
1444    /// supplied `args`. This function also checks the args conform with the
1445    /// types given in the contract header.
1446    pub fn call_versioned_contract(
1447        &mut self,
1448        contract_package_hash: PackageHash,
1449        contract_version: Option<EntityVersion>,
1450        entry_point_name: String,
1451        args: RuntimeArgs,
1452    ) -> Result<CLValue, ExecError> {
1453        let contract_package_hash = contract_package_hash.value();
1454        let identifier = CallContractIdentifier::ContractPackage {
1455            contract_package_hash,
1456            version: contract_version,
1457        };
1458
1459        self.execute_contract(identifier, &entry_point_name, args)
1460    }
1461
1462    /// Calls `version` of the contract living at `key`, invoking `method` with
1463    /// supplied `args`. This function also checks the args conform with the
1464    /// types given in the contract header.
1465    pub fn call_package_version(
1466        &mut self,
1467        contract_package_hash: PackageHash,
1468        major_version: u32,
1469        contract_version: EntityVersion,
1470        entry_point_name: String,
1471        args: RuntimeArgs,
1472    ) -> Result<CLValue, ExecError> {
1473        let contract_package_hash = contract_package_hash.value();
1474        let identifier = CallContractIdentifier::PackageVersion {
1475            contract_package_hash,
1476            major_version,
1477            version: contract_version,
1478        };
1479
1480        self.execute_contract(identifier, &entry_point_name, args)
1481    }
1482
1483    fn get_key_from_entity_addr(&self, entity_addr: EntityAddr) -> Key {
1484        if self.context().engine_config().enable_entity {
1485            Key::AddressableEntity(entity_addr)
1486        } else {
1487            match entity_addr {
1488                EntityAddr::System(system_hash_addr) => Key::Hash(system_hash_addr),
1489                EntityAddr::Account(hash_addr) => Key::Account(AccountHash::new(hash_addr)),
1490                EntityAddr::SmartContract(contract_hash_addr) => Key::Hash(contract_hash_addr),
1491            }
1492        }
1493    }
1494
1495    fn get_context_key_for_contract_call(
1496        &self,
1497        entity_addr: EntityAddr,
1498        entry_point: &EntityEntryPoint,
1499    ) -> Result<Key, ExecError> {
1500        let current = self.context.entry_point_type();
1501        let next = entry_point.entry_point_type();
1502        match (current, next) {
1503            (EntryPointType::Called, EntryPointType::Caller) => {
1504                // Session code can't be called from Contract code for security reasons.
1505                Err(ExecError::InvalidContext)
1506            }
1507            (EntryPointType::Factory, EntryPointType::Caller) => {
1508                // Session code can't be called from Installer code for security reasons.
1509                Err(ExecError::InvalidContext)
1510            }
1511            (EntryPointType::Caller, EntryPointType::Caller) => {
1512                // Session code called from session reuses current base key
1513                Ok(self.context.get_context_key())
1514            }
1515            (EntryPointType::Caller, EntryPointType::Called)
1516            | (EntryPointType::Called, EntryPointType::Called) => {
1517                Ok(self.get_key_from_entity_addr(entity_addr))
1518            }
1519            _ => {
1520                // Any other combination (installer, normal, etc.) is a contract context.
1521                Ok(self.get_key_from_entity_addr(entity_addr))
1522            }
1523        }
1524    }
1525
1526    fn try_get_memory(&self) -> Result<&MemoryRef, ExecError> {
1527        self.memory.as_ref().ok_or(ExecError::WasmPreprocessing(
1528            PreprocessingError::MissingMemorySection,
1529        ))
1530    }
1531
1532    fn try_get_module(&self) -> Result<&Module, ExecError> {
1533        self.module.as_ref().ok_or(ExecError::WasmPreprocessing(
1534            PreprocessingError::MissingModule,
1535        ))
1536    }
1537
1538    fn try_get_stack(&self) -> Result<&RuntimeStack, ExecError> {
1539        self.stack.as_ref().ok_or(ExecError::MissingRuntimeStack)
1540    }
1541
1542    fn maybe_system_type(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
1543        let is_mint = self.is_mint(hash_addr);
1544        if is_mint.is_some() {
1545            return is_mint;
1546        };
1547
1548        let is_auction = self.is_auction(hash_addr);
1549        if is_auction.is_some() {
1550            return is_auction;
1551        };
1552        let is_handle = self.is_handle_payment(hash_addr);
1553        if is_handle.is_some() {
1554            return is_handle;
1555        };
1556
1557        None
1558    }
1559
1560    fn is_mint(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
1561        let hash = match self.context.get_system_contract(MINT) {
1562            Ok(hash) => hash,
1563            Err(_) => {
1564                error!("Failed to get system mint contract hash");
1565                return None;
1566            }
1567        };
1568        if hash.value() == hash_addr {
1569            Some(SystemEntityType::Mint)
1570        } else {
1571            None
1572        }
1573    }
1574
1575    /// Checks if current context is the `handle_payment` system contract.
1576    fn is_handle_payment(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
1577        let hash = match self.context.get_system_contract(HANDLE_PAYMENT) {
1578            Ok(hash) => hash,
1579            Err(_) => {
1580                error!("Failed to get system handle payment contract hash");
1581                return None;
1582            }
1583        };
1584        if hash.value() == hash_addr {
1585            Some(SystemEntityType::HandlePayment)
1586        } else {
1587            None
1588        }
1589    }
1590
1591    /// Checks if given hash is the auction system contract.
1592    fn is_auction(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
1593        let hash = match self.context.get_system_contract(AUCTION) {
1594            Ok(hash) => hash,
1595            Err(_) => {
1596                error!("Failed to get system auction contract hash");
1597                return None;
1598            }
1599        };
1600
1601        if hash.value() == hash_addr {
1602            Some(SystemEntityType::Auction)
1603        } else {
1604            None
1605        }
1606    }
1607
1608    fn execute_contract(
1609        &mut self,
1610        identifier: CallContractIdentifier,
1611        entry_point_name: &str,
1612        args: RuntimeArgs,
1613    ) -> Result<CLValue, ExecError> {
1614        let (footprint, entity_addr, package) = match identifier {
1615            CallContractIdentifier::Contract { contract_hash } => {
1616                let entity_addr = if self.context.is_system_addressable_entity(&contract_hash)? {
1617                    EntityAddr::new_system(contract_hash)
1618                } else {
1619                    EntityAddr::new_smart_contract(contract_hash)
1620                };
1621                let footprint = match self.context.read_gs(&Key::Hash(contract_hash))? {
1622                    Some(StoredValue::Contract(contract)) => {
1623                        if self.context.engine_config().enable_entity {
1624                            self.migrate_contract_and_contract_package(contract_hash)?;
1625                        };
1626
1627                        let maybe_system_entity_type = self.maybe_system_type(contract_hash);
1628
1629                        RuntimeFootprint::new_contract_footprint(
1630                            ContractHash::new(contract_hash),
1631                            contract,
1632                            maybe_system_entity_type,
1633                        )
1634                    }
1635                    Some(_) | None => {
1636                        if !self.context.engine_config().enable_entity {
1637                            return Err(ExecError::KeyNotFound(Key::Hash(contract_hash)));
1638                        }
1639                        let key = Key::AddressableEntity(entity_addr);
1640                        let entity = self.context.read_gs_typed::<AddressableEntity>(&key)?;
1641                        let entity_named_keys = self
1642                            .context
1643                            .state()
1644                            .borrow_mut()
1645                            .get_named_keys(entity_addr)?;
1646                        let entry_points = self.context.get_casper_vm_v1_entry_point(key)?;
1647                        RuntimeFootprint::new_entity_footprint(
1648                            entity_addr,
1649                            entity,
1650                            entity_named_keys,
1651                            entry_points,
1652                        )
1653                    }
1654                };
1655
1656                let package_hash = footprint.package_hash().ok_or(ExecError::InvalidContext)?;
1657                let package: Package = self.context.get_package(package_hash)?;
1658
1659                // System contract hashes are disabled at upgrade point
1660                let is_calling_system_contract = self.is_system_contract(contract_hash)?;
1661
1662                let entity_hash = AddressableEntityHash::new(contract_hash);
1663
1664                // Check if provided contract hash is disabled
1665                let is_contract_enabled = package.is_entity_enabled(&entity_addr);
1666
1667                if !is_calling_system_contract && !is_contract_enabled {
1668                    return Err(ExecError::DisabledEntity(entity_hash));
1669                }
1670
1671                (footprint, entity_addr, package)
1672            }
1673            CallContractIdentifier::ContractPackage {
1674                contract_package_hash,
1675                version,
1676            } => {
1677                if version.is_some() {
1678                    return Err(ExecError::Revert(ApiError::UnexpectedContractRefVariant));
1679                }
1680
1681                let package = self.context.get_package(contract_package_hash)?;
1682
1683                let entity_version_key = match package.current_entity_version() {
1684                    Some(v) => v,
1685                    None => {
1686                        return Err(ExecError::NoActiveEntityVersions(
1687                            contract_package_hash.into(),
1688                        ));
1689                    }
1690                };
1691
1692                if package.is_version_missing(entity_version_key) {
1693                    return Err(ExecError::MissingEntityVersion(entity_version_key));
1694                }
1695
1696                if !package.is_version_enabled(entity_version_key) {
1697                    return Err(ExecError::DisabledEntityVersion(entity_version_key));
1698                }
1699
1700                let hash_addr = package
1701                    .lookup_entity_hash(entity_version_key)
1702                    .copied()
1703                    .ok_or(ExecError::MissingEntityVersion(entity_version_key))?
1704                    .value();
1705
1706                let entity_addr = if self.context.is_system_addressable_entity(&hash_addr)? {
1707                    EntityAddr::new_system(hash_addr)
1708                } else {
1709                    EntityAddr::new_smart_contract(hash_addr)
1710                };
1711
1712                let footprint = match self.context.read_gs(&Key::Hash(hash_addr))? {
1713                    Some(StoredValue::Contract(contract)) => {
1714                        if self.context.engine_config().enable_entity {
1715                            self.migrate_contract_and_contract_package(hash_addr)?;
1716                        };
1717                        let maybe_system_entity_type = self.maybe_system_type(hash_addr);
1718                        RuntimeFootprint::new_contract_footprint(
1719                            ContractHash::new(hash_addr),
1720                            contract,
1721                            maybe_system_entity_type,
1722                        )
1723                    }
1724                    Some(_) | None => {
1725                        if !self.context.engine_config().enable_entity {
1726                            return Err(ExecError::KeyNotFound(Key::Hash(hash_addr)));
1727                        }
1728                        let key = Key::AddressableEntity(entity_addr);
1729                        let entity = self.context.read_gs_typed::<AddressableEntity>(&key)?;
1730                        let entity_named_keys = self
1731                            .context
1732                            .state()
1733                            .borrow_mut()
1734                            .get_named_keys(entity_addr)?;
1735                        let entry_points = self.context.get_casper_vm_v1_entry_point(key)?;
1736                        RuntimeFootprint::new_entity_footprint(
1737                            entity_addr,
1738                            entity,
1739                            entity_named_keys,
1740                            entry_points,
1741                        )
1742                    }
1743                };
1744
1745                (footprint, entity_addr, package)
1746            }
1747            CallContractIdentifier::PackageVersion {
1748                contract_package_hash,
1749                major_version,
1750                version,
1751            } => {
1752                let package = self.context.get_package(contract_package_hash)?;
1753
1754                let entity_version_key = EntityVersionKey::new(major_version, version);
1755
1756                if package.is_version_missing(entity_version_key) {
1757                    return Err(ExecError::MissingEntityVersion(entity_version_key));
1758                }
1759
1760                if !package.is_version_enabled(entity_version_key) {
1761                    return Err(ExecError::DisabledEntityVersion(entity_version_key));
1762                }
1763
1764                let hash_addr = package
1765                    .lookup_entity_hash(entity_version_key)
1766                    .copied()
1767                    .ok_or(ExecError::MissingEntityVersion(entity_version_key))?
1768                    .value();
1769
1770                let entity_addr = if self.context.is_system_addressable_entity(&hash_addr)? {
1771                    EntityAddr::new_system(hash_addr)
1772                } else {
1773                    EntityAddr::new_smart_contract(hash_addr)
1774                };
1775
1776                let footprint = match self.context.read_gs(&Key::Hash(hash_addr))? {
1777                    Some(StoredValue::Contract(contract)) => {
1778                        if self.context.engine_config().enable_entity {
1779                            self.migrate_contract_and_contract_package(hash_addr)?;
1780                        };
1781                        let maybe_system_entity_type = self.maybe_system_type(hash_addr);
1782                        RuntimeFootprint::new_contract_footprint(
1783                            ContractHash::new(hash_addr),
1784                            contract,
1785                            maybe_system_entity_type,
1786                        )
1787                    }
1788                    Some(_) | None => {
1789                        if !self.context.engine_config().enable_entity {
1790                            return Err(ExecError::KeyNotFound(Key::Hash(hash_addr)));
1791                        }
1792                        let key = Key::AddressableEntity(entity_addr);
1793                        let entity = self.context.read_gs_typed::<AddressableEntity>(&key)?;
1794                        let entity_named_keys = self
1795                            .context
1796                            .state()
1797                            .borrow_mut()
1798                            .get_named_keys(entity_addr)?;
1799                        let entry_points = self.context.get_casper_vm_v1_entry_point(key)?;
1800                        RuntimeFootprint::new_entity_footprint(
1801                            entity_addr,
1802                            entity,
1803                            entity_named_keys,
1804                            entry_points,
1805                        )
1806                    }
1807                };
1808
1809                (footprint, entity_addr, package)
1810            }
1811        };
1812
1813        if let EntityKind::Account(_) = footprint.entity_kind() {
1814            return Err(ExecError::InvalidContext);
1815        }
1816
1817        let entry_point = match footprint.entry_points().get(entry_point_name) {
1818            Some(entry_point) => entry_point,
1819            None => {
1820                match footprint.entity_kind() {
1821                    EntityKind::System(_) => {
1822                        self.charge_system_contract_call(
1823                            self.context()
1824                                .engine_config()
1825                                .system_config()
1826                                .no_such_entrypoint(),
1827                        )?;
1828                    }
1829                    EntityKind::Account(_) => {}
1830                    EntityKind::SmartContract(_) => {}
1831                }
1832                return Err(ExecError::NoSuchMethod(entry_point_name.to_owned()));
1833            }
1834        };
1835
1836        let entry_point_type = entry_point.entry_point_type();
1837
1838        if self.context.engine_config().enable_entity && entry_point_type.is_invalid_context() {
1839            return Err(ExecError::InvalidContext);
1840        }
1841
1842        // Get contract entry point hash
1843        // if public, allowed
1844        // if group, restricted to user group access
1845        // if template, not allowed
1846        self.validate_entry_point_access(&package, entry_point_name, entry_point.access())?;
1847        if self.context.engine_config().strict_argument_checking() {
1848            let entry_point_args_lookup: BTreeMap<&str, &Parameter> = entry_point
1849                .args()
1850                .iter()
1851                .map(|param| (param.name(), param))
1852                .collect();
1853
1854            let args_lookup: BTreeMap<&str, &NamedArg> = args
1855                .named_args()
1856                .map(|named_arg| (named_arg.name(), named_arg))
1857                .collect();
1858
1859            // variable ensure args type(s) match defined args of entry point
1860            for (param_name, param) in entry_point_args_lookup {
1861                if let Some(named_arg) = args_lookup.get(param_name) {
1862                    if param.cl_type() != named_arg.cl_value().cl_type() {
1863                        return Err(ExecError::type_mismatch(
1864                            param.cl_type().clone(),
1865                            named_arg.cl_value().cl_type().clone(),
1866                        ));
1867                    }
1868                } else if !param.cl_type().is_option() {
1869                    return Err(ExecError::MissingArgument {
1870                        name: param.name().to_string(),
1871                    });
1872                }
1873            }
1874        }
1875
1876        let entity_hash = AddressableEntityHash::new(entity_addr.value());
1877
1878        if !self
1879            .context
1880            .engine_config()
1881            .administrative_accounts()
1882            .is_empty()
1883            && !package.is_entity_enabled(&entity_addr)
1884            && !self
1885                .context
1886                .is_system_addressable_entity(&entity_addr.value())?
1887        {
1888            return Err(ExecError::DisabledEntity(entity_hash));
1889        }
1890
1891        // if session the caller's context
1892        // else the called contract's context
1893        let context_entity_key =
1894            self.get_context_key_for_contract_call(entity_addr, entry_point)?;
1895
1896        let context_entity_hash = context_entity_key
1897            .into_entity_hash_addr()
1898            .ok_or(ExecError::UnexpectedKeyVariant(context_entity_key))?;
1899
1900        let (should_attenuate_urefs, should_validate_urefs) = {
1901            // Determines if this call originated from the system account based on a first
1902            // element of the call stack.
1903            let is_system_account =
1904                self.context.get_initiator() == PublicKey::System.to_account_hash();
1905            // Is the immediate caller a system contract, such as when the auction calls the mint.
1906            let is_caller_system_contract =
1907                self.is_system_contract(self.context.access_rights().context_key())?;
1908            // Checks if the contract we're about to call is a system contract.
1909            let is_calling_system_contract = self.is_system_contract(context_entity_hash)?;
1910            // uref attenuation is necessary in the following circumstances:
1911            //   the originating account (aka the caller) is not the system account and
1912            //   the immediate caller is either a normal account or a normal contract and
1913            //   the target contract about to be called is a normal contract
1914            let should_attenuate_urefs =
1915                !is_system_account && !is_caller_system_contract && !is_calling_system_contract;
1916            let should_validate_urefs = !is_caller_system_contract || !is_calling_system_contract;
1917            (should_attenuate_urefs, should_validate_urefs)
1918        };
1919        let runtime_args = if should_attenuate_urefs {
1920            // Main purse URefs should be attenuated only when a non-system contract is executed by
1921            // a non-system account to avoid possible phishing attack scenarios.
1922            utils::attenuate_uref_in_args(
1923                args,
1924                self.context
1925                    .runtime_footprint()
1926                    .borrow()
1927                    .main_purse()
1928                    .expect("need purse for attenuation")
1929                    .addr(),
1930                AccessRights::WRITE,
1931            )?
1932        } else {
1933            args
1934        };
1935
1936        let extended_access_rights = {
1937            let mut all_urefs = vec![];
1938            for arg in runtime_args.to_values() {
1939                let urefs = utils::extract_urefs(arg)?;
1940                if should_validate_urefs {
1941                    for uref in &urefs {
1942                        self.context.validate_uref(uref)?;
1943                    }
1944                }
1945                all_urefs.extend(urefs);
1946            }
1947            all_urefs
1948        };
1949
1950        let (mut named_keys, access_rights) = match entry_point_type {
1951            EntryPointType::Caller => {
1952                let mut access_rights = self
1953                    .context
1954                    .runtime_footprint()
1955                    .borrow()
1956                    .extract_access_rights(context_entity_hash);
1957                access_rights.extend(&extended_access_rights);
1958
1959                let named_keys = self
1960                    .context
1961                    .runtime_footprint()
1962                    .borrow()
1963                    .named_keys()
1964                    .clone();
1965
1966                (named_keys, access_rights)
1967            }
1968            EntryPointType::Called | EntryPointType::Factory => {
1969                let mut access_rights = footprint.extract_access_rights(entity_hash.value());
1970                access_rights.extend(&extended_access_rights);
1971                let named_keys = footprint.named_keys().clone();
1972                (named_keys, access_rights)
1973            }
1974        };
1975
1976        let stack = {
1977            let mut stack = self.try_get_stack()?.clone();
1978
1979            let package_hash = match footprint.package_hash() {
1980                Some(hash) => PackageHash::new(hash),
1981                None => {
1982                    return Err(ExecError::UnexpectedStoredValueVariant);
1983                }
1984            };
1985
1986            let caller = if self.context.engine_config().enable_entity {
1987                Caller::entity(package_hash, entity_addr)
1988            } else {
1989                Caller::smart_contract(
1990                    ContractPackageHash::new(package_hash.value()),
1991                    ContractHash::new(entity_addr.value()),
1992                )
1993            };
1994
1995            stack.push(caller)?;
1996
1997            stack
1998        };
1999
2000        if let EntityKind::System(system_contract_type) = footprint.entity_kind() {
2001            let entry_point_name = entry_point.name();
2002
2003            match system_contract_type {
2004                SystemEntityType::Mint => {
2005                    return self.call_host_mint(
2006                        entry_point_name,
2007                        &runtime_args,
2008                        access_rights,
2009                        stack,
2010                    );
2011                }
2012                SystemEntityType::HandlePayment => {
2013                    return self.call_host_handle_payment(
2014                        entry_point_name,
2015                        &runtime_args,
2016                        access_rights,
2017                        stack,
2018                    );
2019                }
2020                SystemEntityType::Auction => {
2021                    return self.call_host_auction(
2022                        entry_point_name,
2023                        &runtime_args,
2024                        access_rights,
2025                        stack,
2026                    );
2027                }
2028                // Not callable
2029                SystemEntityType::StandardPayment => {}
2030            }
2031        }
2032
2033        let module: Module = {
2034            let byte_code_addr = footprint.wasm_hash().ok_or(ExecError::InvalidContext)?;
2035
2036            let byte_code_key = match footprint.entity_kind() {
2037                EntityKind::System(_) | EntityKind::Account(_) => {
2038                    Key::ByteCode(ByteCodeAddr::Empty)
2039                }
2040                EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1) => {
2041                    if self.context.engine_config().enable_entity {
2042                        Key::ByteCode(ByteCodeAddr::new_wasm_addr(byte_code_addr))
2043                    } else {
2044                        Key::Hash(byte_code_addr)
2045                    }
2046                }
2047                EntityKind::SmartContract(runtime @ ContractRuntimeTag::VmCasperV2) => {
2048                    return Err(ExecError::IncompatibleRuntime(runtime));
2049                }
2050            };
2051
2052            let byte_code: ByteCode = match self.context.read_gs(&byte_code_key)? {
2053                Some(StoredValue::ContractWasm(wasm)) => {
2054                    ByteCode::new(ByteCodeKind::V1CasperWasm, wasm.take_bytes())
2055                }
2056                Some(StoredValue::ByteCode(byte_code)) => byte_code,
2057                Some(_) => {
2058                    return Err(ExecError::InvalidByteCode(ByteCodeHash::new(
2059                        byte_code_addr,
2060                    )))
2061                }
2062                None => return Err(ExecError::KeyNotFound(byte_code_key)),
2063            };
2064
2065            casper_wasm::deserialize_buffer(byte_code.bytes())?
2066        };
2067
2068        let context = self.context.new_from_self(
2069            context_entity_key,
2070            entry_point.entry_point_type(),
2071            &mut named_keys,
2072            access_rights,
2073            runtime_args,
2074        );
2075
2076        let (instance, memory) = utils::instance_and_memory(
2077            module.clone(),
2078            self.context.protocol_version(),
2079            self.context.engine_config(),
2080        )?;
2081        let runtime = &mut Runtime::new_invocation_runtime(self, context, module, memory, stack);
2082        let result = instance.invoke_export(entry_point.name(), &[], runtime);
2083        // The `runtime`'s context was initialized with our counter from before the call and any gas
2084        // charged by the sub-call was added to its counter - so let's copy the correct value of the
2085        // counter from there to our counter. Do the same for the message cost tracking.
2086        self.context.set_gas_counter(runtime.context.gas_counter());
2087        self.context
2088            .set_emit_message_cost(runtime.context.emit_message_cost());
2089        let transfers = self.context.transfers_mut();
2090        runtime.context.transfers().clone_into(transfers);
2091
2092        match result {
2093            Ok(_) => {
2094                // If `Ok` and the `host_buffer` is `None`, the contract's execution succeeded but
2095                // did not explicitly call `runtime::ret()`.  Treat as though the
2096                // execution returned the unit type `()` as per Rust functions which
2097                // don't specify a return value.
2098                if self.context.entry_point_type() == EntryPointType::Caller
2099                    && runtime.context.entry_point_type() == EntryPointType::Caller
2100                {
2101                    // Overwrites parent's named keys with child's new named key but only when
2102                    // running session code.
2103                    *self.context.named_keys_mut() = runtime.context.named_keys().clone();
2104                }
2105                self.context
2106                    .set_remaining_spending_limit(runtime.context.remaining_spending_limit());
2107                Ok(runtime.take_host_buffer().unwrap_or(CLValue::from_t(())?))
2108            }
2109            Err(error) => {
2110                #[cfg(feature = "test-support")]
2111                dump_runtime_stack_info(
2112                    instance,
2113                    self.context
2114                        .engine_config()
2115                        .wasm_config()
2116                        .v1()
2117                        .max_stack_height(),
2118                );
2119                if let Some(host_error) = error.as_host_error() {
2120                    // If the "error" was in fact a trap caused by calling `ret` then this is normal
2121                    // operation and we should return the value captured in the Runtime result
2122                    // field.
2123                    let downcasted_error = host_error.downcast_ref::<ExecError>();
2124                    return match downcasted_error {
2125                        Some(ExecError::Ret(ref ret_urefs)) => {
2126                            // Insert extra urefs returned from call.
2127                            // Those returned URef's are guaranteed to be valid as they were already
2128                            // validated in the `ret` call inside context we ret from.
2129                            self.context.access_rights_extend(ret_urefs);
2130                            if self.context.entry_point_type() == EntryPointType::Caller
2131                                && runtime.context.entry_point_type() == EntryPointType::Caller
2132                            {
2133                                // Overwrites parent's named keys with child's new named key but
2134                                // only when running session code.
2135                                *self.context.named_keys_mut() =
2136                                    runtime.context.named_keys().clone();
2137                            }
2138                            // Stored contracts are expected to always call a `ret` function,
2139                            // otherwise it's an error.
2140                            runtime
2141                                .take_host_buffer()
2142                                .ok_or(ExecError::ExpectedReturnValue)
2143                        }
2144                        Some(error) => Err(error.clone()),
2145                        None => Err(ExecError::Interpreter(host_error.to_string())),
2146                    };
2147                }
2148                Err(ExecError::Interpreter(error.into()))
2149            }
2150        }
2151    }
2152
2153    fn call_contract_host_buffer(
2154        &mut self,
2155        contract_hash: AddressableEntityHash,
2156        entry_point_name: &str,
2157        args_bytes: &[u8],
2158        result_size_ptr: u32,
2159    ) -> Result<Result<(), ApiError>, ExecError> {
2160        // Exit early if the host buffer is already occupied
2161        if let Err(err) = self.check_host_buffer() {
2162            return Ok(Err(err));
2163        }
2164        let args: RuntimeArgs = bytesrepr::deserialize_from_slice(args_bytes)?;
2165
2166        if let Some(payment_purse) = self.context.maybe_payment_purse() {
2167            for named_arg in args.named_args() {
2168                if utils::extract_urefs(named_arg.cl_value())?
2169                    .into_iter()
2170                    .any(|uref| uref.remove_access_rights() == payment_purse.remove_access_rights())
2171                {
2172                    warn!("attempt to call_contract with payment purse");
2173
2174                    return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
2175                        handle_payment::Error::AttemptToPersistPaymentPurse as u8,
2176                    ))));
2177                }
2178            }
2179        }
2180
2181        let result = self.call_contract(contract_hash, entry_point_name, args)?;
2182        self.manage_call_contract_host_buffer(result_size_ptr, result)
2183    }
2184
2185    fn call_versioned_contract_host_buffer(
2186        &mut self,
2187        contract_package_hash: PackageHash,
2188        contract_version: Option<EntityVersion>,
2189        entry_point_name: String,
2190        args_bytes: &[u8],
2191        result_size_ptr: u32,
2192    ) -> Result<Result<(), ApiError>, ExecError> {
2193        // Exit early if the host buffer is already occupied
2194        if let Err(err) = self.check_host_buffer() {
2195            return Ok(Err(err));
2196        }
2197        let args: RuntimeArgs = bytesrepr::deserialize_from_slice(args_bytes)?;
2198
2199        if let Some(payment_purse) = self.context.maybe_payment_purse() {
2200            for named_arg in args.named_args() {
2201                if utils::extract_urefs(named_arg.cl_value())?
2202                    .into_iter()
2203                    .any(|uref| uref.remove_access_rights() == payment_purse.remove_access_rights())
2204                {
2205                    warn!("attempt to call_versioned_contract with payment purse");
2206
2207                    return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
2208                        handle_payment::Error::AttemptToPersistPaymentPurse as u8,
2209                    ))));
2210                }
2211            }
2212        }
2213
2214        let result = self.call_versioned_contract(
2215            contract_package_hash,
2216            contract_version,
2217            entry_point_name,
2218            args,
2219        )?;
2220        self.manage_call_contract_host_buffer(result_size_ptr, result)
2221    }
2222
2223    fn call_package_version_host_buffer(
2224        &mut self,
2225        contract_package_hash: PackageHash,
2226        major_version: u32,
2227        contract_version: EntityVersion,
2228        entry_point_name: String,
2229        args_bytes: &[u8],
2230        result_size_ptr: u32,
2231    ) -> Result<Result<(), ApiError>, ExecError> {
2232        // Exit early if the host buffer is already occupied
2233        if let Err(err) = self.check_host_buffer() {
2234            return Ok(Err(err));
2235        }
2236        let args: RuntimeArgs = bytesrepr::deserialize_from_slice(args_bytes)?;
2237
2238        if let Some(payment_purse) = self.context.maybe_payment_purse() {
2239            for named_arg in args.named_args() {
2240                if utils::extract_urefs(named_arg.cl_value())?
2241                    .into_iter()
2242                    .any(|uref| uref.remove_access_rights() == payment_purse.remove_access_rights())
2243                {
2244                    warn!("attempt to call_versioned_contract with payment purse");
2245
2246                    return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
2247                        handle_payment::Error::AttemptToPersistPaymentPurse as u8,
2248                    ))));
2249                }
2250            }
2251        }
2252
2253        let result = self.call_package_version(
2254            contract_package_hash,
2255            major_version,
2256            contract_version,
2257            entry_point_name,
2258            args,
2259        )?;
2260        self.manage_call_contract_host_buffer(result_size_ptr, result)
2261    }
2262
2263    fn check_host_buffer(&mut self) -> Result<(), ApiError> {
2264        if !self.can_write_to_host_buffer() {
2265            Err(ApiError::HostBufferFull)
2266        } else {
2267            Ok(())
2268        }
2269    }
2270
2271    fn manage_call_contract_host_buffer(
2272        &mut self,
2273        result_size_ptr: u32,
2274        result: CLValue,
2275    ) -> Result<Result<(), ApiError>, ExecError> {
2276        let result_size: u32 = match result.inner_bytes().len().try_into() {
2277            Ok(value) => value,
2278            Err(_) => return Ok(Err(ApiError::OutOfMemory)),
2279        };
2280
2281        // leave the host buffer set to `None` if there's nothing to write there
2282        if result_size != 0 {
2283            if let Err(error) = self.write_host_buffer(result) {
2284                return Ok(Err(error));
2285            }
2286        }
2287
2288        let result_size_bytes = result_size.to_le_bytes(); // Wasm is little-endian
2289        if let Err(error) = self
2290            .try_get_memory()?
2291            .set(result_size_ptr, &result_size_bytes)
2292        {
2293            return Err(ExecError::Interpreter(error.into()));
2294        }
2295
2296        Ok(Ok(()))
2297    }
2298
2299    fn load_named_keys(
2300        &mut self,
2301        total_keys_ptr: u32,
2302        result_size_ptr: u32,
2303    ) -> Result<Result<(), ApiError>, Trap> {
2304        if !self.can_write_to_host_buffer() {
2305            // Exit early if the host buffer is already occupied
2306            return Ok(Err(ApiError::HostBufferFull));
2307        }
2308
2309        let total_keys: u32 = match self.context.named_keys().len().try_into() {
2310            Ok(value) => value,
2311            Err(_) => return Ok(Err(ApiError::OutOfMemory)),
2312        };
2313
2314        let total_keys_bytes = total_keys.to_le_bytes();
2315        if let Err(error) = self
2316            .try_get_memory()?
2317            .set(total_keys_ptr, &total_keys_bytes)
2318        {
2319            return Err(ExecError::Interpreter(error.into()).into());
2320        }
2321
2322        if total_keys == 0 {
2323            // No need to do anything else, we leave host buffer empty.
2324            return Ok(Ok(()));
2325        }
2326
2327        let named_keys =
2328            CLValue::from_t(self.context.named_keys().clone()).map_err(ExecError::CLValue)?;
2329
2330        let length: u32 = match named_keys.inner_bytes().len().try_into() {
2331            Ok(value) => value,
2332            Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
2333        };
2334
2335        if let Err(error) = self.write_host_buffer(named_keys) {
2336            return Ok(Err(error));
2337        }
2338
2339        let length_bytes = length.to_le_bytes();
2340        if let Err(error) = self.try_get_memory()?.set(result_size_ptr, &length_bytes) {
2341            return Err(ExecError::Interpreter(error.into()).into());
2342        }
2343
2344        Ok(Ok(()))
2345    }
2346
2347    fn create_contract_package(
2348        &mut self,
2349        is_locked: PackageStatus,
2350    ) -> Result<(ContractPackage, URef), ExecError> {
2351        let access_key = self.context.new_unit_uref()?;
2352        let package_status = match is_locked {
2353            PackageStatus::Locked => ContractPackageStatus::Locked,
2354            PackageStatus::Unlocked => ContractPackageStatus::Unlocked,
2355        };
2356
2357        let contract_package = ContractPackage::new(
2358            access_key,
2359            ContractVersions::default(),
2360            DisabledVersions::default(),
2361            Groups::default(),
2362            package_status,
2363        );
2364
2365        Ok((contract_package, access_key))
2366    }
2367
2368    fn create_package(&mut self, is_locked: PackageStatus) -> Result<(Package, URef), ExecError> {
2369        let access_key = self.context.new_unit_uref()?;
2370        let contract_package = Package::new(
2371            EntityVersions::new(),
2372            BTreeSet::new(),
2373            Groups::new(),
2374            is_locked,
2375        );
2376
2377        Ok((contract_package, access_key))
2378    }
2379
2380    fn create_contract_package_at_hash(
2381        &mut self,
2382        lock_status: PackageStatus,
2383    ) -> Result<([u8; 32], [u8; 32]), ExecError> {
2384        let addr = self.context.new_hash_address()?;
2385        let access_key = if self.context.engine_config().enable_entity {
2386            let (package, access_key) = self.create_package(lock_status)?;
2387            self.context
2388                .metered_write_gs_unsafe(Key::SmartContract(addr), package)?;
2389            access_key
2390        } else {
2391            let (package, access_key) = self.create_contract_package(lock_status)?;
2392            self.context
2393                .metered_write_gs_unsafe(Key::Hash(addr), package)?;
2394            access_key
2395        };
2396        Ok((addr, access_key.addr()))
2397    }
2398
2399    fn create_contract_user_group_by_contract_package(
2400        &mut self,
2401        contract_package_hash: PackageHash,
2402        label: String,
2403        num_new_urefs: u32,
2404        mut existing_urefs: BTreeSet<URef>,
2405        output_size_ptr: u32,
2406    ) -> Result<Result<(), ApiError>, ExecError> {
2407        let mut contract_package: ContractPackage = self
2408            .context
2409            .get_validated_contract_package(contract_package_hash.value())?;
2410
2411        let groups = contract_package.groups_mut();
2412        let new_group = Group::new(label);
2413
2414        // Ensure group does not already exist
2415        if groups.contains(&new_group) {
2416            return Ok(Err(addressable_entity::Error::GroupAlreadyExists.into()));
2417        }
2418
2419        // Ensure there are not too many groups
2420        if groups.len() >= (addressable_entity::MAX_GROUPS as usize) {
2421            return Ok(Err(addressable_entity::Error::MaxGroupsExceeded.into()));
2422        }
2423
2424        // Ensure there are not too many urefs
2425        let total_urefs: usize =
2426            groups.total_urefs() + (num_new_urefs as usize) + existing_urefs.len();
2427        if total_urefs > addressable_entity::MAX_TOTAL_UREFS {
2428            let err = addressable_entity::Error::MaxTotalURefsExceeded;
2429            return Ok(Err(ApiError::ContractHeader(err as u8)));
2430        }
2431
2432        // Proceed with creating user group
2433        let mut new_urefs = Vec::with_capacity(num_new_urefs as usize);
2434        for _ in 0..num_new_urefs {
2435            let u = self.context.new_unit_uref()?;
2436            new_urefs.push(u);
2437        }
2438
2439        for u in new_urefs.iter().cloned() {
2440            existing_urefs.insert(u);
2441        }
2442        groups.insert(new_group, existing_urefs);
2443
2444        // check we can write to the host buffer
2445        if let Err(err) = self.check_host_buffer() {
2446            return Ok(Err(err));
2447        }
2448        // create CLValue for return value
2449        let new_urefs_value = CLValue::from_t(new_urefs)?;
2450        let value_size = new_urefs_value.inner_bytes().len();
2451        // write return value to buffer
2452        if let Err(err) = self.write_host_buffer(new_urefs_value) {
2453            return Ok(Err(err));
2454        }
2455        // Write return value size to output location
2456        let output_size_bytes = value_size.to_le_bytes(); // Wasm is little-endian
2457        if let Err(error) = self
2458            .try_get_memory()?
2459            .set(output_size_ptr, &output_size_bytes)
2460        {
2461            return Err(ExecError::Interpreter(error.into()));
2462        }
2463
2464        // Write updated package to the global state
2465        self.context.metered_write_gs_unsafe(
2466            ContractPackageHash::new(contract_package_hash.value()),
2467            contract_package,
2468        )?;
2469
2470        Ok(Ok(()))
2471    }
2472
2473    fn create_contract_user_group(
2474        &mut self,
2475        contract_package_hash: PackageHash,
2476        label: String,
2477        num_new_urefs: u32,
2478        mut existing_urefs: BTreeSet<URef>,
2479        output_size_ptr: u32,
2480    ) -> Result<Result<(), ApiError>, ExecError> {
2481        if !self.context.engine_config().enable_entity {
2482            return self.create_contract_user_group_by_contract_package(
2483                contract_package_hash,
2484                label,
2485                num_new_urefs,
2486                existing_urefs,
2487                output_size_ptr,
2488            );
2489        };
2490
2491        let mut contract_package: Package =
2492            self.context.get_validated_package(contract_package_hash)?;
2493
2494        let groups = contract_package.groups_mut();
2495        let new_group = Group::new(label);
2496
2497        // Ensure group does not already exist
2498        if groups.contains(&new_group) {
2499            return Ok(Err(addressable_entity::Error::GroupAlreadyExists.into()));
2500        }
2501
2502        // Ensure there are not too many groups
2503        if groups.len() >= (addressable_entity::MAX_GROUPS as usize) {
2504            return Ok(Err(addressable_entity::Error::MaxGroupsExceeded.into()));
2505        }
2506
2507        // Ensure there are not too many urefs
2508        let total_urefs: usize =
2509            groups.total_urefs() + (num_new_urefs as usize) + existing_urefs.len();
2510        if total_urefs > addressable_entity::MAX_TOTAL_UREFS {
2511            let err = addressable_entity::Error::MaxTotalURefsExceeded;
2512            return Ok(Err(ApiError::ContractHeader(err as u8)));
2513        }
2514
2515        // Proceed with creating user group
2516        let mut new_urefs = Vec::with_capacity(num_new_urefs as usize);
2517        for _ in 0..num_new_urefs {
2518            let u = self.context.new_unit_uref()?;
2519            new_urefs.push(u);
2520        }
2521
2522        for u in new_urefs.iter().cloned() {
2523            existing_urefs.insert(u);
2524        }
2525        groups.insert(new_group, existing_urefs);
2526
2527        // check we can write to the host buffer
2528        if let Err(err) = self.check_host_buffer() {
2529            return Ok(Err(err));
2530        }
2531        // create CLValue for return value
2532        let new_urefs_value = CLValue::from_t(new_urefs)?;
2533        let value_size = new_urefs_value.inner_bytes().len();
2534        // write return value to buffer
2535        if let Err(err) = self.write_host_buffer(new_urefs_value) {
2536            return Ok(Err(err));
2537        }
2538        // Write return value size to output location
2539        let output_size_bytes = value_size.to_le_bytes(); // Wasm is little-endian
2540        if let Err(error) = self
2541            .try_get_memory()?
2542            .set(output_size_ptr, &output_size_bytes)
2543        {
2544            return Err(ExecError::Interpreter(error.into()));
2545        }
2546
2547        // Write updated package to the global state
2548        self.context
2549            .metered_write_gs_unsafe(contract_package_hash, contract_package)?;
2550
2551        Ok(Ok(()))
2552    }
2553
2554    #[allow(clippy::too_many_arguments)]
2555    fn add_contract_version(
2556        &mut self,
2557        package_hash: PackageHash,
2558        version_ptr: u32,
2559        entry_points: EntryPoints,
2560        named_keys: NamedKeys,
2561        message_topics: BTreeMap<String, MessageTopicOperation>,
2562        output_ptr: u32,
2563    ) -> Result<Result<(), ApiError>, ExecError> {
2564        if self.context.engine_config().enable_entity {
2565            self.add_contract_version_by_package(
2566                package_hash,
2567                version_ptr,
2568                entry_points,
2569                named_keys,
2570                message_topics,
2571                output_ptr,
2572            )
2573        } else {
2574            self.add_contract_version_by_contract_package(
2575                package_hash.value(),
2576                version_ptr,
2577                entry_points,
2578                named_keys,
2579                message_topics,
2580                output_ptr,
2581            )
2582        }
2583    }
2584
2585    #[allow(clippy::too_many_arguments)]
2586    fn add_contract_version_by_contract_package(
2587        &mut self,
2588        contract_package_hash: HashAddr,
2589        version_ptr: u32,
2590        entry_points: EntryPoints,
2591        mut named_keys: NamedKeys,
2592        message_topics: BTreeMap<String, MessageTopicOperation>,
2593        output_ptr: u32,
2594    ) -> Result<Result<(), ApiError>, ExecError> {
2595        if !self.context.install_upgrade_allowed() {
2596            // NOTE: This is not a permission check on the caller,
2597            // it is enforcing the rule that only legacy standard deploys (which are grandfathered)
2598            // and install / upgrade transactions are allowed to call this method
2599            return Ok(Err(ApiError::NotAllowedToAddContractVersion));
2600        }
2601
2602        // if entry_points.contains_stored_session() {
2603        //     // As of 2.0 we do not allow stored session logic to be
2604        //     // installed or upgraded. Pre-existing stored
2605        //     // session logic is still callable.
2606        //     return Err(ExecError::InvalidEntryPointType);
2607        // }
2608
2609        self.context
2610            .validate_key(&Key::Hash(contract_package_hash))?;
2611
2612        let mut contract_package: ContractPackage = self
2613            .context
2614            .get_validated_contract_package(contract_package_hash)?;
2615
2616        let version = contract_package.current_contract_version();
2617
2618        // Return an error if the contract is locked and has some version associated with it.
2619        if contract_package.is_locked() && version.is_some() {
2620            return Err(ExecError::LockedEntity(PackageHash::new(
2621                contract_package_hash,
2622            )));
2623        }
2624
2625        for (_, key) in named_keys.iter() {
2626            self.context.validate_key(key)?
2627        }
2628
2629        let contract_wasm_hash = self.context.new_hash_address()?;
2630        let contract_wasm = {
2631            let module_bytes = self.get_module_from_entry_points(&entry_points)?;
2632            ContractWasm::new(module_bytes)
2633        };
2634
2635        let contract_hash_addr: HashAddr = self.context.new_hash_address()?;
2636        let contract_entity_addr = EntityAddr::SmartContract(contract_hash_addr);
2637
2638        let protocol_version = self.context.protocol_version();
2639        let major = protocol_version.value().major;
2640
2641        let maybe_previous_hash =
2642            if let Some(previous_contract_hash) = contract_package.current_contract_hash() {
2643                let previous_contract: Contract =
2644                    self.context.read_gs_typed(&previous_contract_hash.into())?;
2645
2646                let previous_named_keys = previous_contract.take_named_keys();
2647                named_keys.append(previous_named_keys);
2648                Some(EntityAddr::SmartContract(previous_contract_hash.value()))
2649            } else {
2650                None
2651            };
2652
2653        if let Err(err) = self.carry_forward_message_topics(
2654            maybe_previous_hash,
2655            contract_entity_addr,
2656            message_topics,
2657        )? {
2658            return Ok(Err(err));
2659        };
2660
2661        let contract_package_hash = ContractPackageHash::new(contract_package_hash);
2662        let contract = Contract::new(
2663            contract_package_hash,
2664            contract_wasm_hash.into(),
2665            named_keys,
2666            entry_points.into(),
2667            protocol_version,
2668        );
2669
2670        let insert_contract_result =
2671            contract_package.insert_contract_version(major, contract_hash_addr.into());
2672
2673        self.context
2674            .metered_write_gs_unsafe(Key::Hash(contract_wasm_hash), contract_wasm)?;
2675        self.context
2676            .metered_write_gs_unsafe(Key::Hash(contract_hash_addr), contract)?;
2677        self.context
2678            .metered_write_gs_unsafe(Key::Hash(contract_package_hash.value()), contract_package)?;
2679
2680        // set return values to buffer
2681        {
2682            let hash_bytes = match contract_hash_addr.to_bytes() {
2683                Ok(bytes) => bytes,
2684                Err(error) => return Ok(Err(error.into())),
2685            };
2686
2687            // Set serialized hash bytes into the output buffer
2688            if let Err(error) = self.try_get_memory()?.set(output_ptr, &hash_bytes) {
2689                return Err(ExecError::Interpreter(error.into()));
2690            }
2691
2692            // Set version into VM shared memory
2693            let version_value: u32 = insert_contract_result.contract_version();
2694            let version_bytes = version_value.to_le_bytes();
2695            if let Err(error) = self.try_get_memory()?.set(version_ptr, &version_bytes) {
2696                return Err(ExecError::Interpreter(error.into()));
2697            }
2698        }
2699
2700        Ok(Ok(()))
2701    }
2702
2703    #[allow(clippy::too_many_arguments)]
2704    fn add_contract_version_by_package(
2705        &mut self,
2706        package_hash: PackageHash,
2707        version_ptr: u32,
2708        entry_points: EntryPoints,
2709        mut named_keys: NamedKeys,
2710        message_topics: BTreeMap<String, MessageTopicOperation>,
2711        output_ptr: u32,
2712    ) -> Result<Result<(), ApiError>, ExecError> {
2713        if !self.context.install_upgrade_allowed() {
2714            // NOTE: This is not a permission check on the caller,
2715            // it is enforcing the rule that only legacy standard deploys (which are grandfathered)
2716            // and install / upgrade transactions are allowed to call this method
2717            return Ok(Err(ApiError::NotAllowedToAddContractVersion));
2718        }
2719
2720        if entry_points.contains_stored_session() {
2721            // As of 2.0 we do not allow stored session logic to be
2722            // installed or upgraded. Pre-existing stored
2723            // session logic is still callable.
2724            return Err(ExecError::InvalidEntryPointType);
2725        }
2726
2727        let mut package = self.context.get_package(package_hash.value())?;
2728
2729        // Return an error if the contract is locked and has some version associated with it.
2730        if package.is_locked() {
2731            return Err(ExecError::LockedEntity(package_hash));
2732        }
2733
2734        let (
2735            main_purse,
2736            previous_named_keys,
2737            action_thresholds,
2738            associated_keys,
2739            previous_hash_addr,
2740        ) = self.new_version_entity_parts(&package)?;
2741
2742        // We generate the byte code hash because a byte code record
2743        // must exist for a contract record to exist.
2744        let byte_code_hash = self.context.new_hash_address()?;
2745
2746        let hash_addr = self.context.new_hash_address()?;
2747        let entity_addr = EntityAddr::SmartContract(hash_addr);
2748
2749        if let Err(err) =
2750            self.carry_forward_message_topics(previous_hash_addr, entity_addr, message_topics)?
2751        {
2752            return Ok(Err(err));
2753        };
2754
2755        let protocol_version = self.context.protocol_version();
2756
2757        let insert_entity_version_result =
2758            package.insert_entity_version(protocol_version.value().major, entity_addr);
2759
2760        let byte_code = {
2761            let module_bytes = self.get_module_from_entry_points(&entry_points)?;
2762            ByteCode::new(ByteCodeKind::V1CasperWasm, module_bytes)
2763        };
2764
2765        self.context.metered_write_gs_unsafe(
2766            Key::ByteCode(ByteCodeAddr::new_wasm_addr(byte_code_hash)),
2767            byte_code,
2768        )?;
2769
2770        let entity_addr = EntityAddr::new_smart_contract(hash_addr);
2771
2772        {
2773            // DO NOT EXTRACT INTO SEPARATE FUNCTION.
2774            for (_, key) in named_keys.iter() {
2775                // Validate all the imputed named keys
2776                // against the installers permissions
2777                self.context.validate_key(key)?;
2778            }
2779            // Carry forward named keys from previous version
2780            // Grant all the imputed named keys + previous named keys.
2781            named_keys.append(previous_named_keys);
2782            for (name, key) in named_keys.iter() {
2783                let named_key_value =
2784                    StoredValue::NamedKey(NamedKeyValue::from_concrete_values(*key, name.clone())?);
2785                let named_key_addr = NamedKeyAddr::new_from_string(entity_addr, name.clone())?;
2786                self.context
2787                    .metered_write_gs_unsafe(Key::NamedKey(named_key_addr), named_key_value)?;
2788            }
2789            self.context.write_entry_points(entity_addr, entry_points)?;
2790        }
2791
2792        let entity = AddressableEntity::new(
2793            package_hash,
2794            byte_code_hash.into(),
2795            protocol_version,
2796            main_purse,
2797            associated_keys,
2798            action_thresholds,
2799            EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1),
2800        );
2801        let entity_key = Key::AddressableEntity(entity_addr);
2802        self.context.metered_write_gs_unsafe(entity_key, entity)?;
2803        self.context
2804            .metered_write_gs_unsafe(package_hash, package)?;
2805
2806        // set return values to buffer
2807        {
2808            let hash_bytes = match hash_addr.to_bytes() {
2809                Ok(bytes) => bytes,
2810                Err(error) => return Ok(Err(error.into())),
2811            };
2812
2813            // Set serialized hash bytes into the output buffer
2814            if let Err(error) = self.try_get_memory()?.set(output_ptr, &hash_bytes) {
2815                return Err(ExecError::Interpreter(error.into()));
2816            }
2817
2818            // Set version into VM shared memory
2819            let version_value: u32 = insert_entity_version_result.entity_version();
2820            let version_bytes = version_value.to_le_bytes();
2821            if let Err(error) = self.try_get_memory()?.set(version_ptr, &version_bytes) {
2822                return Err(ExecError::Interpreter(error.into()));
2823            }
2824        }
2825
2826        Ok(Ok(()))
2827    }
2828
2829    fn carry_forward_message_topics(
2830        &mut self,
2831        previous_entity_addr: Option<EntityAddr>,
2832        entity_addr: EntityAddr,
2833        message_topics: BTreeMap<String, MessageTopicOperation>,
2834    ) -> Result<Result<(), ApiError>, ExecError> {
2835        let mut previous_message_topics = match previous_entity_addr {
2836            Some(previous_hash) => self.context.get_message_topics(previous_hash)?,
2837            None => MessageTopics::default(),
2838        };
2839
2840        let max_topics_per_contract = self
2841            .context
2842            .engine_config()
2843            .wasm_config()
2844            .messages_limits()
2845            .max_topics_per_contract();
2846
2847        let topics_to_add = message_topics
2848            .iter()
2849            .filter(|(_, operation)| match operation {
2850                MessageTopicOperation::Add => true,
2851            });
2852        // Check if registering the new topics would exceed the limit per contract
2853        if previous_message_topics.len() + topics_to_add.clone().count()
2854            > max_topics_per_contract as usize
2855        {
2856            return Ok(Err(ApiError::from(MessageTopicError::MaxTopicsExceeded)));
2857        }
2858
2859        // Extend the previous topics with the newly added ones.
2860        for (new_topic, _) in topics_to_add {
2861            let topic_name_hash = cryptography::blake2b(new_topic.as_bytes()).into();
2862            if let Err(e) = previous_message_topics.add_topic(new_topic.as_str(), topic_name_hash) {
2863                return Ok(Err(e.into()));
2864            }
2865        }
2866
2867        for (topic_name, topic_hash) in previous_message_topics.iter() {
2868            let topic_key = Key::message_topic(entity_addr, *topic_hash);
2869            let block_time = self.context.get_block_info().block_time();
2870            let summary = StoredValue::MessageTopic(MessageTopicSummary::new(
2871                0,
2872                block_time,
2873                topic_name.clone(),
2874            ));
2875            self.context.metered_write_gs_unsafe(topic_key, summary)?;
2876        }
2877        Ok(Ok(()))
2878    }
2879
2880    fn new_version_entity_parts(
2881        &mut self,
2882        package: &Package,
2883    ) -> Result<
2884        (
2885            URef,
2886            NamedKeys,
2887            ActionThresholds,
2888            AssociatedKeys,
2889            Option<EntityAddr>,
2890        ),
2891        ExecError,
2892    > {
2893        if let Some(previous_entity_hash) = package.current_entity_hash() {
2894            let previous_entity_key = Key::AddressableEntity(previous_entity_hash);
2895            let (mut previous_entity, requires_purse_creation) =
2896                self.context.get_contract_entity(previous_entity_key)?;
2897
2898            let action_thresholds = previous_entity.action_thresholds().clone();
2899
2900            let associated_keys = previous_entity.associated_keys().clone();
2901            // STEP 1: LOAD THE CONTRACT AND CHECK IF CALLER IS IN ASSOCIATED KEYS WITH ENOUGH
2902            // WEIGHT     TO UPGRADE (COMPARE TO THE ACTION THRESHOLD FOR UPGRADE
2903            // ACTION). STEP 2: IF CALLER IS NOT IN CONTRACTS ASSOCIATED KEYS
2904            //    CHECK FOR LEGACY UREFADDR UNDER KEY:HASH(PACKAGEADDR)
2905            //    IF FOUND,
2906            //      call validate_uref(that uref)
2907            //    IF VALID,
2908            //      create the new contract version carrying forward previous state including
2909            // associated keys      BUT add the caller to the associated keys with
2910            // weight == to the action threshold for upgrade ELSE, error
2911            if !previous_entity.can_upgrade_with(self.context.authorization_keys()) {
2912                // Check if the calling entity must be grandfathered into the new
2913                // addressable entity format
2914                let account_hash = self.context.get_initiator();
2915
2916                let access_key = match self
2917                    .context
2918                    .read_gs(&Key::Hash(previous_entity.package_hash().value()))?
2919                {
2920                    Some(StoredValue::ContractPackage(contract_package)) => {
2921                        contract_package.access_key()
2922                    }
2923                    Some(StoredValue::CLValue(cl_value)) => {
2924                        let (_key, uref) = cl_value
2925                            .into_t::<(Key, URef)>()
2926                            .map_err(ExecError::CLValue)?;
2927                        uref
2928                    }
2929                    Some(_other) => return Err(ExecError::UnexpectedStoredValueVariant),
2930                    None => {
2931                        return Err(ExecError::UpgradeAuthorizationFailure);
2932                    }
2933                };
2934
2935                let has_access = self.context.validate_uref(&access_key).is_ok();
2936
2937                if has_access && !associated_keys.contains_key(&account_hash) {
2938                    previous_entity.add_associated_key(
2939                        account_hash,
2940                        *action_thresholds.upgrade_management(),
2941                    )?;
2942                } else {
2943                    return Err(ExecError::UpgradeAuthorizationFailure);
2944                }
2945            }
2946
2947            let main_purse = if requires_purse_creation {
2948                self.create_purse()?
2949            } else {
2950                previous_entity.main_purse()
2951            };
2952
2953            let associated_keys = previous_entity.associated_keys().clone();
2954
2955            let previous_named_keys = self.context.get_named_keys(previous_entity_key)?;
2956
2957            return Ok((
2958                main_purse,
2959                previous_named_keys,
2960                action_thresholds,
2961                associated_keys,
2962                Some(previous_entity_hash),
2963            ));
2964        }
2965
2966        Ok((
2967            self.create_purse()?,
2968            NamedKeys::new(),
2969            ActionThresholds::default(),
2970            AssociatedKeys::new(self.context.get_initiator(), Weight::new(1)),
2971            None,
2972        ))
2973    }
2974
2975    fn disable_contract_version(
2976        &mut self,
2977        contract_package_hash: PackageHash,
2978        contract_hash: AddressableEntityHash,
2979    ) -> Result<Result<(), ApiError>, ExecError> {
2980        if self.context.engine_config().enable_entity {
2981            let contract_package_key = Key::SmartContract(contract_package_hash.value());
2982            self.context.validate_key(&contract_package_key)?;
2983
2984            let mut contract_package: Package =
2985                self.context.get_validated_package(contract_package_hash)?;
2986
2987            if contract_package.is_locked() {
2988                return Err(ExecError::LockedEntity(contract_package_hash));
2989            }
2990
2991            if let Err(err) = contract_package
2992                .disable_entity_version(EntityAddr::SmartContract(contract_hash.value()))
2993            {
2994                return Ok(Err(err.into()));
2995            }
2996
2997            self.context
2998                .metered_write_gs_unsafe(contract_package_key, contract_package)?;
2999        } else {
3000            let contract_package_key = Key::Hash(contract_package_hash.value());
3001            self.context.validate_key(&contract_package_key)?;
3002
3003            let mut contract_package: ContractPackage = self
3004                .context
3005                .get_validated_contract_package(contract_package_hash.value())?;
3006
3007            if contract_package.is_locked() {
3008                return Err(ExecError::LockedEntity(PackageHash::new(
3009                    contract_package_hash.value(),
3010                )));
3011            }
3012            let contract_hash = ContractHash::new(contract_hash.value());
3013
3014            if let Err(err) = contract_package.disable_contract_version(contract_hash) {
3015                return Ok(Err(err.into()));
3016            }
3017
3018            self.context
3019                .metered_write_gs_unsafe(contract_package_key, contract_package)?;
3020        }
3021
3022        Ok(Ok(()))
3023    }
3024
3025    fn enable_contract_version(
3026        &mut self,
3027        contract_package_hash: PackageHash,
3028        contract_hash: AddressableEntityHash,
3029    ) -> Result<Result<(), ApiError>, ExecError> {
3030        if self.context.engine_config().enable_entity {
3031            let contract_package_key = Key::SmartContract(contract_package_hash.value());
3032            self.context.validate_key(&contract_package_key)?;
3033
3034            let mut contract_package: Package =
3035                self.context.get_validated_package(contract_package_hash)?;
3036
3037            if contract_package.is_locked() {
3038                return Err(ExecError::LockedEntity(contract_package_hash));
3039            }
3040
3041            if let Err(err) =
3042                contract_package.enable_version(EntityAddr::SmartContract(contract_hash.value()))
3043            {
3044                return Ok(Err(err.into()));
3045            }
3046
3047            self.context
3048                .metered_write_gs_unsafe(contract_package_key, contract_package)?;
3049        } else {
3050            let contract_package_key = Key::Hash(contract_package_hash.value());
3051            self.context.validate_key(&contract_package_key)?;
3052
3053            let mut contract_package: ContractPackage = self
3054                .context
3055                .get_validated_contract_package(contract_package_hash.value())?;
3056
3057            if contract_package.is_locked() {
3058                return Err(ExecError::LockedEntity(PackageHash::new(
3059                    contract_package_hash.value(),
3060                )));
3061            }
3062            let contract_hash = ContractHash::new(contract_hash.value());
3063
3064            if let Err(err) = contract_package.enable_contract_version(contract_hash) {
3065                return Ok(Err(err.into()));
3066            }
3067
3068            self.context
3069                .metered_write_gs_unsafe(contract_package_key, contract_package)?;
3070        }
3071
3072        Ok(Ok(()))
3073    }
3074
3075    /// Writes function address (`hash_bytes`) into the Wasm memory (at
3076    /// `dest_ptr` pointer).
3077    fn function_address(&mut self, hash_bytes: [u8; 32], dest_ptr: u32) -> Result<(), Trap> {
3078        self.try_get_memory()?
3079            .set(dest_ptr, &hash_bytes)
3080            .map_err(|e| ExecError::Interpreter(e.into()).into())
3081    }
3082
3083    /// Generates new unforgeable reference and adds it to the context's
3084    /// access_rights set.
3085    fn new_uref(&mut self, uref_ptr: u32, value_ptr: u32, value_size: u32) -> Result<(), Trap> {
3086        let cl_value = self.cl_value_from_mem(value_ptr, value_size)?; // read initial value from memory
3087        let uref = self.context.new_uref(StoredValue::CLValue(cl_value))?;
3088        self.try_get_memory()?
3089            .set(uref_ptr, &uref.into_bytes().map_err(ExecError::BytesRepr)?)
3090            .map_err(|e| ExecError::Interpreter(e.into()).into())
3091    }
3092
3093    /// Writes `value` under `key` in GlobalState.
3094    fn write(
3095        &mut self,
3096        key_ptr: u32,
3097        key_size: u32,
3098        value_ptr: u32,
3099        value_size: u32,
3100    ) -> Result<(), Trap> {
3101        let key = self.key_from_mem(key_ptr, key_size)?;
3102        let cl_value = self.cl_value_from_mem(value_ptr, value_size)?;
3103        self.context
3104            .metered_write_gs(key, cl_value)
3105            .map_err(Into::into)
3106    }
3107
3108    /// Records a transfer.
3109    fn record_transfer(
3110        &mut self,
3111        maybe_to: Option<AccountHash>,
3112        source: URef,
3113        target: URef,
3114        amount: U512,
3115        id: Option<u64>,
3116    ) -> Result<(), ExecError> {
3117        if self.context.get_context_key() != self.context.get_system_entity_key(MINT)? {
3118            return Err(ExecError::InvalidContext);
3119        }
3120
3121        if self.context.phase() != Phase::Session {
3122            return Ok(());
3123        }
3124
3125        let txn_hash = self.context.get_transaction_hash();
3126        let from = InitiatorAddr::AccountHash(self.context.get_initiator());
3127        let fee = Gas::from(
3128            self.context
3129                .engine_config()
3130                .system_config()
3131                .mint_costs()
3132                .transfer,
3133        );
3134        let transfer = Transfer::V2(TransferV2::new(
3135            txn_hash, from, maybe_to, source, target, amount, fee, id,
3136        ));
3137        self.context.transfers_mut().push(transfer);
3138        Ok(())
3139    }
3140
3141    /// Records given auction info at a given era id
3142    fn record_era_info(&mut self, era_info: EraInfo) -> Result<(), ExecError> {
3143        if self.context.get_initiator() != PublicKey::System.to_account_hash() {
3144            return Err(ExecError::InvalidContext);
3145        }
3146
3147        if self.context.get_context_key() != self.context.get_system_entity_key(AUCTION)? {
3148            return Err(ExecError::InvalidContext);
3149        }
3150
3151        if self.context.phase() != Phase::Session {
3152            return Ok(());
3153        }
3154
3155        self.context.write_era_info(Key::EraSummary, era_info);
3156
3157        Ok(())
3158    }
3159
3160    /// Adds `value` to the cell that `key` points at.
3161    fn add(
3162        &mut self,
3163        key_ptr: u32,
3164        key_size: u32,
3165        value_ptr: u32,
3166        value_size: u32,
3167    ) -> Result<(), Trap> {
3168        let key = self.key_from_mem(key_ptr, key_size)?;
3169        let cl_value = self.cl_value_from_mem(value_ptr, value_size)?;
3170        self.context
3171            .metered_add_gs(key, cl_value)
3172            .map_err(Into::into)
3173    }
3174
3175    /// Reads value from the GS living under key specified by `key_ptr` and
3176    /// `key_size`. Wasm and host communicate through memory that Wasm
3177    /// module exports. If contract wants to pass data to the host, it has
3178    /// to tell it [the host] where this data lives in the exported memory
3179    /// (pass its pointer and length).
3180    fn read(
3181        &mut self,
3182        key_ptr: u32,
3183        key_size: u32,
3184        output_size_ptr: u32,
3185    ) -> Result<Result<(), ApiError>, Trap> {
3186        if !self.can_write_to_host_buffer() {
3187            // Exit early if the host buffer is already occupied
3188            return Ok(Err(ApiError::HostBufferFull));
3189        }
3190
3191        let key = self.key_from_mem(key_ptr, key_size)?;
3192        let cl_value = match self.context.read_gs(&key)? {
3193            Some(stored_value) => {
3194                CLValue::try_from(stored_value).map_err(ExecError::TypeMismatch)?
3195            }
3196            None => return Ok(Err(ApiError::ValueNotFound)),
3197        };
3198
3199        let value_size: u32 = match cl_value.inner_bytes().len().try_into() {
3200            Ok(value) => value,
3201            Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
3202        };
3203
3204        if let Err(error) = self.write_host_buffer(cl_value) {
3205            return Ok(Err(error));
3206        }
3207
3208        let value_bytes = value_size.to_le_bytes(); // Wasm is little-endian
3209        if let Err(error) = self.try_get_memory()?.set(output_size_ptr, &value_bytes) {
3210            return Err(ExecError::Interpreter(error.into()).into());
3211        }
3212
3213        Ok(Ok(()))
3214    }
3215
3216    /// Reverts contract execution with a status specified.
3217    fn revert(&mut self, status: u32) -> Trap {
3218        ExecError::Revert(status.into()).into()
3219    }
3220
3221    /// Checks if a caller can manage its own associated keys and thresholds.
3222    ///
3223    /// On some private chains with administrator keys configured this requires that the caller is
3224    /// an admin to be able to manage its own keys. If the caller is not an administrator then the
3225    /// deploy has to be signed by an administrator.
3226    fn can_manage_keys(&self) -> bool {
3227        if self
3228            .context
3229            .engine_config()
3230            .administrative_accounts()
3231            .is_empty()
3232        {
3233            // Public chain
3234            return self
3235                .context
3236                .runtime_footprint()
3237                .borrow()
3238                .can_manage_keys_with(self.context.authorization_keys());
3239        }
3240
3241        if self
3242            .context
3243            .engine_config()
3244            .is_administrator(&self.context.get_initiator())
3245        {
3246            return true;
3247        }
3248
3249        // If caller is not an admin, check if deploy was co-signed by admin account.
3250        self.context.is_authorized_by_admin()
3251    }
3252
3253    fn add_associated_key(
3254        &mut self,
3255        account_hash_ptr: u32,
3256        account_hash_size: usize,
3257        weight_value: u8,
3258    ) -> Result<i32, Trap> {
3259        let account_hash = {
3260            // Account hash as serialized bytes
3261            let source_serialized = self.bytes_from_mem(account_hash_ptr, account_hash_size)?;
3262            // Account hash deserialized
3263            let source: AccountHash = bytesrepr::deserialize_from_slice(source_serialized)
3264                .map_err(ExecError::BytesRepr)?;
3265            source
3266        };
3267        let weight = Weight::new(weight_value);
3268
3269        if !self.can_manage_keys() {
3270            return Ok(AddKeyFailure::PermissionDenied as i32);
3271        }
3272
3273        match self.context.add_associated_key(account_hash, weight) {
3274            Ok(_) => Ok(0),
3275            // This relies on the fact that `AddKeyFailure` is represented as
3276            // i32 and first variant start with number `1`, so all other variants
3277            // are greater than the first one, so it's safe to assume `0` is success,
3278            // and any error is greater than 0.
3279            Err(ExecError::AddKeyFailure(e)) => Ok(e as i32),
3280            // Any other variant just pass as `Trap`
3281            Err(e) => Err(e.into()),
3282        }
3283    }
3284
3285    fn remove_associated_key(
3286        &mut self,
3287        account_hash_ptr: u32,
3288        account_hash_size: usize,
3289    ) -> Result<i32, Trap> {
3290        let account_hash = {
3291            // Account hash as serialized bytes
3292            let source_serialized = self.bytes_from_mem(account_hash_ptr, account_hash_size)?;
3293            // Account hash deserialized
3294            let source: AccountHash = bytesrepr::deserialize_from_slice(source_serialized)
3295                .map_err(ExecError::BytesRepr)?;
3296            source
3297        };
3298
3299        if !self.can_manage_keys() {
3300            return Ok(RemoveKeyFailure::PermissionDenied as i32);
3301        }
3302
3303        match self.context.remove_associated_key(account_hash) {
3304            Ok(_) => Ok(0),
3305            Err(ExecError::RemoveKeyFailure(e)) => Ok(e as i32),
3306            Err(e) => Err(e.into()),
3307        }
3308    }
3309
3310    fn update_associated_key(
3311        &mut self,
3312        account_hash_ptr: u32,
3313        account_hash_size: usize,
3314        weight_value: u8,
3315    ) -> Result<i32, Trap> {
3316        let account_hash = {
3317            // Account hash as serialized bytes
3318            let source_serialized = self.bytes_from_mem(account_hash_ptr, account_hash_size)?;
3319            // Account hash deserialized
3320            let source: AccountHash = bytesrepr::deserialize_from_slice(source_serialized)
3321                .map_err(ExecError::BytesRepr)?;
3322            source
3323        };
3324        let weight = Weight::new(weight_value);
3325
3326        if !self.can_manage_keys() {
3327            return Ok(UpdateKeyFailure::PermissionDenied as i32);
3328        }
3329
3330        match self.context.update_associated_key(account_hash, weight) {
3331            Ok(_) => Ok(0),
3332            // This relies on the fact that `UpdateKeyFailure` is represented as
3333            // i32 and first variant start with number `1`, so all other variants
3334            // are greater than the first one, so it's safe to assume `0` is success,
3335            // and any error is greater than 0.
3336            Err(ExecError::UpdateKeyFailure(e)) => Ok(e as i32),
3337            // Any other variant just pass as `Trap`
3338            Err(e) => Err(e.into()),
3339        }
3340    }
3341
3342    fn set_action_threshold(
3343        &mut self,
3344        action_type_value: u32,
3345        threshold_value: u8,
3346    ) -> Result<i32, Trap> {
3347        if !self.can_manage_keys() {
3348            return Ok(SetThresholdFailure::PermissionDeniedError as i32);
3349        }
3350
3351        match ActionType::try_from(action_type_value) {
3352            Ok(action_type) => {
3353                let threshold = Weight::new(threshold_value);
3354                match self.context.set_action_threshold(action_type, threshold) {
3355                    Ok(_) => Ok(0),
3356                    Err(ExecError::SetThresholdFailure(e)) => Ok(e as i32),
3357                    Err(error) => Err(error.into()),
3358                }
3359            }
3360            Err(_) => Err(Trap::Code(TrapCode::Unreachable)),
3361        }
3362    }
3363
3364    /// Looks up the public mint contract key in the context's protocol data.
3365    ///
3366    /// Returned URef is already attenuated depending on the calling account.
3367    fn get_mint_hash(&self) -> Result<AddressableEntityHash, ExecError> {
3368        self.context.get_system_contract(MINT)
3369    }
3370
3371    /// Looks up the public handle payment contract key in the context's protocol data.
3372    ///
3373    /// Returned URef is already attenuated depending on the calling account.
3374    fn get_handle_payment_hash(&self) -> Result<AddressableEntityHash, ExecError> {
3375        self.context.get_system_contract(HANDLE_PAYMENT)
3376    }
3377
3378    /// Looks up the public standard payment contract key in the context's protocol data.
3379    ///
3380    /// Returned URef is already attenuated depending on the calling account.
3381    fn get_standard_payment_hash(&self) -> Result<AddressableEntityHash, ExecError> {
3382        self.context.get_system_contract(STANDARD_PAYMENT)
3383    }
3384
3385    /// Looks up the public auction contract key in the context's protocol data.
3386    ///
3387    /// Returned URef is already attenuated depending on the calling account.
3388    fn get_auction_hash(&self) -> Result<AddressableEntityHash, ExecError> {
3389        self.context.get_system_contract(AUCTION)
3390    }
3391
3392    /// Calls the `read_base_round_reward` method on the mint contract at the given mint
3393    /// contract key
3394    fn mint_read_base_round_reward(
3395        &mut self,
3396        mint_contract_hash: AddressableEntityHash,
3397    ) -> Result<U512, ExecError> {
3398        let gas_counter = self.gas_counter();
3399        let call_result = self.call_contract(
3400            mint_contract_hash,
3401            mint::METHOD_READ_BASE_ROUND_REWARD,
3402            RuntimeArgs::default(),
3403        );
3404        self.set_gas_counter(gas_counter);
3405
3406        let reward = call_result?.into_t()?;
3407        Ok(reward)
3408    }
3409
3410    /// Calls the `mint` method on the mint contract at the given mint
3411    /// contract key
3412    fn mint_mint(
3413        &mut self,
3414        mint_contract_hash: AddressableEntityHash,
3415        amount: U512,
3416    ) -> Result<URef, ExecError> {
3417        let gas_counter = self.gas_counter();
3418        let runtime_args = {
3419            let mut runtime_args = RuntimeArgs::new();
3420            runtime_args.insert(mint::ARG_AMOUNT, amount)?;
3421            runtime_args
3422        };
3423        let call_result = self.call_contract(mint_contract_hash, mint::METHOD_MINT, runtime_args);
3424        self.set_gas_counter(gas_counter);
3425
3426        let result: Result<URef, mint::Error> = call_result?.into_t()?;
3427        Ok(result.map_err(system::Error::from)?)
3428    }
3429
3430    /// Calls the `reduce_total_supply` method on the mint contract at the given mint
3431    /// contract key
3432    fn mint_reduce_total_supply(
3433        &mut self,
3434        mint_contract_hash: AddressableEntityHash,
3435        amount: U512,
3436    ) -> Result<(), ExecError> {
3437        let gas_counter = self.gas_counter();
3438        let runtime_args = {
3439            let mut runtime_args = RuntimeArgs::new();
3440            runtime_args.insert(mint::ARG_AMOUNT, amount)?;
3441            runtime_args
3442        };
3443        let call_result = self.call_contract(
3444            mint_contract_hash,
3445            mint::METHOD_REDUCE_TOTAL_SUPPLY,
3446            runtime_args,
3447        );
3448        self.set_gas_counter(gas_counter);
3449
3450        let result: Result<(), mint::Error> = call_result?.into_t()?;
3451        Ok(result.map_err(system::Error::from)?)
3452    }
3453
3454    /// Calls the "create" method on the mint contract at the given mint
3455    /// contract key
3456    fn mint_create(
3457        &mut self,
3458        mint_contract_hash: AddressableEntityHash,
3459    ) -> Result<URef, ExecError> {
3460        let result =
3461            self.call_contract(mint_contract_hash, mint::METHOD_CREATE, RuntimeArgs::new());
3462        let purse = result?.into_t()?;
3463        Ok(purse)
3464    }
3465
3466    fn create_purse(&mut self) -> Result<URef, ExecError> {
3467        let _scoped_host_function_flag = self.host_function_flag.enter_host_function_scope();
3468        self.mint_create(self.get_mint_hash()?)
3469    }
3470
3471    /// Calls the "transfer" method on the mint contract at the given mint
3472    /// contract key
3473    fn mint_transfer(
3474        &mut self,
3475        mint_contract_hash: AddressableEntityHash,
3476        to: Option<AccountHash>,
3477        source: URef,
3478        target: URef,
3479        amount: U512,
3480        id: Option<u64>,
3481    ) -> Result<Result<(), mint::Error>, ExecError> {
3482        self.context.validate_uref(&source)?;
3483
3484        let args_values = {
3485            let mut runtime_args = RuntimeArgs::new();
3486            runtime_args.insert(mint::ARG_TO, to)?;
3487            runtime_args.insert(mint::ARG_SOURCE, source)?;
3488            runtime_args.insert(mint::ARG_TARGET, target)?;
3489            runtime_args.insert(mint::ARG_AMOUNT, amount)?;
3490            runtime_args.insert(mint::ARG_ID, id)?;
3491            runtime_args
3492        };
3493
3494        let gas_counter = self.gas_counter();
3495        let call_result =
3496            self.call_contract(mint_contract_hash, mint::METHOD_TRANSFER, args_values);
3497        self.set_gas_counter(gas_counter);
3498
3499        Ok(call_result?.into_t()?)
3500    }
3501
3502    /// Creates a new account at `target` hash, transferring `amount`
3503    /// of motes from `source` purse to the new account's main purse.
3504    fn transfer_to_new_account(
3505        &mut self,
3506        source: URef,
3507        target: AccountHash,
3508        amount: U512,
3509        id: Option<u64>,
3510    ) -> Result<TransferResult, ExecError> {
3511        let mint_contract_hash = self.get_mint_hash()?;
3512
3513        let allow_unrestricted_transfers =
3514            self.context.engine_config().allow_unrestricted_transfers();
3515
3516        if !allow_unrestricted_transfers
3517            && self.context.get_initiator() != PublicKey::System.to_account_hash()
3518            && !self
3519                .context
3520                .engine_config()
3521                .is_administrator(&self.context.get_initiator())
3522            && !self.context.engine_config().is_administrator(&target)
3523        {
3524            return Err(ExecError::DisabledUnrestrictedTransfers);
3525        }
3526
3527        // A precondition check that verifies that the transfer can be done
3528        // as the source purse has enough funds to cover the transfer.
3529        if amount > self.available_balance(source)?.unwrap_or_default() {
3530            return Ok(Err(mint::Error::InsufficientFunds.into()));
3531        }
3532
3533        let target_purse = self.mint_create(mint_contract_hash)?;
3534
3535        if source == target_purse {
3536            return Ok(Err(mint::Error::EqualSourceAndTarget.into()));
3537        }
3538
3539        let result = self.mint_transfer(
3540            mint_contract_hash,
3541            Some(target),
3542            source,
3543            target_purse.with_access_rights(AccessRights::ADD),
3544            amount,
3545            id,
3546        );
3547
3548        // We granted a temporary access rights bit to newly created main purse as part of
3549        // `mint_create` call, and we need to remove it to avoid leakage of access rights.
3550
3551        self.context
3552            .remove_access(target_purse.addr(), target_purse.access_rights());
3553
3554        match result? {
3555            Ok(()) => {
3556                let main_purse = target_purse;
3557                if !self.context.engine_config().enable_entity {
3558                    let account = Account::create(target, NamedKeys::new(), target_purse);
3559                    self.context.metered_write_gs_unsafe(
3560                        Key::Account(target),
3561                        StoredValue::Account(account),
3562                    )?;
3563                    return Ok(Ok(TransferredTo::NewAccount));
3564                }
3565
3566                let protocol_version = self.context.protocol_version();
3567                let byte_code_hash = ByteCodeHash::default();
3568                let entity_hash = AddressableEntityHash::new(target.value());
3569                let package_hash = PackageHash::new(self.context.new_hash_address()?);
3570
3571                let associated_keys = AssociatedKeys::new(target, Weight::new(1));
3572
3573                let entity = AddressableEntity::new(
3574                    package_hash,
3575                    byte_code_hash,
3576                    protocol_version,
3577                    main_purse,
3578                    associated_keys,
3579                    ActionThresholds::default(),
3580                    EntityKind::Account(target),
3581                );
3582
3583                let package = {
3584                    let mut package = Package::new(
3585                        EntityVersions::default(),
3586                        BTreeSet::default(),
3587                        Groups::default(),
3588                        PackageStatus::Locked,
3589                    );
3590                    package.insert_entity_version(
3591                        protocol_version.value().major,
3592                        EntityAddr::Account(target.value()),
3593                    );
3594                    package
3595                };
3596
3597                let entity_key: Key = entity.entity_key(entity_hash);
3598
3599                self.context
3600                    .metered_write_gs_unsafe(entity_key, StoredValue::AddressableEntity(entity))?;
3601
3602                let contract_package_key: Key = package_hash.into();
3603
3604                self.context.metered_write_gs_unsafe(
3605                    contract_package_key,
3606                    StoredValue::SmartContract(package),
3607                )?;
3608
3609                let contract_by_account = CLValue::from_t(entity_key)?;
3610
3611                let target_key = Key::Account(target);
3612
3613                self.context.metered_write_gs_unsafe(
3614                    target_key,
3615                    StoredValue::CLValue(contract_by_account),
3616                )?;
3617
3618                Ok(Ok(TransferredTo::NewAccount))
3619            }
3620            Err(mint_error) => Ok(Err(mint_error.into())),
3621        }
3622    }
3623
3624    /// Transferring a given amount of motes from the given source purse to the
3625    /// new account's purse. Requires that the [`URef`]s have already
3626    /// been created by the mint contract (or are the genesis account's).
3627    fn transfer_to_existing_account(
3628        &mut self,
3629        to: Option<AccountHash>,
3630        source: URef,
3631        target: URef,
3632        amount: U512,
3633        id: Option<u64>,
3634    ) -> Result<TransferResult, ExecError> {
3635        let mint_contract_key = self.get_mint_hash()?;
3636
3637        match self.mint_transfer(mint_contract_key, to, source, target, amount, id)? {
3638            Ok(()) => Ok(Ok(TransferredTo::ExistingAccount)),
3639            Err(error) => Ok(Err(error.into())),
3640        }
3641    }
3642
3643    /// Transfers `amount` of motes from default purse of the account to
3644    /// `target` account. If that account does not exist, creates one.
3645    fn transfer_to_account(
3646        &mut self,
3647        target: AccountHash,
3648        amount: U512,
3649        id: Option<u64>,
3650    ) -> Result<TransferResult, ExecError> {
3651        let source = self.context.get_main_purse()?;
3652        self.transfer_from_purse_to_account_hash(source, target, amount, id)
3653    }
3654
3655    /// Transfers `amount` of motes from `source` purse to `target` account's main purse.
3656    /// If that account does not exist, creates one.
3657    fn transfer_from_purse_to_account_hash(
3658        &mut self,
3659        source: URef,
3660        target: AccountHash,
3661        amount: U512,
3662        id: Option<u64>,
3663    ) -> Result<TransferResult, ExecError> {
3664        let _scoped_host_function_flag = self.host_function_flag.enter_host_function_scope();
3665        let target_key = Key::Account(target);
3666
3667        // Look up the account at the given key
3668        match self.context.read_gs(&target_key)? {
3669            None => {
3670                // If no account exists, create a new account and transfer the amount to its
3671                // main purse.
3672
3673                self.transfer_to_new_account(source, target, amount, id)
3674            }
3675            Some(StoredValue::CLValue(entity_key_value)) => {
3676                // Attenuate the target main purse
3677                let entity_key = CLValue::into_t::<Key>(entity_key_value)?;
3678                let target_uref = if let Some(StoredValue::AddressableEntity(entity)) =
3679                    self.context.read_gs(&entity_key)?
3680                {
3681                    entity.main_purse_add_only()
3682                } else {
3683                    let contract_hash = if let Some(entity_hash) = entity_key
3684                        .into_entity_hash_addr()
3685                        .map(AddressableEntityHash::new)
3686                    {
3687                        entity_hash
3688                    } else {
3689                        return Err(ExecError::UnexpectedKeyVariant(entity_key));
3690                    };
3691                    return Err(ExecError::InvalidEntity(contract_hash));
3692                };
3693
3694                if source.with_access_rights(AccessRights::ADD) == target_uref {
3695                    return Ok(Ok(TransferredTo::ExistingAccount));
3696                }
3697
3698                // Upsert ADD access to caller on target allowing deposit of motes; this will be
3699                // revoked after the transfer is completed if caller did not already have ADD access
3700                let granted_access = self.context.grant_access(target_uref);
3701
3702                // If an account exists, transfer the amount to its purse
3703                let transfer_result = self.transfer_to_existing_account(
3704                    Some(target),
3705                    source,
3706                    target_uref,
3707                    amount,
3708                    id,
3709                );
3710
3711                // Remove from caller temporarily granted ADD access on target.
3712                if let GrantedAccess::Granted {
3713                    uref_addr,
3714                    newly_granted_access_rights,
3715                } = granted_access
3716                {
3717                    self.context
3718                        .remove_access(uref_addr, newly_granted_access_rights)
3719                }
3720                transfer_result
3721            }
3722            Some(StoredValue::Account(account)) => {
3723                self.transfer_from_purse_to_account(source, &account, amount, id)
3724            }
3725            Some(_) => {
3726                // If some other value exists, return an error
3727                Err(ExecError::AccountNotFound(target_key))
3728            }
3729        }
3730    }
3731
3732    fn transfer_from_purse_to_account(
3733        &mut self,
3734        source: URef,
3735        target_account: &Account,
3736        amount: U512,
3737        id: Option<u64>,
3738    ) -> Result<TransferResult, ExecError> {
3739        // Attenuate the target main purse
3740        let target_uref = target_account.main_purse_add_only();
3741
3742        if source.with_access_rights(AccessRights::ADD) == target_uref {
3743            return Ok(Ok(TransferredTo::ExistingAccount));
3744        }
3745
3746        // Grant ADD access to caller on target allowing deposit of motes; this will be
3747        // revoked after the transfer is completed if caller did not already have ADD access
3748        let granted_access = self.context.grant_access(target_uref);
3749
3750        // If an account exists, transfer the amount to its purse
3751        let transfer_result = self.transfer_to_existing_account(
3752            Some(target_account.account_hash()),
3753            source,
3754            target_uref,
3755            amount,
3756            id,
3757        );
3758
3759        // Remove from caller temporarily granted ADD access on target.
3760        if let GrantedAccess::Granted {
3761            uref_addr,
3762            newly_granted_access_rights,
3763        } = granted_access
3764        {
3765            self.context
3766                .remove_access(uref_addr, newly_granted_access_rights)
3767        }
3768        transfer_result
3769    }
3770
3771    /// Transfers `amount` of motes from `source` purse to `target` purse.
3772    fn transfer_from_purse_to_purse(
3773        &mut self,
3774        source: URef,
3775        target: URef,
3776        amount: U512,
3777        id: Option<u64>,
3778    ) -> Result<Result<(), mint::Error>, ExecError> {
3779        self.context.validate_uref(&source)?;
3780        let mint_contract_key = self.get_mint_hash()?;
3781        match self.mint_transfer(mint_contract_key, None, source, target, amount, id)? {
3782            Ok(()) => Ok(Ok(())),
3783            Err(mint_error) => Ok(Err(mint_error)),
3784        }
3785    }
3786
3787    fn total_balance(&mut self, purse: URef) -> Result<U512, ExecError> {
3788        match self.context.total_balance(&purse) {
3789            Ok(motes) => Ok(motes.value()),
3790            Err(err) => Err(err),
3791        }
3792    }
3793
3794    fn available_balance(&mut self, purse: URef) -> Result<Option<U512>, ExecError> {
3795        match self.context.available_balance(&purse) {
3796            Ok(motes) => Ok(Some(motes.value())),
3797            Err(err) => Err(err),
3798        }
3799    }
3800
3801    fn get_balance_host_buffer(
3802        &mut self,
3803        purse_ptr: u32,
3804        purse_size: usize,
3805        output_size_ptr: u32,
3806    ) -> Result<Result<(), ApiError>, ExecError> {
3807        if !self.can_write_to_host_buffer() {
3808            // Exit early if the host buffer is already occupied
3809            return Ok(Err(ApiError::HostBufferFull));
3810        }
3811
3812        let purse: URef = {
3813            let bytes = self.bytes_from_mem(purse_ptr, purse_size)?;
3814            match bytesrepr::deserialize_from_slice(bytes) {
3815                Ok(purse) => purse,
3816                Err(error) => return Ok(Err(error.into())),
3817            }
3818        };
3819
3820        let balance = match self.available_balance(purse)? {
3821            Some(balance) => balance,
3822            None => return Ok(Err(ApiError::InvalidPurse)),
3823        };
3824
3825        let balance_cl_value = match CLValue::from_t(balance) {
3826            Ok(cl_value) => cl_value,
3827            Err(error) => return Ok(Err(error.into())),
3828        };
3829
3830        let balance_size = balance_cl_value.inner_bytes().len() as i32;
3831        if let Err(error) = self.write_host_buffer(balance_cl_value) {
3832            return Ok(Err(error));
3833        }
3834
3835        let balance_size_bytes = balance_size.to_le_bytes(); // Wasm is little-endian
3836        if let Err(error) = self
3837            .try_get_memory()?
3838            .set(output_size_ptr, &balance_size_bytes)
3839        {
3840            return Err(ExecError::Interpreter(error.into()));
3841        }
3842
3843        Ok(Ok(()))
3844    }
3845
3846    fn get_system_contract(
3847        &mut self,
3848        system_contract_index: u32,
3849        dest_ptr: u32,
3850        _dest_size: u32,
3851    ) -> Result<Result<(), ApiError>, Trap> {
3852        let hash: AddressableEntityHash = match SystemEntityType::try_from(system_contract_index) {
3853            Ok(SystemEntityType::Mint) => self.get_mint_hash()?,
3854            Ok(SystemEntityType::HandlePayment) => self.get_handle_payment_hash()?,
3855            Ok(SystemEntityType::StandardPayment) => self.get_standard_payment_hash()?,
3856            Ok(SystemEntityType::Auction) => self.get_auction_hash()?,
3857            Err(error) => return Ok(Err(error)),
3858        };
3859
3860        match self.try_get_memory()?.set(dest_ptr, hash.as_ref()) {
3861            Ok(_) => Ok(Ok(())),
3862            Err(error) => Err(ExecError::Interpreter(error.into()).into()),
3863        }
3864    }
3865
3866    /// If host_buffer set, clears the host_buffer and returns value, else None
3867    pub fn take_host_buffer(&mut self) -> Option<CLValue> {
3868        self.host_buffer.take()
3869    }
3870
3871    /// Checks if a write to host buffer can happen.
3872    ///
3873    /// This will check if the host buffer is empty.
3874    fn can_write_to_host_buffer(&self) -> bool {
3875        self.host_buffer.is_none()
3876    }
3877
3878    /// Overwrites data in host buffer only if it's in empty state
3879    fn write_host_buffer(&mut self, data: CLValue) -> Result<(), ApiError> {
3880        match self.host_buffer {
3881            Some(_) => return Err(ApiError::HostBufferFull),
3882            None => self.host_buffer = Some(data),
3883        }
3884        Ok(())
3885    }
3886
3887    fn read_host_buffer(
3888        &mut self,
3889        dest_ptr: u32,
3890        dest_size: usize,
3891        bytes_written_ptr: u32,
3892    ) -> Result<Result<(), ApiError>, ExecError> {
3893        let (_cl_type, serialized_value) = match self.take_host_buffer() {
3894            None => return Ok(Err(ApiError::HostBufferEmpty)),
3895            Some(cl_value) => cl_value.destructure(),
3896        };
3897
3898        if serialized_value.len() > u32::MAX as usize {
3899            return Ok(Err(ApiError::OutOfMemory));
3900        }
3901        if serialized_value.len() > dest_size {
3902            return Ok(Err(ApiError::BufferTooSmall));
3903        }
3904
3905        // Slice data, so if `dest_size` is larger than host_buffer size, it will take host_buffer
3906        // as whole.
3907        let sliced_buf = &serialized_value[..cmp::min(dest_size, serialized_value.len())];
3908        if let Err(error) = self.try_get_memory()?.set(dest_ptr, sliced_buf) {
3909            return Err(ExecError::Interpreter(error.into()));
3910        }
3911
3912        // Never panics because we check that `serialized_value.len()` fits in `u32`.
3913        let bytes_written: u32 = sliced_buf
3914            .len()
3915            .try_into()
3916            .expect("Size of buffer should fit within limit");
3917        let bytes_written_data = bytes_written.to_le_bytes();
3918
3919        if let Err(error) = self
3920            .try_get_memory()?
3921            .set(bytes_written_ptr, &bytes_written_data)
3922        {
3923            return Err(ExecError::Interpreter(error.into()));
3924        }
3925
3926        Ok(Ok(()))
3927    }
3928
3929    #[cfg(feature = "test-support")]
3930    fn print(&mut self, text_ptr: u32, text_size: u32) -> Result<(), Trap> {
3931        let text = self.string_from_mem(text_ptr, text_size)?;
3932        println!("{}", text); // this println! is intentional
3933        Ok(())
3934    }
3935
3936    fn get_named_arg_size(
3937        &mut self,
3938        name_ptr: u32,
3939        name_size: usize,
3940        size_ptr: u32,
3941    ) -> Result<Result<(), ApiError>, Trap> {
3942        let name_bytes = self.bytes_from_mem(name_ptr, name_size)?;
3943        let name = String::from_utf8_lossy(&name_bytes);
3944
3945        let arg_size: u32 = match self.context.args().get(&name) {
3946            Some(arg) if arg.inner_bytes().len() > u32::MAX as usize => {
3947                return Ok(Err(ApiError::OutOfMemory));
3948            }
3949            Some(arg) => {
3950                // SAFETY: Safe to unwrap as we asserted length above
3951                arg.inner_bytes()
3952                    .len()
3953                    .try_into()
3954                    .expect("Should fit within the range")
3955            }
3956            None => return Ok(Err(ApiError::MissingArgument)),
3957        };
3958
3959        let arg_size_bytes = arg_size.to_le_bytes(); // Wasm is little-endian
3960
3961        if let Err(e) = self.try_get_memory()?.set(size_ptr, &arg_size_bytes) {
3962            return Err(ExecError::Interpreter(e.into()).into());
3963        }
3964
3965        Ok(Ok(()))
3966    }
3967
3968    fn get_named_arg(
3969        &mut self,
3970        name_ptr: u32,
3971        name_size: usize,
3972        output_ptr: u32,
3973        output_size: usize,
3974    ) -> Result<Result<(), ApiError>, Trap> {
3975        let name_bytes = self.bytes_from_mem(name_ptr, name_size)?;
3976        let name = String::from_utf8_lossy(&name_bytes);
3977
3978        let arg = match self.context.args().get(&name) {
3979            Some(arg) => arg,
3980            None => return Ok(Err(ApiError::MissingArgument)),
3981        };
3982
3983        if arg.inner_bytes().len() > output_size {
3984            return Ok(Err(ApiError::OutOfMemory));
3985        }
3986
3987        if let Err(error) = self
3988            .try_get_memory()?
3989            .set(output_ptr, &arg.inner_bytes()[..output_size])
3990        {
3991            return Err(ExecError::Interpreter(error.into()).into());
3992        }
3993
3994        Ok(Ok(()))
3995    }
3996
3997    /// Enforce group access restrictions (if any) on attempts to call an `EntryPoint`.
3998    fn validate_entry_point_access(
3999        &self,
4000        package: &Package,
4001        name: &str,
4002        access: &EntryPointAccess,
4003    ) -> Result<(), ExecError> {
4004        match access {
4005            EntryPointAccess::Public => Ok(()),
4006            EntryPointAccess::Groups(group_names) => {
4007                if group_names.is_empty() {
4008                    // Exits early in a special case of empty list of groups regardless of the group
4009                    // checking logic below it.
4010                    return Err(ExecError::InvalidContext);
4011                }
4012
4013                let find_result = group_names.iter().find(|&group_name| {
4014                    package
4015                        .groups()
4016                        .get(group_name)
4017                        .and_then(|urefs| {
4018                            urefs
4019                                .iter()
4020                                .find(|&uref| self.context.validate_uref(uref).is_ok())
4021                        })
4022                        .is_some()
4023                });
4024
4025                if find_result.is_none() {
4026                    return Err(ExecError::InvalidContext);
4027                }
4028
4029                Ok(())
4030            }
4031            EntryPointAccess::Template => Err(ExecError::TemplateMethod(name.to_string())),
4032        }
4033    }
4034
4035    /// Remove a user group from access to a contract
4036    fn remove_contract_user_group(
4037        &mut self,
4038        package_key: PackageHash,
4039        label: Group,
4040    ) -> Result<Result<(), ApiError>, ExecError> {
4041        if self.context.engine_config().enable_entity {
4042            let mut package: Package = self.context.get_validated_package(package_key)?;
4043            let group_to_remove = Group::new(label);
4044
4045            // Ensure group exists in groups
4046            if !package.groups().contains(&group_to_remove) {
4047                return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into()));
4048            }
4049
4050            // Remove group if it is not referenced by at least one entry_point in active versions.
4051            let versions = package.versions();
4052            for entity_hash in versions.contract_hashes() {
4053                let entry_points = {
4054                    self.context
4055                        .get_casper_vm_v1_entry_point(Key::AddressableEntity(*entity_hash))?
4056                };
4057                for entry_point in entry_points.take_entry_points() {
4058                    match entry_point.access() {
4059                        EntryPointAccess::Public | EntryPointAccess::Template => {
4060                            continue;
4061                        }
4062                        EntryPointAccess::Groups(groups) => {
4063                            if groups.contains(&group_to_remove) {
4064                                return Ok(Err(addressable_entity::Error::GroupInUse.into()));
4065                            }
4066                        }
4067                    }
4068                }
4069            }
4070
4071            if !package.remove_group(&group_to_remove) {
4072                return Ok(Err(addressable_entity::Error::GroupInUse.into()));
4073            }
4074
4075            // Write updated package to the global state
4076            self.context.metered_write_gs_unsafe(package_key, package)?;
4077        } else {
4078            let mut contract_package = self
4079                .context
4080                .get_validated_contract_package(package_key.value())?;
4081            let group_to_remove = Group::new(label);
4082
4083            // Ensure group exists in groups
4084            if !contract_package.groups().contains(&group_to_remove) {
4085                return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into()));
4086            }
4087
4088            // Remove group if it is not referenced by at least one entry_point in active versions.
4089            for (_version, contract_hash) in contract_package.versions().iter() {
4090                let entry_points = {
4091                    self.context
4092                        .get_casper_vm_v1_entry_point(Key::contract_entity_key(
4093                            AddressableEntityHash::new(contract_hash.value()),
4094                        ))?
4095                };
4096                for entry_point in entry_points.take_entry_points() {
4097                    match entry_point.access() {
4098                        EntryPointAccess::Public | EntryPointAccess::Template => {
4099                            continue;
4100                        }
4101                        EntryPointAccess::Groups(groups) => {
4102                            if groups.contains(&group_to_remove) {
4103                                return Ok(Err(addressable_entity::Error::GroupInUse.into()));
4104                            }
4105                        }
4106                    }
4107                }
4108            }
4109
4110            if !contract_package.remove_group(&group_to_remove) {
4111                return Ok(Err(addressable_entity::Error::GroupInUse.into()));
4112            }
4113
4114            // Write updated package to the global state
4115            self.context.metered_write_gs_unsafe(
4116                ContractPackageHash::new(package_key.value()),
4117                contract_package,
4118            )?;
4119        }
4120        Ok(Ok(()))
4121    }
4122
4123    #[allow(clippy::too_many_arguments)]
4124    fn provision_contract_user_group_uref(
4125        &mut self,
4126        package_ptr: u32,
4127        package_size: u32,
4128        label_ptr: u32,
4129        label_size: u32,
4130        output_size_ptr: u32,
4131    ) -> Result<Result<(), ApiError>, ExecError> {
4132        let contract_package_hash = self.t_from_mem(package_ptr, package_size)?;
4133        let label: String = self.t_from_mem(label_ptr, label_size)?;
4134        let new_uref = if self.context.engine_config().enable_entity {
4135            let mut contract_package = self.context.get_validated_package(contract_package_hash)?;
4136            let groups = contract_package.groups_mut();
4137
4138            let group_label = Group::new(label);
4139
4140            // Ensure there are not too many urefs
4141            if groups.total_urefs() + 1 > addressable_entity::MAX_TOTAL_UREFS {
4142                return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
4143            }
4144
4145            // Ensure given group exists and does not exceed limits
4146            let group = match groups.get_mut(&group_label) {
4147                Some(group) if group.len() + 1 > addressable_entity::MAX_GROUPS as usize => {
4148                    // Ensures there are not too many groups to fit in amount of new urefs
4149                    return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
4150                }
4151                Some(group) => group,
4152                None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
4153            };
4154
4155            // Proceed with creating new URefs
4156            let new_uref = self.context.new_unit_uref()?;
4157            if !group.insert(new_uref) {
4158                return Ok(Err(addressable_entity::Error::URefAlreadyExists.into()));
4159            }
4160
4161            // Write updated package to the global state
4162            self.context
4163                .metered_write_gs_unsafe(contract_package_hash, contract_package)?;
4164            new_uref
4165        } else {
4166            let mut contract_package = self
4167                .context
4168                .get_validated_contract_package(contract_package_hash.value())?;
4169            let groups = contract_package.groups_mut();
4170
4171            let group_label = Group::new(label);
4172
4173            // Ensure there are not too many urefs
4174            if groups.total_urefs() + 1 > addressable_entity::MAX_TOTAL_UREFS {
4175                return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
4176            }
4177
4178            // Ensure given group exists and does not exceed limits
4179            let group = match groups.get_mut(&group_label) {
4180                Some(group) if group.len() + 1 > addressable_entity::MAX_GROUPS as usize => {
4181                    // Ensures there are not too many groups to fit in amount of new urefs
4182                    return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
4183                }
4184                Some(group) => group,
4185                None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
4186            };
4187
4188            // Proceed with creating new URefs
4189            let new_uref = self.context.new_unit_uref()?;
4190            if !group.insert(new_uref) {
4191                return Ok(Err(addressable_entity::Error::URefAlreadyExists.into()));
4192            }
4193
4194            // Write updated package to the global state
4195            self.context.metered_write_gs_unsafe(
4196                ContractPackageHash::new(contract_package_hash.value()),
4197                contract_package,
4198            )?;
4199            new_uref
4200        };
4201
4202        // check we can write to the host buffer
4203        if let Err(err) = self.check_host_buffer() {
4204            return Ok(Err(err));
4205        }
4206        // create CLValue for return value
4207        let new_uref_value = CLValue::from_t(new_uref)?;
4208        let value_size = new_uref_value.inner_bytes().len();
4209        // write return value to buffer
4210        if let Err(err) = self.write_host_buffer(new_uref_value) {
4211            return Ok(Err(err));
4212        }
4213        // Write return value size to output location
4214        let output_size_bytes = value_size.to_le_bytes(); // Wasm is little-endian
4215        if let Err(error) = self
4216            .try_get_memory()?
4217            .set(output_size_ptr, &output_size_bytes)
4218        {
4219            return Err(ExecError::Interpreter(error.into()));
4220        }
4221
4222        Ok(Ok(()))
4223    }
4224
4225    #[allow(clippy::too_many_arguments)]
4226    fn remove_contract_user_group_urefs(
4227        &mut self,
4228        package_ptr: u32,
4229        package_size: u32,
4230        label_ptr: u32,
4231        label_size: u32,
4232        urefs_ptr: u32,
4233        urefs_size: u32,
4234    ) -> Result<Result<(), ApiError>, ExecError> {
4235        let contract_package_hash: PackageHash = self.t_from_mem(package_ptr, package_size)?;
4236        let label: String = self.t_from_mem(label_ptr, label_size)?;
4237        let urefs: BTreeSet<URef> = self.t_from_mem(urefs_ptr, urefs_size)?;
4238
4239        if self.context.engine_config().enable_entity {
4240            let mut contract_package = self.context.get_validated_package(contract_package_hash)?;
4241
4242            let groups = contract_package.groups_mut();
4243            let group_label = Group::new(label);
4244
4245            let group = match groups.get_mut(&group_label) {
4246                Some(group) => group,
4247                None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
4248            };
4249
4250            if urefs.is_empty() {
4251                return Ok(Ok(()));
4252            }
4253
4254            for uref in urefs {
4255                if !group.remove(&uref) {
4256                    return Ok(Err(addressable_entity::Error::UnableToRemoveURef.into()));
4257                }
4258            }
4259            // Write updated package to the global state
4260            self.context
4261                .metered_write_gs_unsafe(contract_package_hash, contract_package)?;
4262        } else {
4263            let contract_package_hash = ContractPackageHash::new(contract_package_hash.value());
4264            let mut contract_package = self
4265                .context
4266                .get_validated_contract_package(contract_package_hash.value())?;
4267
4268            let groups = contract_package.groups_mut();
4269            let group_label = Group::new(label);
4270
4271            let group = match groups.get_mut(&group_label) {
4272                Some(group) => group,
4273                None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
4274            };
4275
4276            if urefs.is_empty() {
4277                return Ok(Ok(()));
4278            }
4279
4280            for uref in urefs {
4281                if !group.remove(&uref) {
4282                    return Ok(Err(addressable_entity::Error::UnableToRemoveURef.into()));
4283                }
4284            }
4285            // Write updated package to the global state
4286            self.context
4287                .metered_write_gs_unsafe(contract_package_hash, contract_package)?;
4288        }
4289
4290        Ok(Ok(()))
4291    }
4292
4293    /// Calculate gas cost for a host function
4294    fn charge_host_function_call<T>(
4295        &mut self,
4296        host_function: &HostFunction<T>,
4297        weights: T,
4298    ) -> Result<(), Trap>
4299    where
4300        T: AsRef<[HostFunctionCost]> + Copy,
4301    {
4302        let cost = host_function
4303            .calculate_gas_cost(weights)
4304            .ok_or(ExecError::GasLimit)?; // Overflowing gas calculation means gas limit was exceeded
4305        self.gas(cost)?;
4306        Ok(())
4307    }
4308
4309    /// Creates a dictionary
4310    fn new_dictionary(&mut self, output_size_ptr: u32) -> Result<Result<(), ApiError>, ExecError> {
4311        // check we can write to the host buffer
4312        if let Err(err) = self.check_host_buffer() {
4313            return Ok(Err(err));
4314        }
4315
4316        // Create new URef
4317        let new_uref = self.context.new_unit_uref()?;
4318
4319        // create CLValue for return value
4320        let new_uref_value = CLValue::from_t(new_uref)?;
4321        let value_size = new_uref_value.inner_bytes().len();
4322        // write return value to buffer
4323        if let Err(err) = self.write_host_buffer(new_uref_value) {
4324            return Ok(Err(err));
4325        }
4326        // Write return value size to output location
4327        let output_size_bytes = value_size.to_le_bytes(); // Wasm is little-endian
4328        if let Err(error) = self
4329            .try_get_memory()?
4330            .set(output_size_ptr, &output_size_bytes)
4331        {
4332            return Err(ExecError::Interpreter(error.into()));
4333        }
4334
4335        Ok(Ok(()))
4336    }
4337
4338    /// Reads the `value` under a `key` in a dictionary
4339    fn dictionary_get(
4340        &mut self,
4341        uref_ptr: u32,
4342        uref_size: u32,
4343        dictionary_item_key_bytes_ptr: u32,
4344        dictionary_item_key_bytes_size: u32,
4345        output_size_ptr: u32,
4346    ) -> Result<Result<(), ApiError>, Trap> {
4347        // check we can write to the host buffer
4348        if let Err(err) = self.check_host_buffer() {
4349            return Ok(Err(err));
4350        }
4351
4352        let uref: URef = self.t_from_mem(uref_ptr, uref_size)?;
4353        let dictionary_item_key = self.checked_memory_slice(
4354            dictionary_item_key_bytes_ptr as usize,
4355            dictionary_item_key_bytes_size as usize,
4356            |utf8_bytes| std::str::from_utf8(utf8_bytes).map(ToOwned::to_owned),
4357        )?;
4358
4359        let dictionary_item_key = if let Ok(item_key) = dictionary_item_key {
4360            item_key
4361        } else {
4362            return Ok(Err(ApiError::InvalidDictionaryItemKey));
4363        };
4364
4365        let cl_value = match self.context.dictionary_get(uref, &dictionary_item_key)? {
4366            Some(cl_value) => cl_value,
4367            None => return Ok(Err(ApiError::ValueNotFound)),
4368        };
4369
4370        let value_size: u32 = match cl_value.inner_bytes().len().try_into() {
4371            Ok(value) => value,
4372            Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
4373        };
4374
4375        if let Err(error) = self.write_host_buffer(cl_value) {
4376            return Ok(Err(error));
4377        }
4378
4379        let value_bytes = value_size.to_le_bytes(); // Wasm is little-endian
4380        if let Err(error) = self.try_get_memory()?.set(output_size_ptr, &value_bytes) {
4381            return Err(ExecError::Interpreter(error.into()).into());
4382        }
4383
4384        Ok(Ok(()))
4385    }
4386
4387    /// Reads the `value` under a `Key::Dictionary`.
4388    fn dictionary_read(
4389        &mut self,
4390        key_ptr: u32,
4391        key_size: u32,
4392        output_size_ptr: u32,
4393    ) -> Result<Result<(), ApiError>, Trap> {
4394        if !self.can_write_to_host_buffer() {
4395            // Exit early if the host buffer is already occupied
4396            return Ok(Err(ApiError::HostBufferFull));
4397        }
4398
4399        let dictionary_key = self.key_from_mem(key_ptr, key_size)?;
4400        let cl_value = match self.context.dictionary_read(dictionary_key)? {
4401            Some(cl_value) => cl_value,
4402            None => return Ok(Err(ApiError::ValueNotFound)),
4403        };
4404
4405        let value_size: u32 = match cl_value.inner_bytes().len().try_into() {
4406            Ok(value) => value,
4407            Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
4408        };
4409
4410        if let Err(error) = self.write_host_buffer(cl_value) {
4411            return Ok(Err(error));
4412        }
4413
4414        let value_bytes = value_size.to_le_bytes(); // Wasm is little-endian
4415        if let Err(error) = self.try_get_memory()?.set(output_size_ptr, &value_bytes) {
4416            return Err(ExecError::Interpreter(error.into()).into());
4417        }
4418
4419        Ok(Ok(()))
4420    }
4421
4422    /// Writes a `key`, `value` pair in a dictionary
4423    fn dictionary_put(
4424        &mut self,
4425        uref_ptr: u32,
4426        uref_size: u32,
4427        key_ptr: u32,
4428        key_size: u32,
4429        value_ptr: u32,
4430        value_size: u32,
4431    ) -> Result<Result<(), ApiError>, Trap> {
4432        let uref: URef = self.t_from_mem(uref_ptr, uref_size)?;
4433        let dictionary_item_key_bytes = {
4434            if (key_size as usize) > DICTIONARY_ITEM_KEY_MAX_LENGTH {
4435                return Ok(Err(ApiError::DictionaryItemKeyExceedsLength));
4436            }
4437            self.checked_memory_slice(key_ptr as usize, key_size as usize, |data| {
4438                std::str::from_utf8(data).map(ToOwned::to_owned)
4439            })?
4440        };
4441
4442        let dictionary_item_key = if let Ok(item_key) = dictionary_item_key_bytes {
4443            item_key
4444        } else {
4445            return Ok(Err(ApiError::InvalidDictionaryItemKey));
4446        };
4447        let cl_value = self.cl_value_from_mem(value_ptr, value_size)?;
4448        if let Err(e) = self
4449            .context
4450            .dictionary_put(uref, &dictionary_item_key, cl_value)
4451        {
4452            return Err(Trap::from(e));
4453        }
4454        Ok(Ok(()))
4455    }
4456
4457    /// Checks if immediate caller is a system contract or account.
4458    ///
4459    /// For cases where call stack is only the session code, then this method returns `true` if the
4460    /// caller is system, or `false` otherwise.
4461    fn is_system_immediate_caller(&self) -> Result<bool, ExecError> {
4462        let immediate_caller = match self.get_immediate_caller() {
4463            Some(call_stack_element) => call_stack_element,
4464            None => {
4465                // Immediate caller is assumed to exist at a time this check is run.
4466                return Ok(false);
4467            }
4468        };
4469
4470        match immediate_caller {
4471            Caller::Initiator { account_hash } => {
4472                // This case can happen during genesis where we're setting up purses for accounts.
4473                Ok(account_hash == &PublicKey::System.to_account_hash())
4474            }
4475            Caller::SmartContract { contract_hash, .. } => Ok(self
4476                .context
4477                .is_system_addressable_entity(&contract_hash.value())?),
4478            Caller::Entity { entity_addr, .. } => Ok(self
4479                .context
4480                .is_system_addressable_entity(&entity_addr.value())?),
4481        }
4482    }
4483
4484    fn load_authorization_keys(
4485        &mut self,
4486        len_ptr: u32,
4487        result_size_ptr: u32,
4488    ) -> Result<Result<(), ApiError>, Trap> {
4489        if !self.can_write_to_host_buffer() {
4490            // Exit early if the host buffer is already occupied
4491            return Ok(Err(ApiError::HostBufferFull));
4492        }
4493
4494        // A set of keys is converted into a vector so it can be written to a host buffer
4495        let authorization_keys = Vec::from_iter(self.context.authorization_keys().clone());
4496
4497        let total_keys: u32 = match authorization_keys.len().try_into() {
4498            Ok(value) => value,
4499            Err(_) => return Ok(Err(ApiError::OutOfMemory)),
4500        };
4501        let total_keys_bytes = total_keys.to_le_bytes();
4502        if let Err(error) = self.try_get_memory()?.set(len_ptr, &total_keys_bytes) {
4503            return Err(ExecError::Interpreter(error.into()).into());
4504        }
4505
4506        if total_keys == 0 {
4507            // No need to do anything else, we leave host buffer empty.
4508            return Ok(Ok(()));
4509        }
4510
4511        let authorization_keys = CLValue::from_t(authorization_keys).map_err(ExecError::CLValue)?;
4512
4513        let length: u32 = match authorization_keys.inner_bytes().len().try_into() {
4514            Ok(value) => value,
4515            Err(_) => return Ok(Err(ApiError::OutOfMemory)),
4516        };
4517        if let Err(error) = self.write_host_buffer(authorization_keys) {
4518            return Ok(Err(error));
4519        }
4520
4521        let length_bytes = length.to_le_bytes();
4522        if let Err(error) = self.try_get_memory()?.set(result_size_ptr, &length_bytes) {
4523            return Err(ExecError::Interpreter(error.into()).into());
4524        }
4525
4526        Ok(Ok(()))
4527    }
4528
4529    fn prune(&mut self, key: Key) {
4530        self.context.prune_gs_unsafe(key);
4531    }
4532
4533    pub(crate) fn migrate_contract_and_contract_package(
4534        &mut self,
4535        hash_addr: HashAddr,
4536    ) -> Result<AddressableEntity, ExecError> {
4537        let protocol_version = self.context.protocol_version();
4538        let contract = self.context.get_contract(ContractHash::new(hash_addr))?;
4539        let package_hash = contract.contract_package_hash();
4540        self.context
4541            .migrate_package(package_hash, protocol_version)?;
4542        let entity_hash = AddressableEntityHash::new(hash_addr);
4543        let key = Key::contract_entity_key(entity_hash);
4544        self.context.read_gs_typed(&key)
4545    }
4546
4547    fn add_message_topic(&mut self, topic_name: &str) -> Result<Result<(), ApiError>, ExecError> {
4548        let topic_hash = cryptography::blake2b(topic_name).into();
4549
4550        self.context
4551            .add_message_topic(topic_name, topic_hash)
4552            .map(|ret| ret.map_err(ApiError::from))
4553    }
4554
4555    fn emit_message(
4556        &mut self,
4557        topic_name: &str,
4558        message: MessagePayload,
4559    ) -> Result<Result<(), ApiError>, Trap> {
4560        let entity_addr = self.context.context_key_to_entity_addr()?;
4561
4562        let topic_name_hash = cryptography::blake2b(topic_name).into();
4563        let topic_key = Key::Message(MessageAddr::new_topic_addr(entity_addr, topic_name_hash));
4564
4565        // Check if the topic exists and get the summary.
4566        let Some(StoredValue::MessageTopic(prev_topic_summary)) =
4567            self.context.read_gs(&topic_key)?
4568        else {
4569            return Ok(Err(ApiError::MessageTopicNotRegistered));
4570        };
4571
4572        let current_blocktime = self.context.get_block_info().block_time();
4573        let topic_message_index = if prev_topic_summary.blocktime() != current_blocktime {
4574            for index in 1..prev_topic_summary.message_count() {
4575                self.context
4576                    .prune_gs_unsafe(Key::message(entity_addr, topic_name_hash, index));
4577            }
4578            0
4579        } else {
4580            prev_topic_summary.message_count()
4581        };
4582
4583        let block_message_index: u64 = match self
4584            .context
4585            .read_gs(&Key::BlockGlobal(BlockGlobalAddr::MessageCount))?
4586        {
4587            Some(stored_value) => {
4588                let (prev_block_time, prev_count): (BlockTime, u64) = CLValue::into_t(
4589                    CLValue::try_from(stored_value).map_err(ExecError::TypeMismatch)?,
4590                )
4591                .map_err(ExecError::CLValue)?;
4592                if prev_block_time == current_blocktime {
4593                    prev_count
4594                } else {
4595                    0
4596                }
4597            }
4598            None => 0,
4599        };
4600
4601        let Some(topic_message_count) = topic_message_index.checked_add(1) else {
4602            return Ok(Err(ApiError::MessageTopicFull));
4603        };
4604
4605        let Some(block_message_count) = block_message_index.checked_add(1) else {
4606            return Ok(Err(ApiError::MaxMessagesPerBlockExceeded));
4607        };
4608
4609        self.context.metered_emit_message(
4610            topic_key,
4611            current_blocktime,
4612            block_message_count,
4613            topic_message_count,
4614            Message::new(
4615                entity_addr,
4616                message,
4617                topic_name.to_string(),
4618                topic_name_hash,
4619                topic_message_index,
4620                block_message_index,
4621            ),
4622        )?;
4623        Ok(Ok(()))
4624    }
4625}
4626
4627#[cfg(feature = "test-support")]
4628fn dump_runtime_stack_info(instance: casper_wasmi::ModuleRef, max_stack_height: u32) {
4629    let globals = instance.globals();
4630    let Some(current_runtime_call_stack_height) = globals.last() else {
4631        return;
4632    };
4633
4634    if let RuntimeValue::I32(current_runtime_call_stack_height) =
4635        current_runtime_call_stack_height.get()
4636    {
4637        if current_runtime_call_stack_height > max_stack_height as i32 {
4638            eprintln!("runtime stack overflow, current={current_runtime_call_stack_height}, max={max_stack_height}");
4639        }
4640    };
4641}