diff --git a/contracts/examples/multisig/interact-rs/.gitignore b/contracts/examples/multisig/interact-rs/.gitignore index ea1b920080..0d3b55f264 100644 --- a/contracts/examples/multisig/interact-rs/.gitignore +++ b/contracts/examples/multisig/interact-rs/.gitignore @@ -5,4 +5,4 @@ state.toml # Trace file of interactor tooling -interactor_trace.scen.json +interactor*.scen.json diff --git a/contracts/examples/multisig/scenarios/interactor_trace.scen.json b/contracts/examples/multisig/scenarios/interactor_wegld.scen.json similarity index 100% rename from contracts/examples/multisig/scenarios/interactor_trace.scen.json rename to contracts/examples/multisig/scenarios/interactor_wegld.scen.json diff --git a/contracts/examples/multisig/tests/multisig_scenario_go_test.rs b/contracts/examples/multisig/tests/multisig_scenario_go_test.rs index 5335581336..3983c7a619 100644 --- a/contracts/examples/multisig/tests/multisig_scenario_go_test.rs +++ b/contracts/examples/multisig/tests/multisig_scenario_go_test.rs @@ -55,8 +55,14 @@ fn deploy_duplicate_bm_go() { } #[test] -fn interactor_trace_go() { - world().run("scenarios/interactor_trace.scen.json"); +#[ignore = "system SC not yet implemented"] +fn interactor_nft_go() { + world().run("scenarios/interactor_nft.scen.json"); +} + +#[test] +fn interactor_wegld_go() { + world().run("scenarios/interactor_wegld.scen.json"); } #[test] diff --git a/contracts/examples/multisig/tests/multisig_scenario_rs_test.rs b/contracts/examples/multisig/tests/multisig_scenario_rs_test.rs index 1b78711bb0..3124ac2e89 100644 --- a/contracts/examples/multisig/tests/multisig_scenario_rs_test.rs +++ b/contracts/examples/multisig/tests/multisig_scenario_rs_test.rs @@ -82,8 +82,13 @@ fn deploy_duplicate_bm_rs() { } #[test] -fn interactor_trace_rs() { - world().run("scenarios/interactor_trace.scen.json"); +fn interactor_nft_rs() { + world().run("scenarios/interactor_nft.scen.json"); +} + +#[test] +fn interactor_wegld_rs() { + world().run("scenarios/interactor_wegld.scen.json"); } #[test] diff --git a/framework/scenario/src/scenario/run_vm/set_state.rs b/framework/scenario/src/scenario/run_vm/set_state.rs index 2f8239793c..cd69dc2893 100644 --- a/framework/scenario/src/scenario/run_vm/set_state.rs +++ b/framework/scenario/src/scenario/run_vm/set_state.rs @@ -76,6 +76,9 @@ fn execute(state: &mut BlockchainMock, set_state_step: &SetStateStep) { new_address.new_address.to_vm_address(), ) } + for new_token_identifier in set_state_step.new_token_identifiers.iter().cloned() { + state.put_new_token_identifier(new_token_identifier) + } if let Some(block_info_obj) = &*set_state_step.previous_block_info { update_block_info(&mut state.previous_block_info, block_info_obj); } diff --git a/vm/Cargo.toml b/vm/Cargo.toml index f76841b320..0790b01343 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -26,6 +26,7 @@ rand = "0.8.5" rand_seeder = "0.2.2" ed25519-dalek = "1.0.1" itertools = "0.10.3" +hex-literal = "0.3.1" bitflags = "1.3.2" [dependencies.multiversx-chain-vm-executor] diff --git a/vm/src/tx_execution/exec_call.rs b/vm/src/tx_execution/exec_call.rs index da326cec62..476924560a 100644 --- a/vm/src/tx_execution/exec_call.rs +++ b/vm/src/tx_execution/exec_call.rs @@ -10,7 +10,7 @@ use num_bigint::BigUint; use num_traits::Zero; use std::{collections::HashMap, rc::Rc}; -use super::{execute_builtin_function_or_default, execute_tx_context}; +use super::{execute_builtin_function_or_default, execute_tx_context, is_system_sc_address}; pub fn execute_sc_query(tx_input: TxInput, state: BlockchainMock) -> (TxResult, BlockchainMock) { let state_rc = Rc::new(state); @@ -21,7 +21,9 @@ pub fn execute_sc_query(tx_input: TxInput, state: BlockchainMock) -> (TxResult, } pub fn execute_sc_call(tx_input: TxInput, mut state: BlockchainMock) -> (TxResult, BlockchainMock) { - state.subtract_tx_gas(&tx_input.from, tx_input.gas_limit, tx_input.gas_price); + if !is_system_sc_address(&tx_input.from) { + state.subtract_tx_gas(&tx_input.from, tx_input.gas_limit, tx_input.gas_price); + } let state_rc = Rc::new(state); let tx_cache = TxCache::new(state_rc.clone()); @@ -35,11 +37,15 @@ pub fn execute_sc_call(tx_input: TxInput, mut state: BlockchainMock) -> (TxResul (tx_result, state) } +fn existing_account(state: &BlockchainMock, address: &VMAddress) -> bool { + state.accounts.contains_key(address) || is_system_sc_address(address) +} + pub fn execute_async_call_and_callback( async_data: AsyncCallTxData, state: BlockchainMock, ) -> (TxResult, TxResult, BlockchainMock) { - if state.accounts.contains_key(&async_data.to) { + if existing_account(&state, &async_data.to) { let async_input = async_call_tx_input(&async_data); let (async_result, state) = sc_call_with_async_and_callback(async_input, state); diff --git a/vm/src/tx_execution/exec_general_tx.rs b/vm/src/tx_execution/exec_general_tx.rs index 44d7bf5d03..3747de7c04 100644 --- a/vm/src/tx_execution/exec_general_tx.rs +++ b/vm/src/tx_execution/exec_general_tx.rs @@ -5,21 +5,26 @@ use crate::{ types::VMAddress, }; -use super::execute_tx_context; +use super::{execute_system_sc, execute_tx_context, is_system_sc_address}; pub fn default_execution(tx_input: TxInput, tx_cache: TxCache) -> (TxResult, BlockchainUpdate) { let mut tx_context = TxContext::new(tx_input, tx_cache); - if let Err(err) = tx_context.tx_cache.subtract_egld_balance( - &tx_context.tx_input_box.from, - &tx_context.tx_input_box.egld_value, - ) { - return (TxResult::from_panic_obj(&err), BlockchainUpdate::empty()); + if !is_system_sc_address(&tx_context.tx_input_box.from) { + if let Err(err) = tx_context.tx_cache.subtract_egld_balance( + &tx_context.tx_input_box.from, + &tx_context.tx_input_box.egld_value, + ) { + return (TxResult::from_panic_obj(&err), BlockchainUpdate::empty()); + } + } + + if !is_system_sc_address(&tx_context.tx_input_box.to) { + tx_context.tx_cache.increase_egld_balance( + &tx_context.tx_input_box.to, + &tx_context.tx_input_box.egld_value, + ); } - tx_context.tx_cache.increase_egld_balance( - &tx_context.tx_input_box.to, - &tx_context.tx_input_box.egld_value, - ); // skip for transactions coming directly from scenario json, which should all be coming from user wallets // TODO: reorg context logic @@ -54,11 +59,16 @@ pub fn default_execution(tx_input: TxInput, tx_cache: TxCache) -> (TxResult, Blo } } - let mut tx_result = if !tx_context.tx_input_box.to.is_smart_contract_address() + let recipient_address = &tx_context.tx_input_box.to; + let mut tx_result = if !recipient_address.is_smart_contract_address() || tx_context.tx_input_box.func_name.is_empty() { // direct EGLD transfer TxResult::empty() + } else if is_system_sc_address(recipient_address) { + let (tx_context_modified, tx_result) = execute_system_sc(tx_context); + tx_context = tx_context_modified; + tx_result } else { let (tx_context_modified, tx_result) = execute_tx_context(tx_context); tx_context = tx_context_modified; diff --git a/vm/src/tx_execution/mod.rs b/vm/src/tx_execution/mod.rs index 01541d526e..cdf3d577a9 100644 --- a/vm/src/tx_execution/mod.rs +++ b/vm/src/tx_execution/mod.rs @@ -3,9 +3,11 @@ mod exec_call; mod exec_contract_endpoint; mod exec_create; mod exec_general_tx; +mod system_sc; pub use builtin_function_mocks::*; pub use exec_call::*; pub use exec_contract_endpoint::*; pub use exec_create::*; pub use exec_general_tx::*; +pub use system_sc::*; diff --git a/vm/src/tx_execution/system_sc.rs b/vm/src/tx_execution/system_sc.rs new file mode 100644 index 0000000000..b8e7a4b380 --- /dev/null +++ b/vm/src/tx_execution/system_sc.rs @@ -0,0 +1,60 @@ +mod system_sc_issue; +mod system_sc_special_roles; +mod system_sc_unimplemented; + +use crate::{ + tx_mock::{TxContext, TxResult}, + types::VMAddress, +}; +use hex_literal::hex; +use system_sc_issue::*; +use system_sc_special_roles::*; +use system_sc_unimplemented::*; + +/// Address of the system smart contract that manages ESDT. +/// Bech32: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u +pub const ESDT_SYSTEM_SC_ADDRESS_ARRAY: [u8; 32] = + hex!("000000000000000000010000000000000000000000000000000000000002ffff"); + +pub fn is_system_sc_address(address: &VMAddress) -> bool { + address.as_array() == &ESDT_SYSTEM_SC_ADDRESS_ARRAY +} + +pub fn execute_system_sc(tx_context: TxContext) -> (TxContext, TxResult) { + let func_name = tx_context.tx_input_box.func_name.clone(); + match func_name.as_str() { + "issue" => issue(tx_context), + "issueSemiFungible" => issue_semi_fungible(tx_context), + "issueNonFungible" => issue_non_fungible(tx_context), + "registerMetaESDT" => register_meta_esdt(tx_context), + "changeSFTToMetaESDT" => change_sft_to_meta_esdt(tx_context), + "registerAndSetAllRoles" => register_and_set_all_roles(), + "ESDTBurn" => esdt_burn(tx_context), + "mint" => mint(tx_context), + "freeze" => freeze(tx_context), + "unFreeze" => unfreeze(tx_context), + "wipe" => wipe(tx_context), + "pause" => pause(tx_context), + "unPause" => unpause(tx_context), + "freezeSingleNFT" => freeze_single_nft(tx_context), + "unFreezeSingleNFT" => unfreeze_single_nft(tx_context), + "wipeSingleNFT" => wipe_single_nft(tx_context), + "claim" => claim(tx_context), + "configChange" => config_change(tx_context), + "controlChanges" => control_changes(tx_context), + "transferOwnership" => transfer_ownership(tx_context), + "getTokenProperties" => get_token_properties(tx_context), + "getSpecialRoles" => get_special_roles(tx_context), + "setSpecialRole" => set_special_role(tx_context), + "unSetSpecialRole" => unset_special_role(tx_context), + "transferNFTCreateRole" => transfer_nft_create_role(tx_context), + "stopNFTCreate" => stop_nft_create(tx_context), + "getAllAddressesAndRoles" => get_all_addresses_and_roles(tx_context), + "getContractConfig" => get_contract_config(tx_context), + "changeToMultiShardCreate" => change_to_multi_shard_create(tx_context), + "setBurnRoleGlobally" => set_burn_role_globally(tx_context), + "unsetBurnRoleGlobally" => unset_burn_role_globally(tx_context), + "sendAllTransferRoleAddresses" => send_all_transfer_role_addresses(tx_context), + invalid_func_name => panic!("invalid system SC function: {invalid_func_name}"), + } +} diff --git a/vm/src/tx_execution/system_sc/system_sc_issue.rs b/vm/src/tx_execution/system_sc/system_sc_issue.rs new file mode 100644 index 0000000000..cb9c0fcea9 --- /dev/null +++ b/vm/src/tx_execution/system_sc/system_sc_issue.rs @@ -0,0 +1,163 @@ +use num_bigint::BigUint; + +use crate::{ + crypto_functions::keccak256, + tx_mock::{TxCache, TxContext, TxInput, TxResult}, + types::top_decode_u64, +}; + +/// Issues a new token. +pub fn issue(tx_context: TxContext) -> (TxContext, TxResult) { + let tx_input = tx_context.input_ref(); + let tx_cache = tx_context.blockchain_cache(); + let tx_result: TxResult; + + if tx_input.args.len() < 4 { + tx_result = TxResult::from_vm_error("not enough arguments"); + return (tx_context, tx_result); + } + let _name = tx_input.args[0].clone(); + let ticker = tx_input.args[1].clone(); + let _total_supply = BigUint::from_bytes_be(tx_input.args[2].clone().as_ref()); + let _decimals = top_decode_u64(tx_input.args[3].clone().as_ref()) as u32; + + let mut new_token_identifiers = tx_cache.get_new_token_identifiers(); + + let token_identifier = if let Some((i, ti)) = + first_token_identifier_with_ticker(&new_token_identifiers, &ticker) + { + new_token_identifiers.remove(i); + ti.into_bytes() + } else { + generate_token_identifier_from_ticker(tx_input, tx_cache, &ticker) + }; + + println!( + "\n\ngenerated new token_identifier: {}\n\n", + std::str::from_utf8(&token_identifier).unwrap() + ); + + tx_cache.with_account_mut(&tx_input.from, |account| { + account.esdt.issue_token(&token_identifier); + }); + tx_cache.set_new_token_identifiers(new_token_identifiers); + + tx_result = TxResult { + result_values: vec![token_identifier], + ..Default::default() + }; + + (tx_context, tx_result) +} + +/// Issues a new semi-fungible token. +pub fn issue_semi_fungible(tx_context: TxContext) -> (TxContext, TxResult) { + issue_non_fungible(tx_context) +} + +/// Issues a new non-fungible token. +pub fn issue_non_fungible(tx_context: TxContext) -> (TxContext, TxResult) { + let tx_input = tx_context.input_ref(); + let tx_cache = tx_context.blockchain_cache(); + let tx_result: TxResult; + + if tx_input.args.len() < 2 { + tx_result = TxResult::from_vm_error("not enough arguments"); + return (tx_context, tx_result); + } + let _name = tx_input.args[0].clone(); + let ticker = tx_input.args[1].clone(); + + let mut new_token_identifiers = tx_cache.get_new_token_identifiers(); + + let token_identifier = if let Some((i, ti)) = + first_token_identifier_with_ticker(&new_token_identifiers, &ticker) + { + new_token_identifiers.remove(i); + ti.into_bytes() + } else { + generate_token_identifier_from_ticker(tx_input, tx_cache, &ticker) + }; + + tx_cache.with_account_mut(&tx_input.from, |account| { + account.esdt.issue_token(&token_identifier); + }); + tx_cache.set_new_token_identifiers(new_token_identifiers); + + tx_result = TxResult { + result_values: vec![token_identifier], + ..Default::default() + }; + + (tx_context, tx_result) +} + +fn first_token_identifier_with_ticker( + token_identifiers: &[String], + ticker: &[u8], +) -> Option<(usize, String)> { + let extract_ticker = + |ti: &String| -> String { ti.split('-').map(|x| x.to_string()).next().unwrap() }; + + token_identifiers + .iter() + .position(|x| extract_ticker(x).as_bytes() == ticker) + .map(|i| (i, token_identifiers[i].clone())) +} + +fn generate_token_identifier_from_ticker( + tx_input: &TxInput, + tx_cache: &TxCache, + ticker: &[u8], +) -> Vec { + let new_random_base = [ + tx_input.from.as_bytes(), + tx_cache + .blockchain_ref() + .current_block_info + .block_random_seed + .as_slice(), + ] + .concat(); + let new_random = keccak256(&new_random_base); + let new_random_for_ticker = &new_random[..3]; + + let token_identifier = [ + ticker, + "-".as_bytes(), + hex::encode(new_random_for_ticker).as_bytes(), + ] + .concat(); + + token_identifier +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_first_token_identifier_with_ticker_ok() { + let ticker = String::from("BBBB").into_bytes(); + let new_token_indetifiers = vec![ + "AAAA-0123".to_string(), + "BBBB-4567".to_string(), + "BBBB-0123".to_string(), + "CCCC-4567".to_string(), + ]; + + let ti = first_token_identifier_with_ticker(&new_token_indetifiers, &ticker); + let expected = b"BBBB-4567".as_slice(); + assert_eq!(expected, ti.unwrap().1.into_bytes()); + } + + #[test] + fn test_first_token_identifier_with_ticker_is_none() { + let ticker = String::from("BBBB").into_bytes(); + let new_token_indetifiers = vec!["AAAA-0123".to_string()]; + + let i = first_token_identifier_with_ticker(&new_token_indetifiers, &ticker); + let expected = None; + assert_eq!(expected, i); + } +} diff --git a/vm/src/tx_execution/system_sc/system_sc_special_roles.rs b/vm/src/tx_execution/system_sc/system_sc_special_roles.rs new file mode 100644 index 0000000000..f335c7a3e7 --- /dev/null +++ b/vm/src/tx_execution/system_sc/system_sc_special_roles.rs @@ -0,0 +1,21 @@ +use crate::{tx_mock::{TxContext, TxResult}, types::VMAddress}; + +pub fn set_special_role(tx_context: TxContext) -> (TxContext, TxResult) { + let tx_input = tx_context.input_ref(); + let tx_cache = tx_context.blockchain_cache(); + let mut tx_result = TxResult::empty(); + + if tx_input.args.len() < 3 { + tx_result = TxResult::from_vm_error("setSpecialRole too few arguments"); + return (tx_context, tx_result); + } + let token_identifier = tx_input.args[0].clone(); + let address = VMAddress::from_slice(tx_input.args[1].as_slice()); + let role = tx_input.args[2].clone(); + + tx_cache.with_account_mut(&address, |account| { + account.esdt.set_special_role(&token_identifier, &role); + }); + + (tx_context, tx_result) +} diff --git a/vm/src/tx_execution/system_sc/system_sc_unimplemented.rs b/vm/src/tx_execution/system_sc/system_sc_unimplemented.rs new file mode 100644 index 0000000000..9d4d07ce96 --- /dev/null +++ b/vm/src/tx_execution/system_sc/system_sc_unimplemented.rs @@ -0,0 +1,144 @@ +use crate::tx_mock::{TxContext, TxResult}; + +/// Every unimplemented fn will be implemented and moved to its corresponding file. +/// This file will be deleted. + +#[allow(unused_variables)] +pub fn register_meta_esdt(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn change_sft_to_meta_esdt(_tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn register_and_set_all_roles() -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn esdt_burn(_tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn mint(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn freeze(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn unfreeze(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn wipe(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn pause(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn unpause(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn freeze_single_nft(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn unfreeze_single_nft(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn wipe_single_nft(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn claim(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn config_change(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn control_changes(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn transfer_ownership(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn get_token_properties(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn get_special_roles(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn unset_special_role(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn transfer_nft_create_role(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn stop_nft_create(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn get_all_addresses_and_roles(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn get_contract_config(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn change_to_multi_shard_create(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn set_burn_role_globally(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn unset_burn_role_globally(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} + +#[allow(unused_variables)] +pub fn send_all_transfer_role_addresses(tx_context: TxContext) -> (TxContext, TxResult) { + unimplemented!() +} diff --git a/vm/src/tx_mock/tx_cache.rs b/vm/src/tx_mock/tx_cache.rs index f32aa7677f..c188a78e33 100644 --- a/vm/src/tx_mock/tx_cache.rs +++ b/vm/src/tx_mock/tx_cache.rs @@ -16,6 +16,7 @@ use super::TxCacheSource; pub struct TxCache { source_ref: Rc, pub(super) accounts: RefCell>, + pub(super) new_token_identifiers: RefCell>>, } impl fmt::Debug for TxCache { @@ -31,6 +32,7 @@ impl TxCache { TxCache { source_ref, accounts: RefCell::new(HashMap::new()), + new_token_identifiers: RefCell::new(None), } } @@ -97,9 +99,18 @@ impl TxCache { }) } + pub fn get_new_token_identifiers(&self) -> Vec { + self.blockchain_ref().get_new_token_identifiers() + } + + pub fn set_new_token_identifiers(&self, token_identifiers: Vec) { + *self.new_token_identifiers.borrow_mut() = Some(token_identifiers); + } + pub fn into_blockchain_updates(self) -> BlockchainUpdate { BlockchainUpdate { accounts: self.accounts.into_inner(), + new_token_identifiers: self.new_token_identifiers.into_inner(), } } @@ -112,16 +123,22 @@ impl TxCache { pub struct BlockchainUpdate { accounts: HashMap, + new_token_identifiers: Option>, } impl BlockchainUpdate { pub fn empty() -> Self { BlockchainUpdate { accounts: HashMap::new(), + new_token_identifiers: None, } } pub fn apply(self, blockchain: &mut BlockchainMock) { blockchain.update_accounts(self.accounts); + + if let Some(token_identifiers) = self.new_token_identifiers { + blockchain.update_new_token_identifiers(token_identifiers); + } } } diff --git a/vm/src/world_mock/blockchain_mock.rs b/vm/src/world_mock/blockchain_mock.rs index cd2549e288..b6288953ac 100644 --- a/vm/src/world_mock/blockchain_mock.rs +++ b/vm/src/world_mock/blockchain_mock.rs @@ -16,6 +16,7 @@ pub struct BlockchainMock { pub accounts: HashMap, pub builtin_functions: Rc, pub new_addresses: HashMap<(VMAddress, u64), VMAddress>, + pub new_token_identifiers: Vec, pub previous_block_info: BlockInfo, pub current_block_info: BlockInfo, pub executor: Box, @@ -27,20 +28,25 @@ impl BlockchainMock { accounts: HashMap::new(), builtin_functions: Rc::new(init_builtin_functions()), new_addresses: HashMap::new(), + new_token_identifiers: Vec::new(), previous_block_info: BlockInfo::new(), current_block_info: BlockInfo::new(), executor, } } -} -impl Default for BlockchainMock { - fn default() -> Self { - Self::new(Box::new(FailingExecutor)) + pub fn put_new_token_identifier(&mut self, token_identifier: String) { + self.new_token_identifiers.push(token_identifier) + } + + pub fn get_new_token_identifiers(&self) -> Vec { + self.new_token_identifiers.clone() + } + + pub fn update_new_token_identifiers(&mut self, token_identifiers: Vec) { + self.new_token_identifiers = token_identifiers; } -} -impl BlockchainMock { pub fn account_exists(&self, address: &VMAddress) -> bool { self.accounts.contains_key(address) } @@ -105,6 +111,12 @@ impl BlockchainMock { } } +impl Default for BlockchainMock { + fn default() -> Self { + Self::new(Box::new(FailingExecutor)) + } +} + impl Debug for BlockchainMock { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("BlockchainMock") diff --git a/vm/src/world_mock/esdt_data.rs b/vm/src/world_mock/esdt_data.rs index 3155a62408..d44098bb4f 100644 --- a/vm/src/world_mock/esdt_data.rs +++ b/vm/src/world_mock/esdt_data.rs @@ -149,6 +149,34 @@ impl AccountEsdt { pub fn iter(&self) -> Iter, EsdtData> { self.0.iter() } + + pub fn issue_token(&mut self, token_identifier: &[u8]) { + let roles = vec![ + "ESDTLocalMint".as_bytes().to_vec(), + "ESDTLocalBurn".as_bytes().to_vec(), + ]; + + self.0.insert( + token_identifier.to_vec(), + EsdtData { + instances: EsdtInstances::new(), + last_nonce: 0, + roles: EsdtRoles::new(roles), + frozen: false, + }, + ); + } + + pub fn set_special_role(&mut self, token_identifier: &[u8], role: &[u8]) { + if let Some(esdt_data) = self.get_mut_by_identifier(token_identifier) { + let roles = esdt_data.roles.get(); + if !roles.contains(role.to_vec().as_ref()) { + let mut new_roles = roles; + new_roles.push(role.to_vec()); + esdt_data.roles = EsdtRoles::new(new_roles); + } + } + } } impl fmt::Display for EsdtData {