Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix restore_from_stronghold_snapshot() with same source and target path #2234

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/actions/private-tangle/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions bindings/nodejs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion bindings/nodejs/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
12 changes: 9 additions & 3 deletions sdk/src/wallet/core/operations/stronghold_backup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {
Expand Down Expand Up @@ -294,7 +298,9 @@ impl Wallet<StrongholdSecretManager> {
.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?;
Expand Down
99 changes: 99 additions & 0 deletions sdk/tests/wallet/backup_restore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,105 @@ async fn backup_and_restore() -> Result<(), Box<dyn std::error::Error>> {
tear_down(storage_path)
}

// Backup and restore with Stronghold and same path
#[ignore]
#[tokio::test]
async fn backup_and_restore_same_path() -> Result<(), Box<dyn std::error::Error>> {
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> {
Expand Down
Loading