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))?;
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 self.key_store
.ivk_for_address(&dnote.note.recipient()) .viewing_keys
.and_then(|ivk| self.key_store.viewing_keys.get(ivk)) .get(ivk)
.zip( .ok_or_else(|| SpendRetrievalError::FvkNotFound(ivk.clone()))
self.wallet_note_positions })?;
.get(&outpoint.txid)
.and_then(|tx_notes| tx_notes.note_positions.get(&outpoint.action_idx)), let position = self
) .wallet_note_positions
.map(|(fvk, position)| { .get(&outpoint.txid)
assert_eq!( .and_then(|tx_notes| tx_notes.note_positions.get(&outpoint.action_idx))
self.witness_tree .ok_or(SpendRetrievalError::NoteNotPositioned(outpoint))?;
.get_witnessed_leaf(*position)
.expect("tree has witnessed the leaf for this note."), assert_eq!(
&MerkleHashOrchard::from_cmx(&dnote.note.commitment().into()), self.witness_tree
); .get_witnessed_leaf(*position)
let path = self .expect("tree has witnessed the leaf for this note."),
.witness_tree &MerkleHashOrchard::from_cmx(&dnote.note.commitment().into()),
.authentication_path(*position) );
.expect("wallet always has paths to positioned notes");
OrchardSpendInfo::from_parts( let path = self
fvk.clone(), .witness_tree
dnote.note, .authentication_path(*position)
MerklePath::from_parts( .expect("wallet always has paths to positioned notes");
u64::from(*position).try_into().unwrap(),
path.try_into().unwrap(), 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 }; 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()
}
} }
} }