diff --git a/servers/tests/framework.rs b/servers/tests/framework.rs index 8f9117407..b4c339220 100644 --- a/servers/tests/framework.rs +++ b/servers/tests/framework.rs @@ -349,7 +349,8 @@ impl LocalServerContainer { .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); wallet.keychain = Some(keychain); let parent_id = keychain::ExtKeychain::derive_key_id(2, 0, 0, 0, 0); - let _ = wallet::libwallet::internal::updater::refresh_outputs(&mut wallet, &parent_id); + let _ = + wallet::libwallet::internal::updater::refresh_outputs(&mut wallet, &parent_id, false); wallet::libwallet::internal::updater::retrieve_info(&mut wallet, &parent_id, 1).unwrap() } diff --git a/src/bin/cmd/wallet_args.rs b/src/bin/cmd/wallet_args.rs index 8afca3c25..b4528422f 100644 --- a/src/bin/cmd/wallet_args.rs +++ b/src/bin/cmd/wallet_args.rs @@ -587,7 +587,7 @@ pub fn wallet_command( command::cancel(inst_wallet(), a) } ("restore", Some(_)) => command::restore(inst_wallet()), - ("check_repair", Some(_)) => command::check_repair(inst_wallet()), + ("check", Some(_)) => command::check_repair(inst_wallet()), _ => { let msg = format!("Unknown wallet command, use 'grin help wallet' for details"); return Err(ErrorKind::ArgumentError(msg).into()); diff --git a/src/bin/cmd/wallet_tests.rs b/src/bin/cmd/wallet_tests.rs index 83ccd2993..7a091f3c8 100644 --- a/src/bin/cmd/wallet_tests.rs +++ b/src/bin/cmd/wallet_tests.rs @@ -462,7 +462,7 @@ mod wallet_tests { ]; execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; - let arg_vec = vec!["grin", "wallet", "-p", "password", "check_repair"]; + let arg_vec = vec!["grin", "wallet", "-p", "password", "check"]; execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; // txs and outputs (mostly spit out for a visual in test logs) diff --git a/src/bin/grin.yml b/src/bin/grin.yml index e12c22ee1..772ab41a2 100644 --- a/src/bin/grin.yml +++ b/src/bin/grin.yml @@ -294,6 +294,6 @@ subcommands: takes_value: false - restore: about: Restores a wallet contents from a seed file - - check_repair: + - check: about: Checks a wallet's outputs against a live node, repairing and restoring missing outputs if required diff --git a/wallet/src/command.rs b/wallet/src/command.rs index 22b533b88..2a4412998 100644 --- a/wallet/src/command.rs +++ b/wallet/src/command.rs @@ -501,14 +501,16 @@ pub fn check_repair( wallet: Arc>>, ) -> Result<(), Error> { controller::owner_single_use(wallet.clone(), |api| { + warn!("Starting wallet check...",); + warn!("Updating all wallet outputs, please wait ...",); let result = api.check_repair(); match result { Ok(_) => { - warn!("Wallet check/repair complete",); + warn!("Wallet check complete",); Ok(()) } Err(e) => { - error!("Wallet check/repair failed: {}", e); + error!("Wallet check failed: {}", e); error!("Backtrace: {}", e.backtrace().unwrap()); Err(e) } diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index 9fc0d4fb3..91a7b79d5 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -348,7 +348,7 @@ where let mut validated = false; if refresh_from_node { - validated = self.update_outputs(&mut w); + validated = self.update_outputs(&mut w, false); } let res = Ok(( @@ -426,7 +426,7 @@ where let mut validated = false; if refresh_from_node { - validated = self.update_outputs(&mut w); + validated = self.update_outputs(&mut w, false); } let res = Ok(( @@ -498,7 +498,7 @@ where let mut validated = false; if refresh_from_node { - validated = self.update_outputs(&mut w); + validated = self.update_outputs(&mut w, false); } let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?; @@ -708,7 +708,7 @@ where let mut w = self.wallet.lock(); w.open_with_credentials()?; let parent_key_id = w.parent_key_id(); - if !self.update_outputs(&mut w) { + if !self.update_outputs(&mut w, false) { return Err(ErrorKind::TransactionCancellationError( "Can't contact running Grin node. Not Cancelling.", ))?; @@ -765,7 +765,7 @@ where pub fn check_repair(&mut self) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - self.update_outputs(&mut w); + self.update_outputs(&mut w, true); w.check_repair()?; w.close()?; Ok(()) @@ -792,9 +792,9 @@ where } /// Attempt to update outputs in wallet, return whether it was successful - fn update_outputs(&self, w: &mut W) -> bool { + fn update_outputs(&self, w: &mut W, update_all: bool) -> bool { let parent_key_id = w.parent_key_id(); - match updater::refresh_outputs(&mut *w, &parent_key_id) { + match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) { Ok(_) => true, Err(_) => false, } diff --git a/wallet/src/libwallet/internal/restore.rs b/wallet/src/libwallet/internal/restore.rs index e6524c5e9..56598e025 100644 --- a/wallet/src/libwallet/internal/restore.rs +++ b/wallet/src/libwallet/internal/restore.rs @@ -240,10 +240,11 @@ where res }; - // check all definitive outputs exist in the wallet outputs let mut missing_outs = vec![]; let mut accidental_spend_outs = vec![]; let mut locked_outs = vec![]; + + // check all definitive outputs exist in the wallet outputs for deffo in chain_outs.into_iter() { let matched_out = wallet_outputs.iter().find(|wo| wo.0.key_id == deffo.key_id); match matched_out { diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index f2480303d..a18d6b0a1 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -85,7 +85,7 @@ where // Get lock height let current_height = wallet.w2n_client().get_chain_height()?; // ensure outputs we're selecting are up to date - updater::refresh_outputs(wallet, parent_key_id)?; + updater::refresh_outputs(wallet, parent_key_id, false)?; let lock_height = current_height; diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index b4de97486..74ff6293a 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -120,6 +120,7 @@ where pub fn refresh_outputs( wallet: &mut T, parent_key_id: &Identifier, + update_all: bool, ) -> Result<(), Error> where T: WalletBackend, @@ -127,7 +128,7 @@ where K: Keychain, { let height = wallet.w2n_client().get_chain_height()?; - refresh_output_state(wallet, height, parent_key_id)?; + refresh_output_state(wallet, height, parent_key_id, update_all)?; Ok(()) } @@ -136,6 +137,7 @@ where pub fn map_wallet_outputs( wallet: &mut T, parent_key_id: &Identifier, + update_all: bool, ) -> Result, Error> where T: WalletBackend, @@ -144,9 +146,33 @@ where { let mut wallet_outputs: HashMap = HashMap::new(); let keychain = wallet.keychain().clone(); - let unspents = wallet + let unspents: Vec = wallet .iter() - .filter(|x| x.root_key_id == *parent_key_id && x.status != OutputStatus::Spent); + .filter(|x| x.root_key_id == *parent_key_id && x.status != OutputStatus::Spent) + .collect(); + + // Only select outputs that are actually involved in an outstanding transaction + let unspents: Vec = match update_all { + false => unspents + .into_iter() + .filter(|x| match x.tx_log_entry.as_ref() { + Some(t) => { + let entries = retrieve_txs(wallet, Some(*t), None, Some(&parent_key_id)); + match entries { + Err(_) => true, + Ok(e) => { + e.len() > 0 + && !e[0].confirmed && (e[0].tx_type == TxLogEntryType::TxReceived + || e[0].tx_type == TxLogEntryType::TxSent) + } + } + } + None => true, + }) + .collect(), + true => unspents, + }; + for out in unspents { let commit = keychain.commit(out.value, &out.key_id)?; wallet_outputs.insert(commit, out.key_id.clone()); @@ -275,6 +301,7 @@ fn refresh_output_state( wallet: &mut T, height: u64, parent_key_id: &Identifier, + update_all: bool, ) -> Result<(), Error> where T: WalletBackend, @@ -285,7 +312,7 @@ where // build a local map of wallet outputs keyed by commit // and a list of outputs we want to query the node for - let wallet_outputs = map_wallet_outputs(wallet, parent_key_id)?; + let wallet_outputs = map_wallet_outputs(wallet, parent_key_id, update_all)?; let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect();