diff --git a/zingolib/src/wallet/transaction_record.rs b/zingolib/src/wallet/transaction_record.rs index 91af1b02f..6507e0ce2 100644 --- a/zingolib/src/wallet/transaction_record.rs +++ b/zingolib/src/wallet/transaction_record.rs @@ -368,8 +368,9 @@ impl TransactionRecord { &self, sources: &[zcash_client_backend::ShieldedProtocol], exclude: &[NoteId], - ) -> Vec<(NoteId, u64)> { + ) -> Result, ()> { let mut all = vec![]; + let mut missing_output_index = false; if sources.contains(&Sapling) { self.sapling_notes.iter().for_each(|zingo_sapling_note| { if zingo_sapling_note.is_unspent() { @@ -380,6 +381,7 @@ impl TransactionRecord { } } else { println!("note has no index"); + missing_output_index = true; } } }); @@ -394,11 +396,16 @@ impl TransactionRecord { } } else { println!("note has no index"); + missing_output_index = true; } } }); } - all + if missing_output_index { + Err(()) + } else { + Ok(all) + } } } // read/write @@ -979,8 +986,9 @@ mod tests { fn select_spendable_note_ids_and_values() { let transaction_record = nine_note_transaction_record_default(); - let unspent_ids_and_values = - transaction_record.get_spendable_note_ids_and_values(&[Sapling, Orchard], &[]); + let unspent_ids_and_values = transaction_record + .get_spendable_note_ids_and_values(&[Sapling, Orchard], &[]) + .unwrap(); assert_eq!( unspent_ids_and_values, diff --git a/zingolib/src/wallet/transaction_records_by_id.rs b/zingolib/src/wallet/transaction_records_by_id.rs index a93c33fa5..2179874a6 100644 --- a/zingolib/src/wallet/transaction_records_by_id.rs +++ b/zingolib/src/wallet/transaction_records_by_id.rs @@ -668,19 +668,33 @@ impl TransactionRecordsById { sources: &[zcash_client_backend::ShieldedProtocol], anchor_height: zcash_primitives::consensus::BlockHeight, exclude: &[NoteId], - ) -> Vec<(NoteId, u64)> { - self.values() + ) -> Result, Vec> { + let mut missing_output_index = vec![]; + let ok = self + .values() .flat_map(|transaction_record| { if transaction_record .status .is_confirmed_before_or_at(&anchor_height) { - transaction_record.get_spendable_note_ids_and_values(sources, exclude) + if let Ok(notes_from_tx) = + transaction_record.get_spendable_note_ids_and_values(sources, exclude) + { + notes_from_tx + } else { + missing_output_index.push(transaction_record.txid); + vec![] + } } else { vec![] } }) - .collect() + .collect(); + if missing_output_index.is_empty() { + Ok(ok) + } else { + Err(missing_output_index) + } } } diff --git a/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs b/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs index 6ed989877..7f5103333 100644 --- a/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs +++ b/zingolib/src/wallet/transaction_records_by_id/trait_inputsource.rs @@ -12,6 +12,7 @@ use zcash_primitives::{ transaction::{ components::{amount::NonNegativeAmount, TxOut}, fees::zip317::MARGINAL_FEE, + TxId, }, }; @@ -36,6 +37,9 @@ pub enum InputSourceError { /// Value outside the valid range of zatoshis #[error("Value outside valid range of zatoshis. {0:?}")] InvalidValue(BalanceError), + /// Wallet data is out of date + #[error("Output index data is missing! Wallet data is out of date, please rescan.")] + MissingOutputIndexes(Vec), } // Calculate remaining difference between target and selected. @@ -191,6 +195,7 @@ impl InputSource for TransactionRecordsById { ) -> Result, Self::Error> { let mut unselected = self .get_spendable_note_ids_and_values(sources, anchor_height, exclude) + .map_err(InputSourceError::MissingOutputIndexes)? .into_iter() .map(|(id, value)| NonNegativeAmount::from_u64(value).map(|value| (id, value))) .collect::, _>>()