librustzcash/zcash_client_backend/src/data_api/error.rs

236 lines
8.5 KiB
Rust

//! Types for wallet error handling.
use std::error;
use std::fmt::{self, Debug, Display};
use shardtree::error::ShardTreeError;
use zcash_address::ConversionError;
use zcash_primitives::transaction::components::amount::NonNegativeAmount;
use zcash_primitives::transaction::{
builder,
components::{amount::BalanceError, transparent},
};
use crate::address::UnifiedAddress;
use crate::data_api::wallet::input_selection::InputSelectorError;
use crate::proposal::ProposalError;
use crate::PoolType;
#[cfg(feature = "transparent-inputs")]
use zcash_primitives::legacy::TransparentAddress;
use crate::wallet::NoteId;
/// Errors that can occur as a consequence of wallet operations.
#[derive(Debug)]
pub enum Error<DataSourceError, CommitmentTreeError, SelectionError, FeeError> {
/// An error occurred retrieving data from the underlying data source
DataSource(DataSourceError),
/// An error in computations involving the note commitment trees.
CommitmentTree(ShardTreeError<CommitmentTreeError>),
/// An error in note selection
NoteSelection(SelectionError),
/// An error in transaction proposal construction
Proposal(ProposalError),
/// The proposal was structurally valid, but spending shielded outputs of prior multi-step
/// transaction steps is not yet supported.
ProposalNotSupported,
/// No account could be found corresponding to a provided spending key.
KeyNotRecognized,
/// Zcash amount computation encountered an overflow or underflow.
BalanceError(BalanceError),
/// Unable to create a new spend because the wallet balance is not sufficient.
InsufficientFunds {
available: NonNegativeAmount,
required: NonNegativeAmount,
},
/// The wallet must first perform a scan of the blockchain before other
/// operations can be performed.
ScanRequired,
/// An error occurred building a new transaction.
Builder(builder::Error<FeeError>),
/// It is forbidden to provide a memo when constructing a transparent output.
MemoForbidden,
/// Attempted to send change to an unsupported pool.
///
/// This is indicative of a programming error; execution of a transaction proposal that
/// presumes support for the specified pool was performed using an application that does not
/// provide such support.
UnsupportedChangeType(PoolType),
/// Attempted to create a spend to an unsupported Unified Address receiver
NoSupportedReceivers(Box<UnifiedAddress>),
/// A proposed transaction cannot be built because it requires spending an input
/// for which no spending key is available.
///
/// The argument is the address of the note or UTXO being spent.
NoSpendingKey(String),
/// A note being spent does not correspond to either the internal or external
/// full viewing key for an account.
NoteMismatch(NoteId),
/// An error occurred parsing the address from a payment request.
Address(ConversionError<&'static str>),
#[cfg(feature = "transparent-inputs")]
AddressNotRecognized(TransparentAddress),
}
impl<DE, CE, SE, FE> fmt::Display for Error<DE, CE, SE, FE>
where
DE: fmt::Display,
CE: fmt::Display,
SE: fmt::Display,
FE: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::DataSource(e) => {
write!(
f,
"The underlying datasource produced the following error: {}",
e
)
}
Error::CommitmentTree(e) => {
write!(f, "An error occurred in querying or updating a note commitment tree: {}", e)
}
Error::NoteSelection(e) => {
write!(f, "Note selection encountered the following error: {}", e)
}
Error::Proposal(e) => {
write!(f, "Input selection attempted to construct an invalid proposal: {}", e)
}
Error::ProposalNotSupported => {
write!(
f,
"The proposal was valid, but spending shielded outputs of prior transaction steps is not yet supported."
)
}
Error::KeyNotRecognized => {
write!(
f,
"Wallet does not contain an account corresponding to the provided spending key"
)
}
Error::BalanceError(e) => write!(
f,
"The value lies outside the valid range of Zcash amounts: {:?}.",
e
),
Error::InsufficientFunds { available, required } => write!(
f,
"Insufficient balance (have {}, need {} including fee)",
u64::from(*available),
u64::from(*required)
),
Error::ScanRequired => write!(f, "Must scan blocks first"),
Error::Builder(e) => write!(f, "An error occurred building the transaction: {}", e),
Error::MemoForbidden => write!(f, "It is not possible to send a memo to a transparent address."),
Error::UnsupportedChangeType(t) => write!(f, "Attempted to send change to an unsupported pool type: {}", t),
Error::NoSupportedReceivers(ua) => write!(
f,
"A recipient's unified address does not contain any receivers to which the wallet can send funds; required one of {}",
ua.receiver_types().iter().enumerate().map(|(i, tc)| format!("{}{:?}", if i > 0 { ", " } else { "" }, tc)).collect::<String>()
),
Error::NoSpendingKey(addr) => write!(f, "No spending key available for address: {}", addr),
Error::NoteMismatch(n) => write!(f, "A note being spent ({:?}) does not correspond to either the internal or external full viewing key for the provided spending key.", n),
Error::Address(e) => {
write!(f, "An error occurred decoding the address from a payment request: {}.", e)
}
#[cfg(feature = "transparent-inputs")]
Error::AddressNotRecognized(_) => {
write!(f, "The specified transparent address was not recognized as belonging to the wallet.")
}
}
}
}
impl<DE, CE, SE, FE> error::Error for Error<DE, CE, SE, FE>
where
DE: Debug + Display + error::Error + 'static,
CE: Debug + Display + error::Error + 'static,
SE: Debug + Display + error::Error + 'static,
FE: Debug + Display + 'static,
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self {
Error::DataSource(e) => Some(e),
Error::CommitmentTree(e) => Some(e),
Error::NoteSelection(e) => Some(e),
Error::Proposal(e) => Some(e),
Error::Builder(e) => Some(e),
_ => None,
}
}
}
impl<DE, CE, SE, FE> From<builder::Error<FE>> for Error<DE, CE, SE, FE> {
fn from(e: builder::Error<FE>) -> Self {
Error::Builder(e)
}
}
impl<DE, CE, SE, FE> From<BalanceError> for Error<DE, CE, SE, FE> {
fn from(e: BalanceError) -> Self {
Error::BalanceError(e)
}
}
impl<DE, CE, SE, FE> From<ConversionError<&'static str>> for Error<DE, CE, SE, FE> {
fn from(value: ConversionError<&'static str>) -> Self {
Error::Address(value)
}
}
impl<DE, CE, SE, FE> From<InputSelectorError<DE, SE>> for Error<DE, CE, SE, FE> {
fn from(e: InputSelectorError<DE, SE>) -> Self {
match e {
InputSelectorError::DataSource(e) => Error::DataSource(e),
InputSelectorError::Selection(e) => Error::NoteSelection(e),
InputSelectorError::Proposal(e) => Error::Proposal(e),
InputSelectorError::InsufficientFunds {
available,
required,
} => Error::InsufficientFunds {
available,
required,
},
InputSelectorError::SyncRequired => Error::ScanRequired,
InputSelectorError::Address(e) => Error::Address(e),
}
}
}
impl<DE, CE, SE, FE> From<sapling::builder::Error> for Error<DE, CE, SE, FE> {
fn from(e: sapling::builder::Error) -> Self {
Error::Builder(builder::Error::SaplingBuild(e))
}
}
impl<DE, CE, SE, FE> From<transparent::builder::Error> for Error<DE, CE, SE, FE> {
fn from(e: transparent::builder::Error) -> Self {
Error::Builder(builder::Error::TransparentBuild(e))
}
}
impl<DE, CE, SE, FE> From<ShardTreeError<CE>> for Error<DE, CE, SE, FE> {
fn from(e: ShardTreeError<CE>) -> Self {
Error::CommitmentTree(e)
}
}