From 3a1ee56d9bb907c4d462d1a83acbc2fd3eaf17ae Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Wed, 28 Aug 2024 12:55:34 +0300 Subject: [PATCH 1/8] Modified the adder interactor example to work with keystore-signing Co-authored-by: Andrei Vasilescu --- contracts/examples/adder/interact/alice.json | 23 +++++++++++++++++++ .../adder/interact/src/basic_interact.rs | 4 +++- sdk/core/src/wallet.rs | 11 ++++----- 3 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 contracts/examples/adder/interact/alice.json diff --git a/contracts/examples/adder/interact/alice.json b/contracts/examples/adder/interact/alice.json new file mode 100644 index 0000000000..8f1a81514e --- /dev/null +++ b/contracts/examples/adder/interact/alice.json @@ -0,0 +1,23 @@ +{ + "version": 4, + "kind": "secretKey", + "id": "f2c5e236-9039-46cd-8df5-1efcd9466ead", + "address": "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", + "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "crypto": { + "ciphertext": "45de5198c6c477725abc17d26c6b5fc88b1ff22a67a8c4784b297d426d351cf5c891a4c92d63e04cf23f602f660b7b606ed1c5293c85ff40216d9e53c9a07c23", + "cipherparams": { + "iv": "d6442fb1d4c49106152baeb3c539ed2c" + }, + "cipher": "aes-128-ctr", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "salt": "dd69d88742b9d27a0438a4554d4180b070d9af224ea8ff8225fdf74c8e272adb", + "n": 4096, + "r": 8, + "p": 1 + }, + "mac": "b277984f71d0e9b4a6597911775c0ee6d598002645b29f22021e595c2725d204" + } +} diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 262eb13631..4765ec6fbe 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -65,7 +65,9 @@ impl AdderInteract { let adder_owner_address = interactor.register_wallet(Wallet::from_pem_file("adder-owner.pem").unwrap()); - let wallet_address = interactor.register_wallet(test_wallets::mike()); + // PASSWORD: "alice" + let wallet_address = interactor + .register_wallet(Wallet::from_keystore_secret("alice.json", "alice").unwrap()); Self { interactor, diff --git a/sdk/core/src/wallet.rs b/sdk/core/src/wallet.rs index bf1362a314..e197c13c02 100644 --- a/sdk/core/src/wallet.rs +++ b/sdk/core/src/wallet.rs @@ -156,12 +156,11 @@ impl Wallet { (private_key_str, public_key_str) } - pub fn from_keystore_secret(file_path: &str) -> Result { - let decyption_params = - Self::validate_keystore_password(file_path, Self::get_keystore_password()) - .unwrap_or_else(|e| { - panic!("Error: {:?}", e); - }); + pub fn from_keystore_secret(file_path: &str, password: &str) -> Result { + let decyption_params = Self::validate_keystore_password(file_path, password.to_string()) + .unwrap_or_else(|e| { + panic!("Error: {:?}", e); + }); let priv_key = PrivateKey::from_hex_str( hex::encode(Self::decrypt_secret_key(decyption_params)).as_str(), )?; From 4928800dfe85e9d65c8aaf06f48edbffa441a931 Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Wed, 28 Aug 2024 14:32:43 +0300 Subject: [PATCH 2/8] Password can now be passed either in terminal or as a string parameter Co-authored-by: Andrei Vasilescu --- contracts/examples/adder/interact/.gitignore | 1 + .../adder/interact/src/basic_interact.rs | 16 +++++++++---- sdk/core/src/data/keystore.rs | 7 ++++++ sdk/core/src/wallet.rs | 23 ++++++++++++++----- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/contracts/examples/adder/interact/.gitignore b/contracts/examples/adder/interact/.gitignore index b2630809de..336fa10df0 100644 --- a/contracts/examples/adder/interact/.gitignore +++ b/contracts/examples/adder/interact/.gitignore @@ -1,5 +1,6 @@ # Pem files are used for interactions, but shouldn't be committed *.pem +*.json !adder-owner.pem # Temporary storage of deployed contract address, so we can preserve the context between executions. diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 4765ec6fbe..abcaec4c00 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -7,7 +7,7 @@ use basic_interact_config::Config; use basic_interact_state::State; use clap::Parser; -use multiversx_sc_snippets::imports::*; +use multiversx_sc_snippets::{imports::*, sdk::data::keystore::InsertPassword}; const INTERACTOR_SCENARIO_TRACE_PATH: &str = "interactor_trace.scen.json"; @@ -66,8 +66,14 @@ impl AdderInteract { let adder_owner_address = interactor.register_wallet(Wallet::from_pem_file("adder-owner.pem").unwrap()); // PASSWORD: "alice" - let wallet_address = interactor - .register_wallet(Wallet::from_keystore_secret("alice.json", "alice").unwrap()); + // InsertPassword::Plaintext("alice".to_string()) || InsertPassword::StandardInput + let wallet_address = interactor.register_wallet( + Wallet::from_keystore_secret( + "alice.json", + InsertPassword::Plaintext("alice".to_string()), + ) + .unwrap(), + ); Self { interactor, @@ -243,5 +249,5 @@ async fn test() { basic_interact.deploy().await; basic_interact.add(1u32).await; - basic_interact.upgrade(7u32).await; -} + // basic_interact.upgrade(7u32).await; +} \ No newline at end of file diff --git a/sdk/core/src/data/keystore.rs b/sdk/core/src/data/keystore.rs index 1b3c656ef1..31e6b6538a 100644 --- a/sdk/core/src/data/keystore.rs +++ b/sdk/core/src/data/keystore.rs @@ -12,6 +12,13 @@ pub enum WalletError { InvalidKdf, InvalidCipher, } + +#[derive(Debug)] +pub enum InsertPassword { + Plaintext(String), + StandardInput, +} + #[derive(Debug, Serialize, Deserialize)] pub struct CryptoParams { pub iv: String, diff --git a/sdk/core/src/wallet.rs b/sdk/core/src/wallet.rs index e197c13c02..df9b9dc43a 100644 --- a/sdk/core/src/wallet.rs +++ b/sdk/core/src/wallet.rs @@ -156,13 +156,24 @@ impl Wallet { (private_key_str, public_key_str) } - pub fn from_keystore_secret(file_path: &str, password: &str) -> Result { - let decyption_params = Self::validate_keystore_password(file_path, password.to_string()) - .unwrap_or_else(|e| { - panic!("Error: {:?}", e); - }); + pub fn from_keystore_secret(file_path: &str, insert_password: InsertPassword) -> Result { + let decryption_params = match insert_password { + InsertPassword::Plaintext(password) => { + Self::validate_keystore_password(file_path, password.to_string()).unwrap_or_else( + |e| { + panic!("Error: {:?}", e); + }, + ) + }, + InsertPassword::StandardInput => { + Self::validate_keystore_password(file_path, Self::get_keystore_password()) + .unwrap_or_else(|e| { + panic!("Error: {:?}", e); + }) + }, + }; let priv_key = PrivateKey::from_hex_str( - hex::encode(Self::decrypt_secret_key(decyption_params)).as_str(), + hex::encode(Self::decrypt_secret_key(decryption_params)).as_str(), )?; Ok(Self { priv_key }) } From ca748ff88fc951a40d5a992f7e591d04750e43f6 Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Wed, 28 Aug 2024 15:49:18 +0300 Subject: [PATCH 3/8] Decommented a line to showcase the upgrade failure Co-authored-by: Andrei Vasilescu --- contracts/examples/adder/interact/src/basic_interact.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index abcaec4c00..0061b2a888 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -249,5 +249,5 @@ async fn test() { basic_interact.deploy().await; basic_interact.add(1u32).await; - // basic_interact.upgrade(7u32).await; + basic_interact.upgrade(7u32).await; } \ No newline at end of file From 6f41c77a4fb8f6796b86a5e28d46fe17e5a43e21 Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Wed, 28 Aug 2024 16:55:32 +0300 Subject: [PATCH 4/8] Made the upgrade failure more clear Co-authored-by: Andrei Vasilescu --- .../adder/interact/src/basic_interact.rs | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 0061b2a888..a5e5fb4942 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -209,13 +209,43 @@ impl AdderInteract { println!("sum: {sum}"); } + async fn fail_upgrade(&mut self, new_value: u32) { + self.interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_adder_address()) + .gas(6_000_000) + .typed(adder_proxy::AdderProxy) + .upgrade(BigUint::from(new_value)) + .code_metadata(CodeMetadata::UPGRADEABLE) + .code(ADDER_CODE_PATH) + .with_result(ExpectError(4, "upgrade is allowed only for owner")) + .prepare_async() + .run() + .await; + + let sum = self + .interactor + .query() + .to(self.state.current_adder_address()) + .typed(adder_proxy::AdderProxy) + .sum() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + assert_ne!(sum, RustBigUint::from(new_value)); + + println!("Upgrade failed: upgrade is allowed only for owner"); + } + async fn upgrade(&mut self, new_value: u32) { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(&self.adder_owner_address) .to(self.state.current_adder_address()) - .gas(3_000_000) + .gas(6_000_000) .typed(adder_proxy::AdderProxy) .upgrade(BigUint::from(new_value)) .code_metadata(CodeMetadata::UPGRADEABLE) @@ -243,11 +273,22 @@ impl AdderInteract { #[tokio::test] #[ignore = "run on demand"] -async fn test() { +async fn fail_upgrade_test() { + let mut basic_interact = AdderInteract::init().await; + + basic_interact.deploy().await; + basic_interact.add(1u32).await; + + basic_interact.fail_upgrade(7u32).await; +} + +#[tokio::test] +#[ignore = "run on demand"] +async fn upgrade_test() { let mut basic_interact = AdderInteract::init().await; basic_interact.deploy().await; basic_interact.add(1u32).await; basic_interact.upgrade(7u32).await; -} \ No newline at end of file +} From 5933f501839958bf3ab083a7313adca5e737044f Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Wed, 28 Aug 2024 16:57:57 +0300 Subject: [PATCH 5/8] Updated gas prices Co-authored-by: Andrei Vasilescu --- contracts/examples/adder/interact/src/basic_interact.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index a5e5fb4942..e1dc1b3dec 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -101,7 +101,7 @@ impl AdderInteract { .interactor .tx() .from(&self.adder_owner_address) - .gas(3_000_000) + .gas(6_000_000) .typed(adder_proxy::AdderProxy) .init(0u32) .code(ADDER_CODE_PATH) @@ -131,7 +131,7 @@ impl AdderInteract { .typed(adder_proxy::AdderProxy) .init(0u32) .code(ADDER_CODE_PATH) - .gas(3_000_000) + .gas(6_000_000) .returns(ReturnsNewBech32Address) }); } @@ -159,7 +159,7 @@ impl AdderInteract { .to(self.state.current_adder_address()) .typed(adder_proxy::AdderProxy) .add(value) - .gas(3_000_000) + .gas(6_000_000) }); } @@ -184,7 +184,7 @@ impl AdderInteract { .tx() .from(&self.wallet_address) .to(self.state.current_adder_address()) - .gas(3_000_000) + .gas(6_000_000) .typed(adder_proxy::AdderProxy) .add(value) .prepare_async() From 1f6f64449e3eba2c0014cd4b0a8d3f0fa73ad6b8 Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Thu, 29 Aug 2024 09:47:45 +0300 Subject: [PATCH 6/8] Finished updating the adder example Co-authored-by: Andrei Vasilescu --- .../adder/interact/src/basic_interact.rs | 71 ++++--------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index e1dc1b3dec..37e511b823 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -41,7 +41,8 @@ async fn main() { basic_interact.print_sum().await; }, Some(basic_interact_cli::InteractCliCommand::Upgrade(args)) => { - basic_interact.upgrade(args.value).await + let owner_address = basic_interact.adder_owner_address.clone(); + basic_interact.upgrade(args.value, owner_address, ExpectError(0, "")).await }, None => {}, } @@ -209,63 +210,21 @@ impl AdderInteract { println!("sum: {sum}"); } - async fn fail_upgrade(&mut self, new_value: u32) { - self.interactor - .tx() - .from(&self.wallet_address) - .to(self.state.current_adder_address()) - .gas(6_000_000) - .typed(adder_proxy::AdderProxy) - .upgrade(BigUint::from(new_value)) - .code_metadata(CodeMetadata::UPGRADEABLE) - .code(ADDER_CODE_PATH) - .with_result(ExpectError(4, "upgrade is allowed only for owner")) - .prepare_async() - .run() - .await; - - let sum = self - .interactor - .query() - .to(self.state.current_adder_address()) - .typed(adder_proxy::AdderProxy) - .sum() - .returns(ReturnsResultUnmanaged) - .prepare_async() - .run() - .await; - assert_ne!(sum, RustBigUint::from(new_value)); - - println!("Upgrade failed: upgrade is allowed only for owner"); - } - - async fn upgrade(&mut self, new_value: u32) { + async fn upgrade(&mut self, new_value: u32, sender: Bech32Address, expected_result: ExpectError<'_>) { let response = self .interactor .tx() - .from(&self.adder_owner_address) + .from(sender) .to(self.state.current_adder_address()) .gas(6_000_000) .typed(adder_proxy::AdderProxy) .upgrade(BigUint::from(new_value)) .code_metadata(CodeMetadata::UPGRADEABLE) .code(ADDER_CODE_PATH) - .returns(ReturnsResultUnmanaged) - .prepare_async() - .run() - .await; - - let sum = self - .interactor - .query() - .to(self.state.current_adder_address()) - .typed(adder_proxy::AdderProxy) - .sum() - .returns(ReturnsResultUnmanaged) + .returns(expected_result) .prepare_async() .run() .await; - assert_eq!(sum, RustBigUint::from(new_value)); println!("response: {response:?}"); } @@ -273,22 +232,22 @@ impl AdderInteract { #[tokio::test] #[ignore = "run on demand"] -async fn fail_upgrade_test() { +async fn upgrade_test() { let mut basic_interact = AdderInteract::init().await; basic_interact.deploy().await; basic_interact.add(1u32).await; - basic_interact.fail_upgrade(7u32).await; -} + // Sum will be 1 + basic_interact.print_sum().await; -#[tokio::test] -#[ignore = "run on demand"] -async fn upgrade_test() { - let mut basic_interact = AdderInteract::init().await; + basic_interact.upgrade(7u32, basic_interact.adder_owner_address.clone(), ExpectError(0, "")).await; - basic_interact.deploy().await; - basic_interact.add(1u32).await; + // Sum will be the updated value of 7 + basic_interact.print_sum().await; + + basic_interact.upgrade(10u32, basic_interact.wallet_address.clone(), ExpectError(4, "upgrade is allowed only for owner")).await; - basic_interact.upgrade(7u32).await; + // Sum will remain 7 + basic_interact.print_sum().await; } From 6dc98686a3dbca8979ec8b34ac87f2cc0fb0a5de Mon Sep 17 00:00:00 2001 From: Andrei Vasilescu Date: Thu, 29 Aug 2024 12:10:58 +0300 Subject: [PATCH 7/8] refactored upgrade code --- .../adder/interact/src/basic_interact.rs | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 37e511b823..93a00d4079 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -42,7 +42,9 @@ async fn main() { }, Some(basic_interact_cli::InteractCliCommand::Upgrade(args)) => { let owner_address = basic_interact.adder_owner_address.clone(); - basic_interact.upgrade(args.value, owner_address, ExpectError(0, "")).await + basic_interact + .upgrade(args.value, &owner_address, None) + .await }, None => {}, } @@ -210,23 +212,59 @@ impl AdderInteract { println!("sum: {sum}"); } - async fn upgrade(&mut self, new_value: u32, sender: Bech32Address, expected_result: ExpectError<'_>) { - let response = self - .interactor - .tx() - .from(sender) - .to(self.state.current_adder_address()) - .gas(6_000_000) - .typed(adder_proxy::AdderProxy) - .upgrade(BigUint::from(new_value)) - .code_metadata(CodeMetadata::UPGRADEABLE) - .code(ADDER_CODE_PATH) - .returns(expected_result) - .prepare_async() - .run() - .await; + async fn upgrade( + &mut self, + new_value: u32, + sender: &Bech32Address, + expected_result: Option<(u64, &str)>, + ) { + match expected_result { + Some((code, msg)) => { + let response = self + .interactor + .tx() + .from(sender) + .to(self.state.current_adder_address()) + .gas(6_000_000) + .typed(adder_proxy::AdderProxy) + .upgrade(BigUint::from(new_value)) + .code_metadata(CodeMetadata::UPGRADEABLE) + .code(ADDER_CODE_PATH) + .returns(ExpectError(code, msg)) + .prepare_async() + .run() + .await; + + println!("response: {response:?}"); + }, + None => { + self.interactor + .tx() + .from(sender) + .to(self.state.current_adder_address()) + .gas(6_000_000) + .typed(adder_proxy::AdderProxy) + .upgrade(BigUint::from(new_value)) + .code_metadata(CodeMetadata::UPGRADEABLE) + .code(ADDER_CODE_PATH) + .prepare_async() + .run() + .await; - println!("response: {response:?}"); + let sum = self + .interactor + .query() + .to(self.state.current_adder_address()) + .typed(adder_proxy::AdderProxy) + .sum() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + assert_eq!(sum, RustBigUint::from(new_value)); + }, + } } } @@ -234,6 +272,9 @@ impl AdderInteract { #[ignore = "run on demand"] async fn upgrade_test() { let mut basic_interact = AdderInteract::init().await; + let wallet_address = basic_interact.wallet_address.clone(); + let adder_owner_address = basic_interact.adder_owner_address.clone(); + let error_not_owner = (4, "upgrade is allowed only for owner"); basic_interact.deploy().await; basic_interact.add(1u32).await; @@ -241,12 +282,16 @@ async fn upgrade_test() { // Sum will be 1 basic_interact.print_sum().await; - basic_interact.upgrade(7u32, basic_interact.adder_owner_address.clone(), ExpectError(0, "")).await; + basic_interact + .upgrade(7u32, &adder_owner_address, None) + .await; // Sum will be the updated value of 7 basic_interact.print_sum().await; - basic_interact.upgrade(10u32, basic_interact.wallet_address.clone(), ExpectError(4, "upgrade is allowed only for owner")).await; + basic_interact + .upgrade(10u32, &wallet_address, Some(error_not_owner)) + .await; // Sum will remain 7 basic_interact.print_sum().await; From 3e4654143905787a3e9989804b233bf2ad72c8d1 Mon Sep 17 00:00:00 2001 From: JustEatAnApple Date: Thu, 29 Aug 2024 17:08:59 +0300 Subject: [PATCH 8/8] Added requested changes for clarity Co-authored-by: Andrei Vasilescu --- contracts/examples/adder/interact/src/basic_interact.rs | 6 +++--- framework/snippets/src/imports.rs | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 93a00d4079..454060eb0d 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -7,7 +7,7 @@ use basic_interact_config::Config; use basic_interact_state::State; use clap::Parser; -use multiversx_sc_snippets::{imports::*, sdk::data::keystore::InsertPassword}; +use multiversx_sc_snippets::imports::*; const INTERACTOR_SCENARIO_TRACE_PATH: &str = "interactor_trace.scen.json"; @@ -227,7 +227,7 @@ impl AdderInteract { .to(self.state.current_adder_address()) .gas(6_000_000) .typed(adder_proxy::AdderProxy) - .upgrade(BigUint::from(new_value)) + .upgrade(new_value) .code_metadata(CodeMetadata::UPGRADEABLE) .code(ADDER_CODE_PATH) .returns(ExpectError(code, msg)) @@ -244,7 +244,7 @@ impl AdderInteract { .to(self.state.current_adder_address()) .gas(6_000_000) .typed(adder_proxy::AdderProxy) - .upgrade(BigUint::from(new_value)) + .upgrade(new_value) .code_metadata(CodeMetadata::UPGRADEABLE) .code(ADDER_CODE_PATH) .prepare_async() diff --git a/framework/snippets/src/imports.rs b/framework/snippets/src/imports.rs index d416955e4c..a35e880c78 100644 --- a/framework/snippets/src/imports.rs +++ b/framework/snippets/src/imports.rs @@ -4,7 +4,10 @@ pub use crate::{ dns_address_for_name, test_wallets, Interactor, InteractorPrepareAsync, StepBuffer, }; -pub use multiversx_sdk::wallet::Wallet; +pub use multiversx_sdk::{ + wallet::Wallet, + data::keystore::InsertPassword, +}; pub use env_logger; pub use tokio;