Improve error output from OrchardWallet::get_spend_info

This commit is contained in:
Kris Nuttycombe 2022-03-31 16:53:51 -06:00
parent 6fbcba641d
commit 344aef435d
1 changed files with 64 additions and 42 deletions

View File

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