drt_chain_vm/vm_hooks/vh_impl/
vh_debug_api.rs

1use std::sync::{Arc, MutexGuard};
2
3use drt_chain_vm_executor::BreakpointValue;
4
5use crate::{
6    tx_execution::execute_current_tx_context_input,
7    tx_mock::{
8        async_call_tx_input, AsyncCallTxData, BackTransfers, BlockchainUpdate, CallType, TxCache,
9        TxContext, TxFunctionName, TxInput, TxManagedTypes, TxPanic, TxResult,
10    },
11    types::{VMAddress, VMCodeMetadata},
12    vm_err_msg,
13    vm_hooks::{
14        VMHooksBigFloat, VMHooksBigInt, VMHooksBlockchain, VMHooksCallValue, VMHooksCrypto,
15        VMHooksEndpointArgument, VMHooksEndpointFinish, VMHooksError, VMHooksErrorManaged,
16        VMHooksHandler, VMHooksHandlerSource, VMHooksLog, VMHooksManagedBuffer, VMHooksManagedMap,
17        VMHooksManagedTypes, VMHooksSend, VMHooksStorageRead, VMHooksStorageWrite,
18    },
19    world_mock::{reserved::STORAGE_RESERVED_PREFIX, AccountData, BlockInfo},
20};
21
22/// A simple wrapper around a managed type container RefCell.
23///
24/// Implements `VMHooksManagedTypes` and thus can be used as a basis of a minimal static API.
25#[derive(Debug)]
26pub struct DebugApiVMHooksHandler(Arc<TxContext>);
27
28impl DebugApiVMHooksHandler {
29    pub fn new(tx_context_arc: Arc<TxContext>) -> Self {
30        DebugApiVMHooksHandler(tx_context_arc)
31    }
32}
33
34impl VMHooksHandlerSource for DebugApiVMHooksHandler {
35    fn m_types_lock(&self) -> MutexGuard<TxManagedTypes> {
36        self.0.m_types_lock()
37    }
38
39    fn halt_with_error(&self, status: u64, message: &str) -> ! {
40        *self.0.result_lock() = TxResult::from_panic_obj(&TxPanic::new(status, message));
41        let breakpoint = match status {
42            4 => BreakpointValue::SignalError,
43            _ => BreakpointValue::ExecutionFailed,
44        };
45        std::panic::panic_any(breakpoint);
46    }
47
48    fn input_ref(&self) -> &TxInput {
49        self.0.input_ref()
50    }
51
52    fn random_next_bytes(&self, length: usize) -> Vec<u8> {
53        self.0.rng_lock().next_bytes(length)
54    }
55
56    fn result_lock(&self) -> MutexGuard<TxResult> {
57        self.0.result_lock()
58    }
59
60    fn storage_read_any_address(&self, address: &VMAddress, key: &[u8]) -> Vec<u8> {
61        self.0.with_account_mut(address, |account| {
62            account.storage.get(key).cloned().unwrap_or_default()
63        })
64    }
65
66    fn storage_write(&self, key: &[u8], value: &[u8]) {
67        self.check_reserved_key(key);
68
69        self.0.with_contract_account_mut(|account| {
70            account.storage.insert(key.to_vec(), value.to_vec());
71        });
72    }
73
74    fn get_previous_block_info(&self) -> &BlockInfo {
75        &self.0.blockchain_ref().previous_block_info
76    }
77
78    fn get_current_block_info(&self) -> &BlockInfo {
79        &self.0.blockchain_ref().current_block_info
80    }
81
82    fn back_transfers_lock(&self) -> MutexGuard<BackTransfers> {
83        self.0.back_transfers_lock()
84    }
85
86    fn account_data(&self, address: &VMAddress) -> Option<AccountData> {
87        self.0
88            .with_account_or_else(address, |account| Some(account.clone()), || None)
89    }
90
91    fn account_code(&self, address: &VMAddress) -> Vec<u8> {
92        self.0
93            .blockchain_cache()
94            .with_account(address, |account| account.contract_path.clone())
95            .unwrap_or_else(|| panic!("Account is not a smart contract, it has no code"))
96    }
97
98    fn perform_async_call(
99        &self,
100        to: VMAddress,
101        rewa_value: num_bigint::BigUint,
102        func_name: TxFunctionName,
103        arguments: Vec<Vec<u8>>,
104    ) -> ! {
105        let async_call_data = self.create_async_call_data(to, rewa_value, func_name, arguments);
106        // the cell is no longer needed, since we end in a panic
107        let mut tx_result = self.result_lock();
108        tx_result.all_calls.push(async_call_data.clone());
109        tx_result.pending_calls.async_call = Some(async_call_data);
110        drop(tx_result); // this avoid to poison the mutex
111        std::panic::panic_any(BreakpointValue::AsyncCall);
112    }
113
114    fn perform_execute_on_dest_context(
115        &self,
116        to: VMAddress,
117        rewa_value: num_bigint::BigUint,
118        func_name: TxFunctionName,
119        arguments: Vec<Vec<u8>>,
120    ) -> Vec<Vec<u8>> {
121        let async_call_data = self.create_async_call_data(to, rewa_value, func_name, arguments);
122        let tx_input = async_call_tx_input(&async_call_data, CallType::ExecuteOnDestContext);
123        let tx_cache = TxCache::new(self.0.blockchain_cache_arc());
124        let (tx_result, blockchain_updates) = self.0.vm_ref.execute_builtin_function_or_default(
125            tx_input,
126            tx_cache,
127            execute_current_tx_context_input,
128        );
129
130        if tx_result.result_status == 0 {
131            self.sync_call_post_processing(tx_result, blockchain_updates)
132        } else {
133            // also kill current execution
134            self.halt_with_error(tx_result.result_status, &tx_result.result_message)
135        }
136    }
137
138    fn perform_deploy(
139        &self,
140        rewa_value: num_bigint::BigUint,
141        contract_code: Vec<u8>,
142        code_metadata: VMCodeMetadata,
143        args: Vec<Vec<u8>>,
144    ) -> (VMAddress, Vec<Vec<u8>>) {
145        let contract_address = self.current_address();
146        let tx_hash = self.tx_hash();
147        let tx_input = TxInput {
148            from: contract_address.clone(),
149            to: VMAddress::zero(),
150            rewa_value,
151            dcdt_values: Vec::new(),
152            func_name: TxFunctionName::EMPTY,
153            args,
154            gas_limit: 1000,
155            gas_price: 0,
156            tx_hash,
157            ..Default::default()
158        };
159
160        let tx_cache = TxCache::new(self.0.blockchain_cache_arc());
161        tx_cache.increase_acount_nonce(contract_address);
162        let (tx_result, new_address, blockchain_updates) = self.0.vm_ref.deploy_contract(
163            tx_input,
164            contract_code,
165            code_metadata,
166            tx_cache,
167            execute_current_tx_context_input,
168        );
169
170        match tx_result.result_status {
171            0 => (
172                new_address,
173                self.sync_call_post_processing(tx_result, blockchain_updates),
174            ),
175            10 => self.vm_error(&tx_result.result_message), // TODO: not sure it's the right condition, it catches insufficient funds
176            _ => self.vm_error(vm_err_msg::ERROR_SIGNALLED_BY_SMARTCONTRACT),
177        }
178    }
179
180    fn perform_transfer_execute(
181        &self,
182        to: VMAddress,
183        rewa_value: num_bigint::BigUint,
184        func_name: TxFunctionName,
185        arguments: Vec<Vec<u8>>,
186    ) {
187        let async_call_data = self.create_async_call_data(to, rewa_value, func_name, arguments);
188        let mut tx_input = async_call_tx_input(&async_call_data, CallType::TransferExecute);
189        if self.is_back_transfer(&tx_input) {
190            tx_input.call_type = CallType::BackTransfer;
191        }
192
193        let tx_cache = TxCache::new(self.0.blockchain_cache_arc());
194        let (tx_result, blockchain_updates) = self.0.vm_ref.execute_builtin_function_or_default(
195            tx_input,
196            tx_cache,
197            execute_current_tx_context_input,
198        );
199
200        match tx_result.result_status {
201            0 => {
202                self.0.result_lock().all_calls.push(async_call_data);
203
204                let _ = self.sync_call_post_processing(tx_result, blockchain_updates);
205            },
206            10 => self.vm_error(&tx_result.result_message), // TODO: not sure it's the right condition, it catches insufficient funds
207            _ => self.vm_error(vm_err_msg::ERROR_SIGNALLED_BY_SMARTCONTRACT),
208        }
209    }
210}
211
212impl DebugApiVMHooksHandler {
213    fn create_async_call_data(
214        &self,
215        to: VMAddress,
216        rewa_value: num_bigint::BigUint,
217        func_name: TxFunctionName,
218        arguments: Vec<Vec<u8>>,
219    ) -> AsyncCallTxData {
220        let contract_address = &self.0.input_ref().to;
221        let tx_hash = self.tx_hash();
222        AsyncCallTxData {
223            from: contract_address.clone(),
224            to,
225            call_value: rewa_value,
226            endpoint_name: func_name,
227            arguments,
228            tx_hash,
229        }
230    }
231
232    fn sync_call_post_processing(
233        &self,
234        tx_result: TxResult,
235        blockchain_updates: BlockchainUpdate,
236    ) -> Vec<Vec<u8>> {
237        self.0.blockchain_cache().commit_updates(blockchain_updates);
238
239        self.0.result_lock().merge_after_sync_call(&tx_result);
240
241        let contract_address = &self.0.input_ref().to;
242        let builtin_functions = &self.0.vm_ref.builtin_functions;
243        self.back_transfers_lock()
244            .new_from_result(contract_address, &tx_result, builtin_functions);
245
246        tx_result.result_values
247    }
248
249    fn check_reserved_key(&self, key: &[u8]) {
250        if key.starts_with(STORAGE_RESERVED_PREFIX) {
251            self.vm_error("cannot write to storage under reserved key");
252        }
253    }
254
255    fn is_back_transfer(&self, tx_input: &TxInput) -> bool {
256        let caller_address = &self.0.input_ref().from;
257        if !caller_address.is_smart_contract_address() {
258            return false;
259        }
260
261        let builtin_functions = &self.0.vm_ref.builtin_functions;
262        let token_transfers = builtin_functions.extract_token_transfers(tx_input);
263        &token_transfers.real_recipient == caller_address
264    }
265}
266
267impl VMHooksBigInt for DebugApiVMHooksHandler {}
268impl VMHooksManagedBuffer for DebugApiVMHooksHandler {}
269impl VMHooksManagedMap for DebugApiVMHooksHandler {}
270impl VMHooksBigFloat for DebugApiVMHooksHandler {}
271impl VMHooksManagedTypes for DebugApiVMHooksHandler {}
272
273impl VMHooksCallValue for DebugApiVMHooksHandler {}
274impl VMHooksEndpointArgument for DebugApiVMHooksHandler {}
275impl VMHooksEndpointFinish for DebugApiVMHooksHandler {}
276impl VMHooksError for DebugApiVMHooksHandler {}
277impl VMHooksErrorManaged for DebugApiVMHooksHandler {}
278impl VMHooksStorageRead for DebugApiVMHooksHandler {}
279impl VMHooksStorageWrite for DebugApiVMHooksHandler {}
280impl VMHooksCrypto for DebugApiVMHooksHandler {}
281impl VMHooksBlockchain for DebugApiVMHooksHandler {}
282impl VMHooksLog for DebugApiVMHooksHandler {}
283impl VMHooksSend for DebugApiVMHooksHandler {}
284
285impl VMHooksHandler for DebugApiVMHooksHandler {}