diff --git a/src/rust/src/wallet.rs b/src/rust/src/wallet.rs index 858ff4997..526d07327 100644 --- a/src/rust/src/wallet.rs +++ b/src/rust/src/wallet.rs @@ -42,14 +42,14 @@ pub struct LastObserved { } /// A pointer to a particular action in an Orchard transaction output. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct OutPoint { txid: TxId, action_idx: usize, } /// A pointer to a previous output being spent in an Orchard action. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct InPoint { txid: TxId, action_idx: usize, @@ -195,6 +195,14 @@ pub enum BundleLoadError { InvalidActionIndex(usize), } +#[derive(Debug, Clone)] +pub enum SpendRetrievalError { + DecryptedNoteNotFound(OutPoint), + NoIvkForRecipient(Address), + FvkNotFound(IncomingViewingKey), + NoteNotPositioned(OutPoint), +} + /// A struct used to return metadata about how a bundle was determined /// to be involved with the wallet. #[derive(Debug, Clone)] @@ -652,42 +660,55 @@ impl Wallet { /// /// Returns `None` if the `OutPoint` is not known to the wallet, or the Orchard bundle /// containing the note has not been passed to `Wallet::append_bundle_commitments`. - pub fn get_spend_info(&self, outpoint: OutPoint) -> Option { + pub fn get_spend_info( + &self, + outpoint: OutPoint, + ) -> Result { // TODO: Take `confirmations` parameter and obtain the Merkle path to the root at // that checkpoint, not the latest root. - self.wallet_received_notes + let dnote = self + .wallet_received_notes .get(&outpoint.txid) .and_then(|tx_notes| tx_notes.decrypted_notes.get(&outpoint.action_idx)) - .and_then(|dnote| { + .ok_or(SpendRetrievalError::DecryptedNoteNotFound(outpoint))?; + + let fvk = self + .key_store + .ivk_for_address(&dnote.note.recipient()) + .ok_or_else(|| SpendRetrievalError::NoIvkForRecipient(dnote.note.recipient())) + .and_then(|ivk| { self.key_store - .ivk_for_address(&dnote.note.recipient()) - .and_then(|ivk| self.key_store.viewing_keys.get(ivk)) - .zip( - self.wallet_note_positions - .get(&outpoint.txid) - .and_then(|tx_notes| tx_notes.note_positions.get(&outpoint.action_idx)), - ) - .map(|(fvk, position)| { - assert_eq!( - self.witness_tree - .get_witnessed_leaf(*position) - .expect("tree has witnessed the leaf for this note."), - &MerkleHashOrchard::from_cmx(&dnote.note.commitment().into()), - ); - let path = self - .witness_tree - .authentication_path(*position) - .expect("wallet always has paths to positioned notes"); - OrchardSpendInfo::from_parts( - fvk.clone(), - dnote.note, - MerklePath::from_parts( - u64::from(*position).try_into().unwrap(), - path.try_into().unwrap(), - ), - ) - }) - }) + .viewing_keys + .get(ivk) + .ok_or_else(|| SpendRetrievalError::FvkNotFound(ivk.clone())) + })?; + + let position = self + .wallet_note_positions + .get(&outpoint.txid) + .and_then(|tx_notes| tx_notes.note_positions.get(&outpoint.action_idx)) + .ok_or(SpendRetrievalError::NoteNotPositioned(outpoint))?; + + assert_eq!( + self.witness_tree + .get_witnessed_leaf(*position) + .expect("tree has witnessed the leaf for this note."), + &MerkleHashOrchard::from_cmx(&dnote.note.commitment().into()), + ); + + let path = self + .witness_tree + .authentication_path(*position) + .expect("wallet always has paths to positioned notes"); + + Ok(OrchardSpendInfo::from_parts( + fvk.clone(), + dnote.note, + MerklePath::from_parts( + u64::from(*position).try_into().unwrap(), + path.try_into().unwrap(), + ), + )) } } @@ -1158,15 +1179,16 @@ pub extern "C" fn orchard_wallet_get_spend_info( let outpoint = OutPoint { txid, action_idx }; - if let Some(ret) = wallet.get_spend_info(outpoint) { - Box::into_raw(Box::new(ret)) - } else { - tracing::error!( - "Requested note in action {} of transaction {} wasn't in the wallet", - outpoint.action_idx, - outpoint.txid - ); - ptr::null_mut() + match wallet.get_spend_info(outpoint) { + Ok(ret) => Box::into_raw(Box::new(ret)), + Err(e) => { + tracing::error!( + "Error obtaining spend info for outpoint {:?}: {:?}", + outpoint, + e + ); + ptr::null_mut() + } } }