From e046ae5a1c0c8eda4e1410ccac4f2419553fae82 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:50:52 +0200 Subject: [PATCH 01/14] Fix restore_from_stronghold_snapshot() with same source and target path (#2234) * Fix restore_from_stronghold_snapshot() for the case when source and target path are the same * Update rustls * Unblock port 8084 * Compare canonicalized paths, replace used port 8084 --- .../actions/private-tangle/setup/action.yml | 5 + Cargo.lock | 6 +- bindings/nodejs/CHANGELOG.md | 6 ++ bindings/nodejs/package.json | 2 +- .../core/operations/stronghold_backup/mod.rs | 12 ++- sdk/tests/wallet/backup_restore.rs | 99 +++++++++++++++++++ 6 files changed, 123 insertions(+), 7 deletions(-) diff --git a/.github/actions/private-tangle/setup/action.yml b/.github/actions/private-tangle/setup/action.yml index 2504053375..7b0fbc4de4 100644 --- a/.github/actions/private-tangle/setup/action.yml +++ b/.github/actions/private-tangle/setup/action.yml @@ -27,6 +27,11 @@ runs: go-version-file: "iota-core/go.mod" cache: false + - name: Replace port 8084 by 8087 as it's already used by Mono + shell: bash + run: sed -i 's#8084#8087#g' docker-compose.yml + working-directory: iota-core/tools/docker-network + - name: Setup private tangle shell: bash # setup-go sets the PATH for the correct version, but sudo uses a different PATH by default diff --git a/Cargo.lock b/Cargo.lock index 0191e7fa8d..dfd996edcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1880,7 +1880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.48.5", ] [[package]] @@ -2810,9 +2810,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", diff --git a/bindings/nodejs/CHANGELOG.md b/bindings/nodejs/CHANGELOG.md index 5bece978d6..1f095c9b70 100644 --- a/bindings/nodejs/CHANGELOG.md +++ b/bindings/nodejs/CHANGELOG.md @@ -19,6 +19,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security --> +## 2.0.0-alpha.8 - 2024-04-22 + +### Fixed + +- `Wallet::restoreFromStrongholdSnapshot()` with same source and target path; + ## 2.0.0-alpha.7 - 2024-04-19 ### Fixed diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json index cb52f6f006..97db771f1b 100644 --- a/bindings/nodejs/package.json +++ b/bindings/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@iota/sdk", - "version": "2.0.0-alpha.7", + "version": "2.0.0-alpha.8", "description": "Node.js binding to the IOTA SDK library", "main": "out/index.js", "types": "out/index.d.ts", diff --git a/sdk/src/wallet/core/operations/stronghold_backup/mod.rs b/sdk/src/wallet/core/operations/stronghold_backup/mod.rs index abfedfc1ad..dcf6aa5dee 100644 --- a/sdk/src/wallet/core/operations/stronghold_backup/mod.rs +++ b/sdk/src/wallet/core/operations/stronghold_backup/mod.rs @@ -134,7 +134,9 @@ impl Wallet { .map_err(|_| WalletError::Backup("invalid secret_manager"))?; // Copy Stronghold file so the seed is available in the new location - fs::copy(backup_path, new_snapshot_path)?; + if backup_path.canonicalize()? != new_snapshot_path.canonicalize()? { + fs::copy(backup_path, new_snapshot_path)?; + } if let SecretManager::Stronghold(stronghold) = &restored_secret_manager { // Set password to restored secret manager @@ -144,7 +146,9 @@ impl Wallet { } else { // If no secret manager data was in the backup, just copy the Stronghold file so the seed is available in // the new location. - fs::copy(backup_path, new_snapshot_path)?; + if backup_path.canonicalize()? != new_snapshot_path.canonicalize()? { + fs::copy(backup_path, new_snapshot_path)?; + } } if ignore_if_bip_path_mismatch.is_none() { @@ -294,7 +298,9 @@ impl Wallet { .map_err(|_| WalletError::Backup("invalid secret_manager"))?; // Copy Stronghold file so the seed is available in the new location - fs::copy(backup_path, new_snapshot_path)?; + if backup_path.canonicalize()? != new_snapshot_path.canonicalize()? { + fs::copy(backup_path, new_snapshot_path)?; + } // Set password to restored secret manager restored_secret_manager.set_password(stronghold_password).await?; diff --git a/sdk/tests/wallet/backup_restore.rs b/sdk/tests/wallet/backup_restore.rs index 663acd7744..648b28fc76 100644 --- a/sdk/tests/wallet/backup_restore.rs +++ b/sdk/tests/wallet/backup_restore.rs @@ -131,6 +131,105 @@ async fn backup_and_restore() -> Result<(), Box> { tear_down(storage_path) } +// Backup and restore with Stronghold and same path +#[ignore] +#[tokio::test] +async fn backup_and_restore_same_path() -> Result<(), Box> { + iota_stronghold::engine::snapshot::try_set_encrypt_work_factor(0).unwrap(); + + let storage_path = "test-storage/backup_and_restore_same_path"; + setup(storage_path)?; + + let client_options = ClientOptions::new().with_node(NODE_LOCAL)?; + + let stronghold_password = "some_hopefully_secure_password".to_owned(); + + // Create directory if not existing, because stronghold panics otherwise + std::fs::create_dir_all(storage_path).ok(); + let stronghold = StrongholdSecretManager::builder() + .password(stronghold_password.clone()) + .build("test-storage/backup_and_restore_same_path/1.stronghold")?; + + stronghold.store_mnemonic(Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string())).await.unwrap(); + + let wallet = Wallet::builder() + .with_secret_manager(SecretManager::Stronghold(stronghold)) + .with_client_options(client_options.clone()) + .with_bip_path(Bip44::new(SHIMMER_COIN_TYPE)) + .with_storage_path("test-storage/backup_and_restore_same_path/1") + .finish() + .await?; + + wallet + .backup_to_stronghold_snapshot( + PathBuf::from("test-storage/backup_and_restore_same_path/backup.stronghold"), + stronghold_password.clone(), + ) + .await?; + + // restore from backup + + let stronghold = StrongholdSecretManager::builder() + .password(stronghold_password.clone()) + .build("test-storage/backup_and_restore_same_path/backup.stronghold")?; + + let restored_wallet = Wallet::builder() + .with_storage_path("test-storage/backup_and_restore_same_path/2") + .with_secret_manager(SecretManager::Stronghold(stronghold)) + .with_client_options(ClientOptions::new().with_ignore_node_health().with_node(NODE_LOCAL)?) + // Build with a different coin type, to check if it gets replaced by the one from the backup + .with_bip_path(Bip44::new(IOTA_COIN_TYPE)) + .finish() + .await?; + + // Correct password works + restored_wallet + .restore_from_stronghold_snapshot( + PathBuf::from("test-storage/backup_and_restore_same_path/backup.stronghold"), + stronghold_password, + None, + None, + ) + .await?; + + // Validate restored data + + // Restored coin type is used + assert_eq!(restored_wallet.bip_path().await.unwrap().coin_type, SHIMMER_COIN_TYPE); + + // compare restored client options + let client_options = restored_wallet.client_options().await; + let node_dto = NodeDto::Node(Node::from(Url::parse(NODE_LOCAL).unwrap())); + assert!(client_options.node_manager_builder.nodes.contains(&node_dto)); + + assert_eq!(wallet.address().await.clone(), restored_wallet.address().await.clone()); + + // secret manager is the same + assert_eq!( + wallet + .secret_manager() + .read() + .await + .generate_ed25519_addresses(GetAddressesOptions { + coin_type: SHIMMER_COIN_TYPE, + range: 0..1, + ..Default::default() + }) + .await?, + restored_wallet + .secret_manager() + .read() + .await + .generate_ed25519_addresses(GetAddressesOptions { + coin_type: SHIMMER_COIN_TYPE, + range: 0..1, + ..Default::default() + }) + .await?, + ); + tear_down(storage_path) +} + // // Backup and restore with Stronghold and MnemonicSecretManager // #[tokio::test] // async fn backup_and_restore_mnemonic_secret_manager() -> Result<(), WalletError> { From e5e80d3f8a4ff0af15148ce8f295b168191e0a46 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 29 Apr 2024 13:45:47 +0200 Subject: [PATCH 02/14] Enable modifying Block Issuer Keys in CLI Wallet (#2235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enable adding block issuer keys in cli wallet * Add Modify Block Issuer Keys in AccountChange Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * Add remove block issuer key command * Fix incorrect info message * Add missing comment on enum variant * Add missing "for" in doc comment * Rename `account` -> `account_id` * Refactor account transition requirement Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * Update sdk/src/wallet/operations/transaction/high_level/account_block_issuer_keys.rs Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * Avoid clone of account_id * Remove required_inputs from begin staking as it's handled by the transition --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Co-authored-by: Thoralf Müller --- cli/src/wallet_cli/mod.rs | 76 +++++++++++++++++- .../transaction_builder/error.rs | 2 + .../block_builder/transaction_builder/mod.rs | 12 ++- .../transaction_builder/transition.rs | 24 ++++++ sdk/src/wallet/mod.rs | 1 + .../high_level/account_block_issuer_keys.rs | 77 +++++++++++++++++++ .../transaction/high_level/create_account.rs | 2 +- .../operations/transaction/high_level/mod.rs | 1 + 8 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 sdk/src/wallet/operations/transaction/high_level/account_block_issuer_keys.rs diff --git a/cli/src/wallet_cli/mod.rs b/cli/src/wallet_cli/mod.rs index 5b81f1363b..16e7fbadd1 100644 --- a/cli/src/wallet_cli/mod.rs +++ b/cli/src/wallet_cli/mod.rs @@ -14,7 +14,7 @@ use iota_sdk::{ address::{AccountAddress, Bech32Address, ToBech32Ext}, mana::ManaAllotment, output::{ - feature::{BlockIssuerKeySource, MetadataFeature}, + feature::{BlockIssuerKeySource, Ed25519PublicKeyHashBlockIssuerKey, MetadataFeature}, unlock_condition::AddressUnlockCondition, AccountId, BasicOutputBuilder, DelegationId, FoundryId, NativeToken, NativeTokensBuilder, NftId, Output, OutputId, TokenId, @@ -26,8 +26,8 @@ use iota_sdk::{ utils::ConvertTo, wallet::{ types::OutputData, BeginStakingParams, ConsolidationParams, CreateDelegationParams, CreateNativeTokenParams, - MintNftParams, OutputsToClaim, ReturnStrategy, SendManaParams, SendNativeTokenParams, SendNftParams, - SendParams, SyncOptions, Wallet, WalletError, + MintNftParams, ModifyAccountBlockIssuerKey, OutputsToClaim, ReturnStrategy, SendManaParams, + SendNativeTokenParams, SendNftParams, SendParams, SyncOptions, Wallet, WalletError, }, U256, }; @@ -196,6 +196,22 @@ pub enum WalletCommand { }, /// Lists the implicit accounts of the wallet. ImplicitAccounts, + /// Adds a block issuer key to an account. + AddBlockIssuerKey { + /// The account to which the key should be added. + account_id: AccountId, + /// The hex-encoded public key to add. + // TODO: Use the actual type somehow? + block_issuer_key: String, + }, + /// Removes a block issuer key from an account. + RemoveBlockIssuerKey { + /// The account from which the key should be removed. + account_id: AccountId, + /// The hex-encoded public key to remove. + // TODO: Use the actual type somehow? + block_issuer_key: String, + }, /// Mint additional native tokens. MintNativeToken { /// Token ID to be minted, e.g. 0x087d205988b733d97fb145ae340e27a8b19554d1ceee64574d7e5ff66c45f69e7a0100000000. @@ -924,6 +940,46 @@ pub async fn implicit_accounts_command(wallet: &Wallet) -> Result<(), Error> { Ok(()) } +// `add-block-issuer-key` command +pub async fn add_block_issuer_key(wallet: &Wallet, account_id: AccountId, issuer_key: &str) -> Result<(), Error> { + let issuer_key: [u8; Ed25519PublicKeyHashBlockIssuerKey::LENGTH] = prefix_hex::decode(issuer_key)?; + let params = ModifyAccountBlockIssuerKey { + account_id, + keys_to_add: vec![Ed25519PublicKeyHashBlockIssuerKey::new(issuer_key).into()], + keys_to_remove: vec![], + }; + + let transaction = wallet.modify_account_output_block_issuer_keys(params, None).await?; + + println_log_info!( + "Block issuer key adding transaction sent:\n{:?}\n{:?}", + transaction.transaction_id, + transaction.block_id + ); + + Ok(()) +} + +// `remove-block-issuer-key` command +pub async fn remove_block_issuer_key(wallet: &Wallet, account_id: AccountId, issuer_key: &str) -> Result<(), Error> { + let issuer_key: [u8; Ed25519PublicKeyHashBlockIssuerKey::LENGTH] = prefix_hex::decode(issuer_key)?; + let params = ModifyAccountBlockIssuerKey { + account_id, + keys_to_add: vec![], + keys_to_remove: vec![Ed25519PublicKeyHashBlockIssuerKey::new(issuer_key).into()], + }; + + let transaction = wallet.modify_account_output_block_issuer_keys(params, None).await?; + + println_log_info!( + "Block issuer key removing transaction sent:\n{:?}\n{:?}", + transaction.transaction_id, + transaction.block_id + ); + + Ok(()) +} + // `melt-native-token` command pub async fn melt_native_token_command(wallet: &Wallet, token_id: TokenId, amount: U256) -> Result<(), Error> { let transaction = wallet.melt_native_token(token_id, amount, None).await?; @@ -1580,6 +1636,20 @@ pub async fn prompt_internal( implicit_account_transition_command(wallet, output_id).await } WalletCommand::ImplicitAccounts => implicit_accounts_command(wallet).await, + WalletCommand::AddBlockIssuerKey { + account_id, + block_issuer_key, + } => { + ensure_password(wallet).await?; + add_block_issuer_key(wallet, account_id, &block_issuer_key).await + } + WalletCommand::RemoveBlockIssuerKey { + account_id, + block_issuer_key, + } => { + ensure_password(wallet).await?; + remove_block_issuer_key(wallet, account_id, &block_issuer_key).await + } WalletCommand::MeltNativeToken { token_id, amount } => { ensure_password(wallet).await?; melt_native_token_command(wallet, token_id, amount).await diff --git a/sdk/src/client/api/block_builder/transaction_builder/error.rs b/sdk/src/client/api/block_builder/transaction_builder/error.rs index 8c3f50e910..22f76bd746 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/error.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/error.rs @@ -82,6 +82,8 @@ pub enum TransactionBuilderError { NoAvailableInputsProvided, #[error("account {0} is not staking")] NotStaking(AccountId), + #[error("account {0} has no block issuer feature")] + MissingBlockIssuerFeature(AccountId), /// Required input is not available. #[error("required input {0} is not available")] RequiredInputIsNotAvailable(OutputId), diff --git a/sdk/src/client/api/block_builder/transaction_builder/mod.rs b/sdk/src/client/api/block_builder/transaction_builder/mod.rs index 3e538a0960..78d32a3d13 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/mod.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/mod.rs @@ -321,6 +321,13 @@ impl TransactionBuilder { // Gets requirements from burn. self.burn_requirements()?; + // Add requirements from transitions. + if let Some(transitions) = &self.transitions { + for account_id in transitions.accounts().keys() { + self.requirements.push(Requirement::Account(*account_id)); + } + } + Ok(()) } @@ -331,7 +338,10 @@ impl TransactionBuilder { // If burn or mana allotments are provided, outputs will be added later, in the other cases it will just // create remainder outputs. if !self.provided_outputs.is_empty() - || (self.burn.is_none() && self.mana_allotments.is_empty() && self.required_inputs.is_empty()) + || (self.burn.is_none() + && self.mana_allotments.is_empty() + && self.required_inputs.is_empty() + && self.transitions.is_none()) { return Err(TransactionBuilderError::InvalidOutputCount(self.provided_outputs.len())); } diff --git a/sdk/src/client/api/block_builder/transaction_builder/transition.rs b/sdk/src/client/api/block_builder/transaction_builder/transition.rs index 8137dabb62..8889001432 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/transition.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/transition.rs @@ -140,6 +140,24 @@ impl TransactionBuilder { } features.retain(|f| !f.is_staking()); } + AccountChange::ModifyBlockIssuerKeys { + keys_to_add, + keys_to_remove, + } => { + if let Some(feature) = features.iter_mut().find(|f| f.is_block_issuer()) { + let block_issuer_feature = feature.as_block_issuer(); + let updated_keys = block_issuer_feature + .block_issuer_keys() + .iter() + .filter(|k| !keys_to_remove.contains(k)) + .chain(keys_to_add) + .cloned() + .collect::>(); + *feature = BlockIssuerFeature::new(block_issuer_feature.expiry_slot(), updated_keys)?.into(); + } else { + return Err(TransactionBuilderError::MissingBlockIssuerFeature(account_id)); + } + } } } @@ -308,6 +326,12 @@ pub enum AccountChange { additional_epochs: u32, }, EndStaking, + ModifyBlockIssuerKeys { + /// The keys that will be added. + keys_to_add: Vec, + /// The keys that will be removed. + keys_to_remove: Vec, + }, } /// A type to specify intended transitions. diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index 8b704305ab..746f18c39c 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -51,6 +51,7 @@ pub use self::{ }, transaction::{ high_level::{ + account_block_issuer_keys::ModifyAccountBlockIssuerKey, create_account::CreateAccountParams, delegation::create::{ CreateDelegationParams, CreateDelegationTransaction, PreparedCreateDelegationTransaction, diff --git a/sdk/src/wallet/operations/transaction/high_level/account_block_issuer_keys.rs b/sdk/src/wallet/operations/transaction/high_level/account_block_issuer_keys.rs new file mode 100644 index 0000000000..e36eaa4bb7 --- /dev/null +++ b/sdk/src/wallet/operations/transaction/high_level/account_block_issuer_keys.rs @@ -0,0 +1,77 @@ +// Copyright 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +use crate::{ + client::{ + api::{ + transaction_builder::{transition::AccountChange, Transitions}, + PreparedTransactionData, + }, + secret::SecretManage, + ClientError, + }, + types::block::output::{feature::BlockIssuerKey, AccountId}, + wallet::{operations::transaction::TransactionOptions, types::TransactionWithMetadata, Wallet, WalletError}, +}; + +/// Params for `modify_account_output_block_issuer_keys()` +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ModifyAccountBlockIssuerKey { + pub account_id: AccountId, + /// The keys that will be added. + pub keys_to_add: Vec, + /// The keys that will be removed. + pub keys_to_remove: Vec, +} + +impl Wallet +where + WalletError: From, + ClientError: From, +{ + pub async fn modify_account_output_block_issuer_keys( + &self, + params: ModifyAccountBlockIssuerKey, + options: impl Into> + Send, + ) -> Result { + let options = options.into(); + let prepared_transaction = self + .prepare_modify_account_output_block_issuer_keys(params, options.clone()) + .await?; + + self.sign_and_submit_transaction(prepared_transaction, options).await + } + + /// Prepares the transaction for [Wallet::create_account_output()]. + pub async fn prepare_modify_account_output_block_issuer_keys( + &self, + params: ModifyAccountBlockIssuerKey, + options: impl Into> + Send, + ) -> Result { + log::debug!("[TRANSACTION] prepare_modify_account_output_block_issuer_keys"); + + let change = AccountChange::ModifyBlockIssuerKeys { + keys_to_add: params.keys_to_add, + keys_to_remove: params.keys_to_remove, + }; + + let account_id = params.account_id; + + let mut options = options.into(); + if let Some(options) = options.as_mut() { + if let Some(transitions) = options.transitions.take() { + options.transitions = Some(transitions.add_account(account_id, change)); + } + } else { + options.replace(TransactionOptions { + transitions: Some(Transitions::new().add_account(account_id, change)), + ..Default::default() + }); + } + + self.prepare_send_outputs(None, options).await + } +} diff --git a/sdk/src/wallet/operations/transaction/high_level/create_account.rs b/sdk/src/wallet/operations/transaction/high_level/create_account.rs index fd9d167e72..372035a376 100644 --- a/sdk/src/wallet/operations/transaction/high_level/create_account.rs +++ b/sdk/src/wallet/operations/transaction/high_level/create_account.rs @@ -18,7 +18,7 @@ use crate::{ }, }; -/// Params `create_account_output()` +/// Params for `create_account_output()` #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CreateAccountParams { diff --git a/sdk/src/wallet/operations/transaction/high_level/mod.rs b/sdk/src/wallet/operations/transaction/high_level/mod.rs index 0312a91efe..5f0189596d 100644 --- a/sdk/src/wallet/operations/transaction/high_level/mod.rs +++ b/sdk/src/wallet/operations/transaction/high_level/mod.rs @@ -1,6 +1,7 @@ // Copyright 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +pub(crate) mod account_block_issuer_keys; pub(crate) mod allot_mana; pub(crate) mod burning_melting; pub(crate) mod create_account; From 1a231ee0ce9e859ddbb5911bc8b65da217c9bc17 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:57:33 +0200 Subject: [PATCH 03/14] Fix staking amount (#2238) Co-authored-by: Thibault Martinez --- .../api/block_builder/transaction_builder/transition.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sdk/src/client/api/block_builder/transaction_builder/transition.rs b/sdk/src/client/api/block_builder/transaction_builder/transition.rs index 8889001432..f7cb4eef02 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/transition.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/transition.rs @@ -69,6 +69,8 @@ impl TransactionBuilder { .cloned() .collect::>(); + let mut new_amount = None; + if let Some(change) = self.transitions.as_ref().and_then(|t| t.accounts.get(&account_id)) { match change { AccountChange::BeginStaking { @@ -83,6 +85,7 @@ impl TransactionBuilder { self.protocol_parameters .past_bounded_slot(self.latest_slot_commitment_id), ); + new_amount = Some(*staked_amount); features.push( StakingFeature::new( *staked_amount, @@ -101,6 +104,7 @@ impl TransactionBuilder { .protocol_parameters .future_bounded_epoch(self.latest_slot_commitment_id); let staking_feature = feature.as_staking(); + new_amount = Some(staking_feature.staked_amount()); // Just extend the end epoch if it's still possible if future_bounded_epoch <= staking_feature.end_epoch() { *feature = StakingFeature::new( @@ -162,11 +166,14 @@ impl TransactionBuilder { } let mut builder = AccountOutputBuilder::from(input) - .with_minimum_amount(self.protocol_parameters.storage_score_parameters()) .with_mana(0) .with_account_id(account_id) .with_foundry_counter(u32::max(highest_foundry_serial_number, input.foundry_counter())) .with_features(features); + match new_amount { + Some(amount) => builder = builder.with_amount(amount), + None => builder = builder.with_minimum_amount(self.protocol_parameters.storage_score_parameters()), + } // Block issuers cannot move their mana elsewhere. if input.is_block_issuer() { From 02849504d0cf359b624f140cd2dd91cb933be821 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Mon, 29 Apr 2024 19:42:42 +0200 Subject: [PATCH 04/14] Don't set with_minimum_amount for accounts when they have a staking feature (#2240) --- .../api/block_builder/transaction_builder/transition.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sdk/src/client/api/block_builder/transaction_builder/transition.rs b/sdk/src/client/api/block_builder/transaction_builder/transition.rs index f7cb4eef02..7cbcdcd690 100644 --- a/sdk/src/client/api/block_builder/transaction_builder/transition.rs +++ b/sdk/src/client/api/block_builder/transaction_builder/transition.rs @@ -172,7 +172,11 @@ impl TransactionBuilder { .with_features(features); match new_amount { Some(amount) => builder = builder.with_amount(amount), - None => builder = builder.with_minimum_amount(self.protocol_parameters.storage_score_parameters()), + None => { + if input.features().staking().is_none() { + builder = builder.with_minimum_amount(self.protocol_parameters.storage_score_parameters()); + } + } } // Block issuers cannot move their mana elsewhere. From 19294a7255db8e4599719cb0b5d2d6bbd65fd092 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 29 Apr 2024 21:19:58 +0200 Subject: [PATCH 05/14] Prepare 2.0.0-alpha.1 release (#2239) --- Cargo.lock | 2 +- sdk/CHANGELOG.md | 4 ++++ sdk/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dfd996edcd..b4bbf70210 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1652,7 +1652,7 @@ dependencies = [ [[package]] name = "iota-sdk" -version = "1.1.4" +version = "2.0.0-alpha.1" dependencies = [ "anymap", "async-trait", diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index b1f25d5d1b..036004e0d2 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security --> +## 2.0.0-alpha.1 - 2024-04-29 + +Initial alpha release of the 2.0 SDK. + ## 1.1.5 - 2024-MM-DD ### Added diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index b5a18abcb5..948418440f 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iota-sdk" -version = "1.1.4" +version = "2.0.0-alpha.1" authors = ["IOTA Stiftung"] edition = "2021" description = "The IOTA SDK provides developers with a seamless experience to develop on IOTA by providing account abstractions and clients to interact with node APIs." From 3e44c807dd8d28f2a7221e58c58946383676f187 Mon Sep 17 00:00:00 2001 From: /alex/ Date: Tue, 30 Apr 2024 16:29:43 +0200 Subject: [PATCH 06/14] Clean up wallet syncing code (#2108) * const fn * sync options * move instead of clone * combinators * rm unnecessary cloning 1 * rm unnecessary cloning 2 * slices; impl Into