multiversx_chain_vm/vm_hooks/vh_handler/
vh_blockchain.rs1use crate::{
2 chain_core::builtin_func_names::*,
3 types::{EsdtLocalRole, EsdtLocalRoleFlags, RawHandle, VMAddress},
4 vm_hooks::VMHooksHandlerSource,
5 world_mock::{EsdtData, EsdtInstance},
6};
7use num_bigint::BigInt;
8use num_traits::Zero;
9
10const ESDT_TOKEN_DATA_FUNC_RESETS_VALUES: bool = false;
12const VM_BUILTIN_FUNCTION_NAMES: [&str; 16] = [
13 ESDT_LOCAL_MINT_FUNC_NAME,
14 ESDT_LOCAL_BURN_FUNC_NAME,
15 ESDT_MULTI_TRANSFER_FUNC_NAME,
16 ESDT_NFT_TRANSFER_FUNC_NAME,
17 ESDT_NFT_CREATE_FUNC_NAME,
18 ESDT_NFT_ADD_QUANTITY_FUNC_NAME,
19 ESDT_NFT_ADD_URI_FUNC_NAME,
20 ESDT_NFT_UPDATE_ATTRIBUTES_FUNC_NAME,
21 ESDT_NFT_BURN_FUNC_NAME,
22 ESDT_TRANSFER_FUNC_NAME,
23 CHANGE_OWNER_BUILTIN_FUNC_NAME,
24 CLAIM_DEVELOPER_REWARDS_FUNC_NAME,
25 SET_USERNAME_FUNC_NAME,
26 MIGRATE_USERNAME_FUNC_NAME,
27 DELETE_USERNAME_FUNC_NAME,
28 UPGRADE_CONTRACT_FUNC_NAME,
29];
30
31pub trait VMHooksBlockchain: VMHooksHandlerSource {
32 fn is_contract_address(&self, address_bytes: &[u8]) -> bool {
33 let address = VMAddress::from_slice(address_bytes);
34 &address == self.current_address()
35 }
36
37 fn managed_caller(&self, dest_handle: RawHandle) {
38 self.m_types_lock()
39 .mb_set(dest_handle, self.input_ref().from.to_vec());
40 }
41
42 fn managed_sc_address(&self, dest_handle: RawHandle) {
43 self.m_types_lock()
44 .mb_set(dest_handle, self.current_address().to_vec());
45 }
46
47 fn managed_owner_address(&self, dest_handle: RawHandle) {
48 self.m_types_lock().mb_set(
49 dest_handle,
50 self.current_account_data()
51 .contract_owner
52 .unwrap_or_else(|| panic!("contract owner address not set"))
53 .to_vec(),
54 );
55 }
56
57 fn get_shard_of_address(&self, address_bytes: &[u8]) -> i32 {
58 (address_bytes[address_bytes.len() - 1] % 3).into()
59 }
60
61 fn is_smart_contract(&self, address_bytes: &[u8]) -> bool {
62 VMAddress::from_slice(address_bytes).is_smart_contract_address()
63 }
64
65 fn load_balance(&self, address_bytes: &[u8], dest: RawHandle) {
66 assert!(
67 self.is_contract_address(address_bytes),
68 "get balance not yet implemented for accounts other than the contract itself"
69 );
70 self.m_types_lock()
71 .bi_overwrite(dest, self.current_account_data().egld_balance.into());
72 }
73
74 fn get_tx_hash(&self, dest: RawHandle) {
75 self.m_types_lock()
76 .mb_set(dest, self.input_ref().tx_hash.to_vec());
77 }
78
79 fn get_gas_left(&self) -> u64 {
80 self.input_ref().gas_limit
81 }
82
83 fn get_block_timestamp(&self) -> u64 {
84 self.get_current_block_info().block_timestamp
85 }
86
87 fn get_block_nonce(&self) -> u64 {
88 self.get_current_block_info().block_nonce
89 }
90
91 fn get_block_round(&self) -> u64 {
92 self.get_current_block_info().block_round
93 }
94
95 fn get_block_epoch(&self) -> u64 {
96 self.get_current_block_info().block_epoch
97 }
98
99 fn get_block_random_seed(&self, dest: RawHandle) {
100 self.m_types_lock().mb_set(
101 dest,
102 self.get_current_block_info().block_random_seed.to_vec(),
103 );
104 }
105
106 fn get_prev_block_timestamp(&self) -> u64 {
107 self.get_previous_block_info().block_timestamp
108 }
109
110 fn get_prev_block_nonce(&self) -> u64 {
111 self.get_previous_block_info().block_nonce
112 }
113
114 fn get_prev_block_round(&self) -> u64 {
115 self.get_previous_block_info().block_round
116 }
117
118 fn get_prev_block_epoch(&self) -> u64 {
119 self.get_previous_block_info().block_epoch
120 }
121
122 fn get_prev_block_random_seed(&self, dest: RawHandle) {
123 self.m_types_lock().mb_set(
124 dest,
125 self.get_previous_block_info().block_random_seed.to_vec(),
126 );
127 }
128
129 fn get_current_esdt_nft_nonce(&self, address_bytes: &[u8], token_id_bytes: &[u8]) -> u64 {
130 assert!(
131 self.is_contract_address(address_bytes),
132 "get_current_esdt_nft_nonce not yet implemented for accounts other than the contract itself"
133 );
134
135 self.current_account_data()
136 .esdt
137 .get_by_identifier_or_default(token_id_bytes)
138 .last_nonce
139 }
140
141 fn big_int_get_esdt_external_balance(
142 &self,
143 address_bytes: &[u8],
144 token_id_bytes: &[u8],
145 nonce: u64,
146 dest: RawHandle,
147 ) {
148 assert!(
149 self.is_contract_address(address_bytes),
150 "get_esdt_balance not yet implemented for accounts other than the contract itself"
151 );
152
153 let esdt_balance = self
154 .current_account_data()
155 .esdt
156 .get_esdt_balance(token_id_bytes, nonce);
157 self.m_types_lock().bi_overwrite(dest, esdt_balance.into());
158 }
159
160 fn managed_get_code_metadata(&self, address_handle: i32, response_handle: i32) {
161 let address = VMAddress::from_slice(self.m_types_lock().mb_get(address_handle));
162 let Some(data) = self.account_data(&address) else {
163 self.vm_error(&format!(
164 "account not found: {}",
165 hex::encode(address.as_bytes())
166 ))
167 };
168 let code_metadata_bytes = data.code_metadata.to_byte_array();
169 self.m_types_lock()
170 .mb_set(response_handle, code_metadata_bytes.to_vec())
171 }
172
173 fn managed_is_builtin_function(&self, function_name_handle: i32) -> bool {
174 VM_BUILTIN_FUNCTION_NAMES.contains(
175 &self
176 .m_types_lock()
177 .mb_to_function_name(function_name_handle)
178 .as_str(),
179 )
180 }
181
182 #[allow(clippy::too_many_arguments)]
183 fn managed_get_esdt_token_data(
184 &self,
185 address_handle: RawHandle,
186 token_id_handle: RawHandle,
187 nonce: u64,
188 value_handle: RawHandle,
189 properties_handle: RawHandle,
190 hash_handle: RawHandle,
191 name_handle: RawHandle,
192 attributes_handle: RawHandle,
193 creator_handle: RawHandle,
194 royalties_handle: RawHandle,
195 uris_handle: RawHandle,
196 ) {
197 let address = VMAddress::from_slice(self.m_types_lock().mb_get(address_handle));
198 let token_id_bytes = self.m_types_lock().mb_get(token_id_handle).to_vec();
199
200 if let Some(account) = self.account_data(&address) {
201 if let Some(esdt_data) = account.esdt.get_by_identifier(token_id_bytes.as_slice()) {
202 if let Some(instance) = esdt_data.instances.get_by_nonce(nonce) {
203 self.set_esdt_data_values(
204 esdt_data,
205 instance,
206 value_handle,
207 properties_handle,
208 hash_handle,
209 name_handle,
210 attributes_handle,
211 creator_handle,
212 royalties_handle,
213 uris_handle,
214 );
215 return;
216 }
217 }
218 }
219
220 self.reset_esdt_data_values(
222 value_handle,
223 properties_handle,
224 hash_handle,
225 name_handle,
226 attributes_handle,
227 creator_handle,
228 royalties_handle,
229 uris_handle,
230 );
231 }
232
233 fn managed_get_back_transfers(
234 &self,
235 esdt_transfer_value_handle: RawHandle,
236 call_value_handle: RawHandle,
237 ) {
238 let back_transfers = self.back_transfers_lock();
239 let mut m_types = self.m_types_lock();
240 m_types.bi_overwrite(call_value_handle, back_transfers.call_value.clone().into());
241 m_types.mb_set_vec_of_esdt_payments(
242 esdt_transfer_value_handle,
243 &back_transfers.esdt_transfers,
244 );
245 }
246
247 fn check_esdt_frozen(
248 &self,
249 address_handle: RawHandle,
250 token_id_handle: RawHandle,
251 _nonce: u64,
252 ) -> bool {
253 let address = VMAddress::from_slice(self.m_types_lock().mb_get(address_handle));
254 let token_id_bytes = self.m_types_lock().mb_get(token_id_handle).to_vec();
255 if let Some(account) = self.account_data(&address) {
256 if let Some(esdt_data) = account.esdt.get_by_identifier(token_id_bytes.as_slice()) {
257 return esdt_data.frozen;
258 }
259 }
260
261 false
262 }
263
264 fn get_esdt_local_roles_bits(&self, token_id_handle: RawHandle) -> u64 {
265 let token_id_bytes = self.m_types_lock().mb_get(token_id_handle).to_vec();
266 let account = self.current_account_data();
267 let mut result = EsdtLocalRoleFlags::NONE;
268 if let Some(esdt_data) = account.esdt.get_by_identifier(token_id_bytes.as_slice()) {
269 for role_name in esdt_data.roles.get() {
270 result |= EsdtLocalRole::from(role_name.as_slice()).to_flag();
271 }
272 }
273 result.bits()
274 }
275
276 #[allow(clippy::too_many_arguments)]
277 fn set_esdt_data_values(
278 &self,
279 esdt_data: &EsdtData,
280 instance: &EsdtInstance,
281 value_handle: RawHandle,
282 properties_handle: RawHandle,
283 hash_handle: RawHandle,
284 name_handle: RawHandle,
285 attributes_handle: RawHandle,
286 creator_handle: RawHandle,
287 royalties_handle: RawHandle,
288 uris_handle: RawHandle,
289 ) {
290 let mut m_types = self.m_types_lock();
291 m_types.bi_overwrite(value_handle, instance.balance.clone().into());
292 if esdt_data.frozen {
293 m_types.mb_set(properties_handle, vec![1, 0]);
294 } else {
295 m_types.mb_set(properties_handle, vec![0, 0]);
296 }
297 m_types.mb_set(
298 hash_handle,
299 instance.metadata.hash.clone().unwrap_or_default(),
300 );
301 m_types.mb_set(name_handle, instance.metadata.name.clone());
302 m_types.mb_set(attributes_handle, instance.metadata.attributes.clone());
303 if let Some(creator) = &instance.metadata.creator {
304 m_types.mb_set(creator_handle, creator.to_vec());
305 } else {
306 m_types.mb_set(creator_handle, vec![0u8; 32]);
307 };
308 m_types.bi_overwrite(
309 royalties_handle,
310 num_bigint::BigInt::from(instance.metadata.royalties),
311 );
312 m_types.mb_set_vec_of_bytes(uris_handle, instance.metadata.uri.clone());
313 }
314
315 #[allow(clippy::too_many_arguments)]
316 fn reset_esdt_data_values(
317 &self,
318 value_handle: RawHandle,
319 properties_handle: RawHandle,
320 hash_handle: RawHandle,
321 name_handle: RawHandle,
322 attributes_handle: RawHandle,
323 creator_handle: RawHandle,
324 royalties_handle: RawHandle,
325 uris_handle: RawHandle,
326 ) {
327 if ESDT_TOKEN_DATA_FUNC_RESETS_VALUES {
328 let mut m_types = self.m_types_lock();
329 m_types.bi_overwrite(value_handle, BigInt::zero());
330 m_types.mb_set(properties_handle, vec![0, 0]);
331 m_types.mb_set(hash_handle, vec![]);
332 m_types.mb_set(name_handle, vec![]);
333 m_types.mb_set(attributes_handle, vec![]);
334 m_types.mb_set(creator_handle, vec![0u8; 32]);
335 m_types.bi_overwrite(royalties_handle, BigInt::zero());
336 m_types.bi_overwrite(uris_handle, BigInt::zero());
337 }
338 }
339}