Improve error output from OrchardWallet::get_spend_info
This commit is contained in:
parent
6fbcba641d
commit
344aef435d
|
@ -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]
|
||||||
|
|
Loading…
Reference in New Issue