drt_chain_vm/vm_hooks/vh_impl/
vh_debug_api.rs1use 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#[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 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); 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 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), _ => 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), _ => 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 {}