diff --git a/contracts/examples/adder/interactor/set_state.json b/contracts/examples/adder/interactor/set_state.json new file mode 100644 index 0000000000..a5364326ab --- /dev/null +++ b/contracts/examples/adder/interactor/set_state.json @@ -0,0 +1,82 @@ +[ + { + "address": "erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa", + "nonce": 5947, + "balance": "491982310359999986", + "keys": { + "454c524f4e446573647450544d2d35333666616201": "08021202000122ef0108011212546573742d5061696e742d486172766573741a20e32afedc904fe1939746ad973beb383563cf63642ba669b3040f9b9428a5ed6020c4132a2e516d57564239575362674b52655a64615a434344766b454b70705a6b4d696d397563736e7857565041414c6a4374324368747470733a2f2f697066732e696f2f697066732f516d57564239575362674b52655a64615a434344766b454b70705a6b4d696d397563736e7857565041414c6a43743a3d746167733a3b6d657461646174613a516d52635039346b5872357a5a6a52477669376d4a36756e374c7078556859565234523452706963787a67596b74", + "454c524f4e44657364745453542d643964336136": "1209004563918244f40000", + "454c524f4e44657364745745474c442d613238633539": "120900389351ce08f09e12", + "454c524f4e44657364745453542d633636666535": "1209004563918244f40000", + "454c524f4e44657364745453542d623130616461": "1209004563918244f40000", + "454c524f4e4465736474475245454e2d306531363163": "120b00152d02c7e14af67fffdc", + "454c524f4e44657364745453542d656338383735": "12020064", + "454c524f4e44657364744c5453542d376266336431": "1209000de0b6b3a763fc19", + "454c524f4e44657364745453542d343265356138": "1209004563918244f40000", + "454c524f4e44726f6c656573647450544d2d353336666162": "0a1145534454526f6c654e46544372656174650a0f45534454526f6c654e46544275726e", + "454c524f4e44657364745453542d393836646663": "12020064", + "454c524f4e44657364745453542d323833633361": "12020064", + "454c524f4e4465736474424358535542542d33393264366172": "080112020001", + "454c524f4e44657364744c5453542d346638343965": "1209000de0b6b3a763fc19", + "454c524f4e44657364745453542d386564363538": "1209004563918244f40000", + "454c524f4e44657364745453542d343562383235": "12020064", + "454c524f4e446e6f6e636550544d2d353336666162": "01", + "454c524f4e44657364745453542d363835303064": "1209004563918244f40000", + "454c524f4e44657364745453542d336339363762": "12020064", + "454c524f4e446573647455544b2d313464353764": "120b0001e6ce88d5ebbfd00000", + "454c524f4e44657364745453542d363434633935": "12020064", + "454c524f4e44657364745453542d306632306637": "12020064", + "454c524f4e44657364745453542d333331386638": "1209004563918244f40000", + "454c524f4e44657364745453542d353538616434": "12020064", + "454c524f4e44657364745453542d363437383930": "1209004563918244f40000", + "454c524f4e44657364745453542d633933336139": "1209004563918244f40000", + "454c524f4e44657364745453542d393864633566": "1209004563918244f40000", + "454c524f4e44657364745453542d303637373232": "1209004563918244f40000", + "454c524f4e44657364745453542d346634303238": "12020064", + "454c524f4e44657364745453542d643862306438": "12020064", + "454c524f4e44657364745453542d346230653865": "1209004563918244f40000", + "454c524f4e44657364745453542d343138613232": "1209004563918244f40000", + "454c524f4e44657364745453542d396230323030": "1209004563918244f40000", + "454c524f4e44657364745453542d373639313337": "1209004563918244f40000", + "454c524f4e44657364745453542d303362373664": "12020064", + "454c524f4e44657364745453542d613562663131": "12020064", + "454c524f4e44657364745453542d353966316165": "1209004563918244f40000", + "454c524f4e44657364745453542d623136363735": "1209004563918244f40000", + "454c524f4e44657364745453542d333639646531": "1209004563918244f40000", + "454c524f4e44657364745453542d623830663863": "1209004563918244f40000", + "454c524f4e44657364745453542d633565303835": "1209004563918244f40000" + }, + "code": "", + "code_hash": "", + "root_hash": "2zSeJjLqgozEQmmgDU8L0/GidcKzJOlJgwoaTUqvDFg=", + "code_metadata": "", + "owner_address": "", + "developer_reward": "0" + }, + { + "address": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx", + "nonce": 1417, + "balance": "1753855617144056", + "keys": { + "454c524f4e446573647445564e544e4f544946592d393634383835": "120b00152d02c7e14af6800000", + "454c524f4e4465736474494e5445524e532d63393332356601": "0801120b0013097d1fb962e12fff47", + "454c524f4e44657364744e455453432d623635306261": "120b00d137965aa7a731800000", + "454c524f4e446e6f6e6365494e5445524e532d633933323566": "01", + "454c524f4e44657364745745474c442d613238633539": "120800010593b233281b", + "454c524f4e446e6f6e63654d4554414e46542d643062623339": "01", + "454c524f4e44726f6c6565736474494e5445524e532d633933323566": "0a1145534454526f6c654e46544372656174650a1645534454526f6c654e46544164645175616e74697479", + "454c524f4e44726f6c65657364744d4554414e46542d643062623339": "0a1145534454526f6c654e4654437265617465", + "454c524f4e446573647442534b2d343736343730": "120b00021e19e0c9bab23fff7b", + "454c524f4e44657364744e45543253432d306438663962": "120f0004ee2d6d3f3d6bcc25c64dc00000", + "454c524f4e4465736474424358535542542d3339326436616c": "080112020001", + "454c524f4e44657364744e4943552d393730323932": "120b00d3c21bcecceda1000000", + "454c524f4e4465736474424358535542542d3339326436616e": "080112020001" + }, + "code": "", + "code_hash": "", + "root_hash": "AJ2jyOcPXgZAl0kHAlbWZIlG3F1VDtcoLAHR6eqehBA=", + "code_metadata": "", + "owner_address": "", + "developer_reward": "0" + } +] \ No newline at end of file diff --git a/contracts/examples/adder/interactor/src/basic_interactor.rs b/contracts/examples/adder/interactor/src/basic_interactor.rs index 64dff9673e..fc08f50357 100644 --- a/contracts/examples/adder/interactor/src/basic_interactor.rs +++ b/contracts/examples/adder/interactor/src/basic_interactor.rs @@ -57,7 +57,7 @@ impl AdderInteract { let adder_owner_address = interactor.register_wallet(test_wallets::heidi()).await; let wallet_address = interactor.register_wallet(test_wallets::ivan()).await; - let _ = interactor.generate_blocks(30u64).await; + interactor.generate_blocks(30u64).await.unwrap(); AdderInteract { interactor, diff --git a/contracts/examples/adder/interactor/tests/basic_interactor_cs_test.rs b/contracts/examples/adder/interactor/tests/basic_interactor_cs_test.rs index d6b98b73e5..6d217f66f3 100644 --- a/contracts/examples/adder/interactor/tests/basic_interactor_cs_test.rs +++ b/contracts/examples/adder/interactor/tests/basic_interactor_cs_test.rs @@ -1,5 +1,5 @@ use basic_interactor::{AdderInteract, Config}; -use multiversx_sc_snippets::{sdk::gateway::SetStateAccount, test_wallets}; +use multiversx_sc_snippets::{imports::Bech32Address, sdk::gateway::SetStateAccount, test_wallets}; #[tokio::test] #[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] @@ -61,3 +61,37 @@ async fn set_state_cs_test() { assert!(set_state_response.is_ok()); } + +#[tokio::test] +#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] +async fn set_state_from_file_cs_test() { + let account_address = test_wallets::mike(); + let account_address_2 = test_wallets::ivan(); + + let mut real_chain_interact = AdderInteract::new(Config::load_config()).await; + let simulator_interact = AdderInteract::new(Config::chain_simulator_config()).await; + + // now we should have current mike account in the set state file + real_chain_interact + .interactor + .retrieve_account(&Bech32Address::from(&account_address.to_address())) + .await; + + real_chain_interact + .interactor + .retrieve_account(&Bech32Address::from(&account_address_2.to_address())) + .await; + + let set_state_response = simulator_interact + .interactor + .set_state_for_saved_accounts() + .await; + + simulator_interact + .interactor + .generate_blocks(2u64) + .await + .unwrap(); + + assert!(set_state_response.is_ok()); +} diff --git a/framework/snippets/Cargo.toml b/framework/snippets/Cargo.toml index 54fe80f23f..a22b7fd721 100644 --- a/framework/snippets/Cargo.toml +++ b/framework/snippets/Cargo.toml @@ -27,8 +27,9 @@ log = "0.4.17" env_logger = "0.11" futures = "0.3" anyhow = "1.0.44" +serde_json = "1.0" -tokio = { version = "1.24", features = ["full"], optional = true} +tokio = { version = "1.24", features = ["full"], optional = true } [dependencies.multiversx-sc-scenario] version = "=0.54.1" @@ -52,6 +53,3 @@ default-features = false version = "=0.7.0" path = "../../sdk/dapp" optional = true - -[dev-dependencies] -serde_json = "1.0" diff --git a/framework/snippets/src/account_tool.rs b/framework/snippets/src/account_tool.rs index b0a5ccc220..eefc51642f 100644 --- a/framework/snippets/src/account_tool.rs +++ b/framework/snippets/src/account_tool.rs @@ -4,7 +4,7 @@ use multiversx_sc_scenario::{ imports::Bech32Address, scenario_model::{Account, BytesKey, BytesValue, Scenario, SetStateStep, Step}, }; -use multiversx_sdk::gateway::GatewayAsyncService; +use multiversx_sdk::gateway::{GatewayAsyncService, SetStateAccount}; use multiversx_sdk::gateway::{ GetAccountEsdtRolesRequest, GetAccountEsdtTokensRequest, GetAccountRequest, GetAccountStorageRequest, @@ -20,7 +20,7 @@ pub async fn print_account_as_scenario_set_state Scenario { pub async fn retrieve_account_as_scenario_set_state( api: &GatewayProxy, bech32_address: &Bech32Address, -) -> SetStateStep { +) -> (SetStateAccount, SetStateStep) { let address = bech32_address.as_address(); let sdk_account = api.request(GetAccountRequest::new(address)).await.unwrap(); @@ -63,14 +63,18 @@ pub async fn retrieve_account_as_scenario_set_state where @@ -85,9 +88,33 @@ where } pub async fn retrieve_account(&mut self, wallet_address: &Bech32Address) { - let set_state = retrieve_account_as_scenario_set_state(&self.proxy, wallet_address).await; - self.pre_runners.run_set_state_step(&set_state); - self.post_runners.run_set_state_step(&set_state); + let (set_state_account, set_state_step) = + retrieve_account_as_scenario_set_state(&self.proxy, wallet_address).await; + self.pre_runners.run_set_state_step(&set_state_step); + self.post_runners.run_set_state_step(&set_state_step); + + let path = self.get_state_file_path(); + set_state_account.add_to_state_file(path.as_path()); + } + + pub fn get_state_file_path(&self) -> PathBuf { + self.current_dir.join(INTERACTOR_SET_STATE_PATH) + } + + pub fn get_accounts_from_file(&self) -> Vec { + let file_path = self.get_state_file_path(); + + if !file_path.exists() { + return Vec::new(); + } + + let file = File::open(file_path).expect("Failed to open state file"); + let reader = BufReader::new(file); + + serde_json::from_reader(reader).unwrap_or_else(|_| { + println!("Failed to parse state file; returning an empty list of accounts"); + Vec::new() + }) } /// Tells the interactor where the crate lies relative to the workspace. diff --git a/framework/snippets/src/interactor/interactor_chain_simulator.rs b/framework/snippets/src/interactor/interactor_chain_simulator.rs index 17ca51f3a7..88fa701fe0 100644 --- a/framework/snippets/src/interactor/interactor_chain_simulator.rs +++ b/framework/snippets/src/interactor/interactor_chain_simulator.rs @@ -64,4 +64,15 @@ where .request(ChainSimulatorSetStateRequest::for_accounts(accounts)) .await } + + pub async fn set_state_for_saved_accounts(&self) -> Result { + if !self.use_chain_simulator { + return Ok(String::from("no-simulator")); + } + + let accounts = self.get_accounts_from_file(); + self.proxy + .request(ChainSimulatorSetStateRequest::for_accounts(accounts)) + .await + } } diff --git a/sdk/core/src/gateway/gateway_chain_simulator_set_state.rs b/sdk/core/src/gateway/gateway_chain_simulator_set_state.rs index 77b46d4422..284f00a709 100644 --- a/sdk/core/src/gateway/gateway_chain_simulator_set_state.rs +++ b/sdk/core/src/gateway/gateway_chain_simulator_set_state.rs @@ -1,6 +1,11 @@ use anyhow::anyhow; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{ + collections::HashMap, + fs::{File, OpenOptions}, + io::{BufReader, BufWriter}, + path::Path, +}; use crate::data::account::Account; @@ -55,6 +60,40 @@ impl SetStateAccount { self } + + pub fn add_to_state_file(self, path: &Path) { + let mut accounts = if path.exists() { + let file = File::open(path) + .unwrap_or_else(|_| panic!("Failed to open state file at path {path:#?}")); + + let reader = BufReader::new(file); + + serde_json::from_reader::<_, Vec>(reader).unwrap_or_default() + } else { + Vec::new() + }; + + if let Some(existing_account) = accounts + .iter_mut() + .find(|account| account.address == self.address) + { + *existing_account = self; + } else { + accounts.push(self); + } + + let file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .unwrap_or_else(|_| panic!("Failed to open or create state file at path {path:#?}")); + + let writer = BufWriter::new(file); + serde_json::to_writer_pretty(writer, &accounts).unwrap_or_else(|_| { + panic!("Failed to write updated state accounts to file at path {path:#?}") + }); + } } /// Sets state for a list of accounts using the chain simulator API.