Skip to main content

multiversx_sc_scenario/executor/debug/
contract_debug_instance.rs

1use std::rc::Rc;
2
3use multiversx_chain_vm::host::{
4    context::{TxContextRef, TxFunctionName, TxPanic},
5    runtime::RuntimeInstanceCall,
6};
7use multiversx_chain_vm_executor::{ExecutorError, Instance, InstanceCallResult};
8use multiversx_sc::chain_core::types::ReturnCode;
9
10use super::{
11    ContractContainer, ContractContainerRef, ContractDebugStack, StaticVarData, catch_tx_panic,
12};
13
14/// Used as a flag to check the instance under lambda calls.
15/// Since it is an invalid function name, any other instance should reject it.
16const FUNC_CONTEXT_PUSH: &str = "<ContractDebugInstance-PushContext>";
17const FUNC_CONTEXT_POP: &str = "<ContractDebugInstance-PopContext>";
18const DEBUG_GAS_LIMIT: u64 = 0;
19
20#[derive(Clone, Debug)]
21pub struct ContractDebugInstance {
22    pub tx_context_ref: TxContextRef,
23    pub contract_container_ref: ContractContainerRef,
24    pub static_var_ref: Rc<StaticVarData>,
25}
26
27impl ContractDebugInstance {
28    pub fn new(tx_context_ref: TxContextRef, contract_container: ContractContainerRef) -> Self {
29        ContractDebugInstance {
30            tx_context_ref,
31            contract_container_ref: contract_container,
32            static_var_ref: Default::default(),
33        }
34    }
35
36    /// Dummy instance for tests where no proper context is created on stack.
37    pub fn dummy() -> Self {
38        ContractDebugInstance {
39            tx_context_ref: TxContextRef::dummy(),
40            contract_container_ref: ContractContainerRef::new(ContractContainer::dummy()),
41            static_var_ref: Default::default(),
42        }
43    }
44
45    pub(super) fn wrap_lambda_call<F>(
46        panic_message_flag: bool,
47        instance_call: RuntimeInstanceCall,
48        f: F,
49    ) where
50        F: FnOnce(),
51    {
52        assert!(
53            instance_call.instance.has_function(FUNC_CONTEXT_PUSH),
54            "lambda call is not running on top of a DebugSCInstance instance"
55        );
56
57        let _ = instance_call
58            .instance
59            .call(FUNC_CONTEXT_PUSH, DEBUG_GAS_LIMIT);
60
61        let result = catch_tx_panic(panic_message_flag, || {
62            f();
63            Ok(())
64        });
65
66        if let Err(tx_panic) = result {
67            ContractDebugStack::static_peek()
68                .tx_context_ref
69                .replace_tx_result_with_error(tx_panic);
70        }
71
72        let _ = instance_call
73            .instance
74            .call(FUNC_CONTEXT_POP, DEBUG_GAS_LIMIT);
75    }
76
77    fn call_endpoint(&self, func_name: &str) {
78        let tx_func_name = TxFunctionName::from(func_name);
79
80        ContractDebugStack::static_push(self.clone());
81
82        let result = catch_tx_panic(self.contract_container_ref.0.panic_message, || {
83            let call_successful = self.contract_container_ref.0.call(&tx_func_name);
84            if call_successful {
85                Ok(())
86            } else {
87                Err(TxPanic::new(
88                    ReturnCode::FunctionNotFound,
89                    "invalid function (not found)",
90                ))
91            }
92        });
93
94        if let Err(tx_panic) = result {
95            self.tx_context_ref
96                .clone()
97                .replace_tx_result_with_error(tx_panic);
98        }
99
100        ContractDebugStack::static_pop();
101    }
102}
103
104impl Instance for ContractDebugInstance {
105    fn call(&mut self, func_name: &str, _points_limit: u64) -> InstanceCallResult {
106        match func_name {
107            FUNC_CONTEXT_PUSH => {
108                ContractDebugStack::static_push(self.clone());
109            }
110            FUNC_CONTEXT_POP => {
111                ContractDebugStack::static_pop();
112            }
113            _ => self.call_endpoint(func_name),
114        }
115        InstanceCallResult::Ok
116    }
117
118    fn check_signatures(&self) -> bool {
119        true
120    }
121
122    fn has_function(&self, func_name: &str) -> bool {
123        match func_name {
124            FUNC_CONTEXT_PUSH => true,
125            FUNC_CONTEXT_POP => true,
126            _ => self.contract_container_ref.has_function(func_name),
127        }
128    }
129
130    fn get_exported_function_names(&self) -> Vec<String> {
131        panic!("ContractDebugInstance get_exported_function_names not yet supported")
132    }
133
134    fn get_points_used(&mut self) -> Result<u64, ExecutorError> {
135        Ok(0)
136    }
137
138    fn reset(&self) -> Result<(), ExecutorError> {
139        panic!("ContractDebugInstance reset not supported")
140    }
141
142    fn cache(&self) -> Result<Vec<u8>, ExecutorError> {
143        panic!("ContractDebugInstance cache not supported")
144    }
145}