diff --git a/crates/tests-e2e/src/app.rs b/crates/tests-e2e/src/app.rs index 2d7618bef..b53187739 100644 --- a/crates/tests-e2e/src/app.rs +++ b/crates/tests-e2e/src/app.rs @@ -1,6 +1,7 @@ use crate::test_subscriber::TestSubscriber; use crate::test_subscriber::ThreadSafeSenders; use crate::wait_until; +use native::api; use tempfile::TempDir; pub struct AppHandle { @@ -11,7 +12,13 @@ pub struct AppHandle { _tx: ThreadSafeSenders, } -pub async fn run_app() -> AppHandle { +impl AppHandle { + pub fn stop(&self) { + self._handle.abort() + } +} + +pub async fn run_app(seed_phrase: Option>) -> AppHandle { let app_dir = TempDir::new().expect("Failed to create temporary directory"); let seed_dir = TempDir::new().expect("Failed to create temporary directory"); let _app_handle = { @@ -25,13 +32,23 @@ pub async fn run_app() -> AppHandle { let app_dir = as_string(&app_dir); let seed_dir = as_string(&seed_dir); - tokio::task::spawn_blocking({ - let seed_dir = seed_dir.clone(); - move || { - native::api::set_config(test_config(), app_dir, seed_dir) - .expect("Could not configure app") - } - }); + native::api::set_config(test_config(), app_dir, seed_dir.clone()) + .expect("Could not configure app"); + + if let Some(seed_phrase) = seed_phrase { + tokio::task::spawn_blocking({ + let seed_dir = seed_dir.clone(); + move || { + api::restore_from_seed_phrase( + seed_phrase.join(" "), + format!("{seed_dir}/regtest/seed"), + ) + .expect("Failed to restore from seed phrase"); + } + }) + .await + .expect("Failed to finish restore from seed phrase"); + } tokio::task::spawn_blocking(move || { native::api::run( diff --git a/crates/tests-e2e/src/setup.rs b/crates/tests-e2e/src/setup.rs index 67efaef52..8e5cea8b9 100644 --- a/crates/tests-e2e/src/setup.rs +++ b/crates/tests-e2e/src/setup.rs @@ -43,7 +43,7 @@ impl TestSetup { .await .expect("To be able to sync coordinator wallet"); - let app = run_app().await; + let app = run_app(None).await; assert_eq!( app.rx diff --git a/crates/tests-e2e/tests/basic.rs b/crates/tests-e2e/tests/basic.rs index b76c64395..68b962567 100644 --- a/crates/tests-e2e/tests/basic.rs +++ b/crates/tests-e2e/tests/basic.rs @@ -26,7 +26,7 @@ async fn app_can_be_funded_with_lnd_faucet() -> Result<()> { bitcoind.mine(1).await.unwrap(); coordinator.sync_wallet().await.unwrap(); - let app = run_app().await; + let app = run_app(None).await; // Unfunded wallet should be empty assert_eq!(app.rx.wallet_info().unwrap().balances.lightning, 0); diff --git a/crates/tests-e2e/tests/restore_from_backup.rs b/crates/tests-e2e/tests/restore_from_backup.rs new file mode 100644 index 000000000..b07047cc2 --- /dev/null +++ b/crates/tests-e2e/tests/restore_from_backup.rs @@ -0,0 +1,51 @@ +use anyhow::Result; +use native::api; +use tests_e2e::app::run_app; +use tests_e2e::logger::init_tracing; +use tests_e2e::setup; +use tokio::task::spawn_blocking; + +#[tokio::test] +#[ignore = "need to be run with 'just e2e' command"] +async fn app_can_be_restored_from_a_backup() -> Result<()> { + init_tracing(); + + let test = setup::TestSetup::new_with_open_position().await; + + let seed_phrase = api::get_seed_phrase(); + + let ln_balance = test + .app + .rx + .wallet_info() + .expect("to have wallet info") + .balances + .lightning; + + // kill the app + test.app.stop(); + tracing::info!("Shutting down app!"); + + let app = run_app(Some(seed_phrase.0)).await; + + assert_eq!( + app.rx + .wallet_info() + .expect("to have wallet info") + .balances + .lightning, + ln_balance + ); + + let positions = spawn_blocking(|| api::get_positions().expect("Failed to get positions")) + .await + .unwrap(); + assert_eq!(1, positions.len()); + + // Test if full backup is running without errors + spawn_blocking(|| api::full_backup().expect("Failed to run full backup")) + .await + .unwrap(); + + Ok(()) +} diff --git a/mobile/native/src/api.rs b/mobile/native/src/api.rs index 0c5070b25..bdbb910fb 100644 --- a/mobile/native/src/api.rs +++ b/mobile/native/src/api.rs @@ -412,10 +412,14 @@ pub fn get_seed_phrase() -> SyncReturn> { SyncReturn(ln_dlc::get_seed_phrase()) } -pub fn restore_from_seed_phrase(seed_phrase: String, target_seed_file_path: String) -> Result<()> { +#[tokio::main(flavor = "current_thread")] +pub async fn restore_from_seed_phrase( + seed_phrase: String, + target_seed_file_path: String, +) -> Result<()> { let file_path = PathBuf::from(target_seed_file_path); tracing::info!("Restoring seed from phrase to {:?}", file_path); - ln_dlc::restore_from_mnemonic(&seed_phrase, file_path.as_path())?; + ln_dlc::restore_from_mnemonic(&seed_phrase, file_path.as_path()).await?; Ok(()) } diff --git a/mobile/native/src/backup.rs b/mobile/native/src/backup.rs index 6aeb19949..69f496458 100644 --- a/mobile/native/src/backup.rs +++ b/mobile/native/src/backup.rs @@ -7,7 +7,6 @@ use crate::event::EventType; use anyhow::bail; use anyhow::ensure; use anyhow::Result; -use bitcoin::hashes::hex::ToHex; use coordinator_commons::Backup; use coordinator_commons::DeleteBackup; use coordinator_commons::Restore; @@ -129,7 +128,7 @@ impl RemoteBackupClient { } pub fn backup(&self, key: String, value: Vec) -> RemoteHandle<()> { - tracing::trace!("Creating backup for {key} with value {}", value.to_hex()); + tracing::trace!("Creating backup for {key}"); let (fut, remote_handle) = { let client = self.inner.clone(); let cipher = self.cipher.clone(); @@ -214,11 +213,7 @@ impl RemoteBackupClient { let decrypted_value = cipher.decrypt(restore.value)?; match restore.kind { RestoreKind::LN => { - tracing::debug!( - "Restoring {} with value {}", - restore.key, - decrypted_value.to_hex() - ); + tracing::debug!("Restoring {}", restore.key); let dest_file = Path::new(&data_dir) .join(network.to_string()) .join(restore.key.clone()); @@ -227,11 +222,7 @@ impl RemoteBackupClient { fs::write(dest_file.as_path(), decrypted_value)?; } RestoreKind::DLC => { - tracing::debug!( - "Restoring {} with value {}", - restore.key, - decrypted_value.to_hex() - ); + tracing::debug!("Restoring {}", restore.key); let keys = restore.key.split('/').collect::>(); ensure!(keys.len() == 2, "dlc key is too short"); diff --git a/mobile/native/src/ln_dlc/mod.rs b/mobile/native/src/ln_dlc/mod.rs index c351e5577..f5f6e679b 100644 --- a/mobile/native/src/ln_dlc/mod.rs +++ b/mobile/native/src/ln_dlc/mod.rs @@ -415,12 +415,17 @@ pub fn init_new_mnemonic(target_seed_file: &Path) -> Result<()> { Ok(()) } -#[tokio::main(flavor = "current_thread")] pub async fn restore_from_mnemonic(seed_words: &str, target_seed_file: &Path) -> Result<()> { let seed = Bip39Seed::restore_from_mnemonic(seed_words, target_seed_file)?; crate::state::set_seed(seed); - let storage = get_storage(); + let storage = TenTenOneNodeStorage::new( + config::get_data_dir(), + config::get_network(), + get_node_key(), + ); + tracing::info!("Initialized 10101 storage!"); + crate::state::set_storage(storage.clone()); storage.client.restore(storage.dlc_storage).await } diff --git a/mobile/native/src/storage.rs b/mobile/native/src/storage.rs index 59ced7f69..c7748c14a 100644 --- a/mobile/native/src/storage.rs +++ b/mobile/native/src/storage.rs @@ -199,11 +199,7 @@ impl KVStorePersister for TenTenOneNodeStorage { self.ln_storage.persist(key, value)?; let value = value.encode(); - tracing::trace!( - "Creating a backup of {:?} with value {}", - key, - value.to_hex() - ); + tracing::trace!("Creating a backup of {:?}", key); // Let the backup run asynchronously we don't really care if it is successful or not as the // next persist will fix the issue. Note, if we want to handle failed backup attempts we