multiversx_chain_vm/host/
runtime.rs

1mod runtime_instance_call;
2mod runtime_instance_call_default;
3
4pub use runtime_instance_call::{RuntimeInstanceCall, RuntimeInstanceCallLambda};
5pub use runtime_instance_call_default::RuntimeInstanceCallLambdaDefault;
6
7use std::{
8    ops::Deref,
9    sync::{Arc, RwLock, Weak},
10};
11
12use multiversx_chain_vm_executor::{CompilationOptions, Executor};
13
14use crate::{
15    blockchain::VMConfigRef,
16    display_util::address_hex,
17    host::context::{TxContext, TxContextRef},
18};
19
20pub struct Runtime {
21    pub vm_ref: VMConfigRef,
22    executor: Box<dyn Executor + Send + Sync>,
23    context_cell: RwLock<Option<TxContextRef>>,
24}
25
26#[derive(Clone)]
27pub struct RuntimeRef(Arc<Runtime>);
28
29#[derive(Clone)]
30pub struct RuntimeWeakRef(Weak<Runtime>);
31
32impl Runtime {
33    pub fn new(vm_ref: VMConfigRef, executor: Box<dyn Executor + Send + Sync>) -> Self {
34        Runtime {
35            vm_ref,
36            executor,
37            context_cell: RwLock::new(None),
38        }
39    }
40
41    pub fn get_executor_context(&self) -> TxContextRef {
42        self.context_cell
43            .read()
44            .unwrap()
45            .clone()
46            .expect("no executor context configured")
47    }
48
49    fn set_executor_context(&self, value: Option<TxContextRef>) {
50        let mut cell_ref = self.context_cell.write().unwrap();
51        *cell_ref = value;
52    }
53}
54
55impl RuntimeRef {
56    pub fn new(vm_ref: VMConfigRef, executor: Box<dyn Executor + Send + Sync>) -> Self {
57        RuntimeRef(Arc::new(Runtime::new(vm_ref, executor)))
58    }
59
60    pub fn downgrade(&self) -> RuntimeWeakRef {
61        RuntimeWeakRef(Arc::downgrade(&self.0))
62    }
63
64    pub fn get_mut(&mut self) -> &mut Runtime {
65        Arc::get_mut(&mut self.0).expect(
66            "RuntimeRef cannot grant mutable access, because more than one strong reference exists",
67        )
68    }
69
70    /// Helpful for initializing a runtime that contain executors
71    /// that need to reference back to the runtime itself.
72    ///
73    /// The initializer function receives weak pointer references,
74    /// because circular strong references ensure a memory leak.
75    pub fn new_cyclic<F>(init_fn: F) -> RuntimeRef
76    where
77        F: FnOnce(RuntimeWeakRef) -> Runtime,
78    {
79        let runtime_arc = Arc::new_cyclic(|weak| init_fn(RuntimeWeakRef(weak.clone())));
80        RuntimeRef(runtime_arc)
81    }
82}
83
84impl Deref for RuntimeRef {
85    type Target = Runtime;
86
87    fn deref(&self) -> &Self::Target {
88        self.0.deref()
89    }
90}
91
92impl RuntimeWeakRef {
93    pub fn upgrade(&self) -> RuntimeRef {
94        RuntimeRef(
95            self.0
96                .upgrade()
97                .expect("RuntimeWeakRef points to a dropped reference"),
98        )
99    }
100}
101
102impl RuntimeRef {
103    /// Executes smart contract call using the given tx context, and the configured executor.
104    ///
105    /// It is possible to customize the specific instance call using the given lambda argument.
106    /// Default it is the `instance_call` function.
107    pub fn execute<F>(&self, tx_context: TxContext, call_lambda: F) -> TxContext
108    where
109        F: RuntimeInstanceCallLambda,
110    {
111        let func_name = tx_context.tx_input_box.func_name.clone();
112        let contract_code = get_contract_identifier(&tx_context);
113        let gas_limit = tx_context.input_ref().gas_limit;
114
115        let tx_context_ref = TxContextRef::new(Arc::new(tx_context));
116
117        self.set_executor_context(Some(tx_context_ref.clone()));
118
119        let compilation_options = CompilationOptions {
120            unmetered_locals: 0,
121            max_memory_grow: 0,
122            max_memory_grow_delta: 0,
123            opcode_trace: false,
124        };
125
126        let mut instance = self
127            .executor
128            .new_instance(contract_code.as_slice(), &compilation_options)
129            .expect("error instantiating executor instance");
130
131        self.set_executor_context(None);
132
133        call_lambda.call(RuntimeInstanceCall {
134            instance: &mut *instance,
135            func_name: func_name.as_str(),
136            gas_limit,
137            tx_context_ref: &tx_context_ref,
138        });
139
140        std::mem::drop(instance);
141
142        Arc::into_inner(tx_context_ref.0)
143            .expect("cannot extract final TxContext from stack because of lingering references")
144    }
145}
146
147fn get_contract_identifier(tx_context: &TxContext) -> Vec<u8> {
148    tx_context
149        .tx_cache
150        .with_account(&tx_context.tx_input_box.to, |account| {
151            account.contract_path.clone().unwrap_or_else(|| {
152                panic!(
153                    "Recipient account is not a smart contract {}",
154                    address_hex(&tx_context.tx_input_box.to)
155                )
156            })
157        })
158}