multiversx_chain_vm/host/runtime/
runtime_instance_call_default.rs

1use multiversx_chain_core::types::ReturnCode;
2use multiversx_chain_vm_executor::{BreakpointValue, InstanceCallResult, VMHooksEarlyExit};
3
4use crate::{
5    host::{
6        context::{GasUsed, TxFunctionName, TxResult},
7        vm_hooks::vh_early_exit::ASYNC_CALL_EARLY_EXIT_CODE,
8    },
9    vm_err_msg,
10};
11
12use super::{RuntimeInstanceCall, RuntimeInstanceCallLambda};
13
14/// Default implementation of `RuntimeInstanceCallLambda`.
15///
16/// Simply calls the instance as expected.
17pub struct RuntimeInstanceCallLambdaDefault;
18
19impl RuntimeInstanceCallLambda for RuntimeInstanceCallLambdaDefault {
20    fn call(self, instance_call: RuntimeInstanceCall<'_>) {
21        default_instance_call(instance_call);
22    }
23
24    fn override_function_name(&self) -> Option<TxFunctionName> {
25        None
26    }
27}
28
29fn default_instance_call(instance_call: RuntimeInstanceCall<'_>) {
30    if !instance_call.instance.has_function(instance_call.func_name) {
31        *instance_call.tx_context_ref.result_lock() = TxResult::from_function_not_found();
32        return;
33    }
34
35    let result = instance_call
36        .instance
37        .call(instance_call.func_name, instance_call.gas_limit);
38    let mut tx_result_ref = instance_call.tx_context_ref.result_lock();
39    if let Some(error_tx_result) = instance_call_error_result(result) {
40        *tx_result_ref = error_tx_result;
41    }
42
43    if tx_result_ref.result_status.is_success() {
44        let gas_used = instance_call
45            .instance
46            .get_points_used()
47            .expect("error retrieving gas used");
48        tx_result_ref.gas_used = GasUsed::SomeGas(gas_used);
49    } else {
50        tx_result_ref.gas_used =
51            GasUsed::AllGas(instance_call.tx_context_ref.tx_input_box.gas_limit);
52    }
53}
54
55fn instance_call_error_result(call_result: InstanceCallResult) -> Option<TxResult> {
56    match call_result {
57        InstanceCallResult::Ok => None,
58        InstanceCallResult::FunctionNotFound => Some(TxResult::from_function_not_found()),
59        InstanceCallResult::RuntimeError(error) => Some(TxResult::from_vm_error(error.to_string())),
60        InstanceCallResult::VMHooksEarlyExit(vm_hooks_early_exit) => {
61            vm_hooks_early_exit_result(vm_hooks_early_exit)
62        }
63        InstanceCallResult::Breakpoint(BreakpointValue::None) => {
64            Some(TxResult::from_vm_error("invalid breakpoint".to_string()))
65        }
66        InstanceCallResult::Breakpoint(BreakpointValue::OutOfGas) => Some(TxResult::from_error(
67            ReturnCode::OutOfGas,
68            vm_err_msg::NOT_ENOUGH_GAS,
69        )),
70        InstanceCallResult::Breakpoint(BreakpointValue::MemoryLimit) => {
71            Some(TxResult::from_vm_error("memory limit".to_string()))
72        }
73    }
74}
75
76fn vm_hooks_early_exit_result(vm_hooks_early_exit: VMHooksEarlyExit) -> Option<TxResult> {
77    if vm_hooks_early_exit.code == ASYNC_CALL_EARLY_EXIT_CODE {
78        None
79    } else {
80        Some(TxResult::from_error(
81            ReturnCode::from_u64(vm_hooks_early_exit.code).expect("invalid return code"),
82            vm_hooks_early_exit.message.into_owned(),
83        ))
84    }
85}