multiversx_chain_vm/vm_hooks/vh_handler/
vh_blockchain.rs

1use 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
10// The Go VM doesn't do it, but if we change that, we can enable it easily here too via this constant.
11const 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        // missing account/token identifier/nonce
221        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}