diff --git a/bindings/nodejs/CHANGELOG.md b/bindings/nodejs/CHANGELOG.md index 55f2b6cf25..18dbcf307b 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.7 - 2024-04-19 + +### Fixed + +- Stronghold backup/restore; + ## 2.0.0-alpha.6 - 2024-04-17 ### Fixed diff --git a/sdk/src/wallet/core/operations/stronghold_backup/stronghold_snapshot.rs b/sdk/src/wallet/core/operations/stronghold_backup/stronghold_snapshot.rs index 0be48f2efe..0bc6a88e2a 100644 --- a/sdk/src/wallet/core/operations/stronghold_backup/stronghold_snapshot.rs +++ b/sdk/src/wallet/core/operations/stronghold_backup/stronghold_snapshot.rs @@ -41,15 +41,15 @@ impl Wallet { } // Store the wallet address - stronghold - .set(WALLET_ADDRESS_KEY, self.address().await.as_ref()) - .await?; + stronghold.set(WALLET_ADDRESS_KEY, &self.address().await).await?; // Store the wallet bip path stronghold.set(WALLET_BIP_PATH_KEY, &self.bip_path().await).await?; // Store the wallet alias - stronghold.set(WALLET_ALIAS_KEY, &self.alias().await).await?; + if let Some(alias) = self.alias().await { + stronghold.set(WALLET_ALIAS_KEY, &alias).await?; + } let serialized_wallet_ledger = serde_json::to_value(&WalletLedgerDto::from(&*self.ledger.read().await))?; stronghold.set(WALLET_LEDGER_KEY, &serialized_wallet_ledger).await?; @@ -71,6 +71,7 @@ pub(crate) async fn read_fields_from_stronghold_snapshot { + log::debug!("[read_fields_from_stronghold_snapshot]"); migrate(stronghold).await?; // Get client_options diff --git a/sdk/tests/wallet/backup_restore.rs b/sdk/tests/wallet/backup_restore.rs index 779c7a41d2..663acd7744 100644 --- a/sdk/tests/wallet/backup_restore.rs +++ b/sdk/tests/wallet/backup_restore.rs @@ -1,130 +1,135 @@ -// Copyright 2022 IOTA Stiftung +// Copyright 2022-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -// use std::path::PathBuf; - -// use crypto::keys::bip39::Mnemonic; -// use iota_sdk::{ -// client::{ -// api::GetAddressesOptions, -// constants::{IOTA_COIN_TYPE, SHIMMER_COIN_TYPE}, -// node_manager::node::{Node, NodeDto}, -// secret::{mnemonic::MnemonicSecretManager, stronghold::StrongholdSecretManager, SecretManager}, -// }, -// crypto::keys::bip44::Bip44, -// wallet::{ClientOptions, Result, Wallet}, -// }; -// use pretty_assertions::assert_eq; -// use url::Url; - -// use crate::wallet::common::{setup, tear_down, NODE_LOCAL, NODE_OTHER}; - -// // Backup and restore with Stronghold -// #[tokio::test] -// async fn backup_and_restore() -> Result<(), WalletError> { -// iota_stronghold::engine::snapshot::try_set_encrypt_work_factor(0).unwrap(); - -// let storage_path = "test-storage/backup_and_restore"; -// 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/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/1") -// .finish() -// .await?; - -// wallet -// .backup( -// PathBuf::from("test-storage/backup_and_restore/backup.stronghold"), -// stronghold_password.clone(), -// ) -// .await?; - -// // restore from backup - -// let stronghold = StrongholdSecretManager::builder().build("test-storage/backup_and_restore/2.stronghold")?; - -// let restored_wallet = Wallet::builder() -// .with_storage_path("test-storage/backup_and_restore/2") -// .with_secret_manager(SecretManager::Stronghold(stronghold)) -// .with_client_options(ClientOptions::new().with_node(NODE_OTHER)?) -// // 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?; - -// // Wrong password fails -// restored_wallet -// .restore_backup( -// PathBuf::from("test-storage/backup_and_restore/backup.stronghold"), -// "wrong password".to_owned(), -// None, -// None, -// ) -// .await -// .unwrap_err(); - -// // Correct password works, even after trying with a wrong one before -// restored_wallet -// .restore_backup( -// PathBuf::from("test-storage/backup_and_restore/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().clone(), restored_wallet.address().clone()); - -// // secret manager is the same -// assert_eq!( -// wallet -// .get_secret_manager() -// .read() -// .await -// .generate_ed25519_addresses(GetAddressesOptions { -// coin_type: SHIMMER_COIN_TYPE, -// range: 0..1, -// ..Default::default() -// }) -// .await?, -// restored_wallet -// .get_secret_manager() -// .read() -// .await -// .generate_ed25519_addresses(GetAddressesOptions { -// coin_type: SHIMMER_COIN_TYPE, -// range: 0..1, -// ..Default::default() -// }) -// .await?, -// ); -// tear_down(storage_path) -// } +use std::path::PathBuf; + +use crypto::keys::bip39::Mnemonic; +use iota_sdk::{ + client::{ + api::GetAddressesOptions, + constants::{IOTA_COIN_TYPE, SHIMMER_COIN_TYPE}, + node_manager::node::{Node, NodeDto}, + secret::{stronghold::StrongholdSecretManager, SecretManager}, + }, + crypto::keys::bip44::Bip44, + wallet::{ClientOptions, Wallet}, +}; +use pretty_assertions::assert_eq; +use url::Url; + +use crate::wallet::common::{setup, tear_down, NODE_LOCAL}; + +// Backup and restore with Stronghold +#[ignore] +#[tokio::test] +async fn backup_and_restore() -> Result<(), Box> { + iota_stronghold::engine::snapshot::try_set_encrypt_work_factor(0).unwrap(); + + let storage_path = "test-storage/backup_and_restore"; + 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/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/1") + .finish() + .await?; + + wallet + .backup_to_stronghold_snapshot( + PathBuf::from("test-storage/backup_and_restore/backup.stronghold"), + stronghold_password.clone(), + ) + .await?; + + // restore from backup + + let stronghold = StrongholdSecretManager::builder() + .password(stronghold_password.clone()) + .build("test-storage/backup_and_restore/2.stronghold")?; + + stronghold.store_mnemonic(Mnemonic::from("surprise own liquid gold embrace indoor cereal magnet wink purse similar unusual setup woman catch chuckle critic wet weasel ahead wasp cruise luggage pig".to_string())).await.unwrap(); + + let restored_wallet = Wallet::builder() + .with_storage_path("test-storage/backup_and_restore/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?; + + // Wrong password fails + restored_wallet + .restore_from_stronghold_snapshot( + PathBuf::from("test-storage/backup_and_restore/backup.stronghold"), + "wrong password".to_owned(), + None, + None, + ) + .await + .unwrap_err(); + + // Correct password works, even after trying with a wrong one before + restored_wallet + .restore_from_stronghold_snapshot( + PathBuf::from("test-storage/backup_and_restore/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]