1use std::error;
4use std::fmt;
5
6use nonempty::NonEmpty;
7use shardtree::error::ShardTreeError;
8
9use zcash_address::ParseError;
10use zcash_client_backend::data_api::NoteFilter;
11use zcash_keys::address::UnifiedAddress;
12use zcash_keys::keys::AddressGenerationError;
13use zcash_protocol::{consensus::BlockHeight, value::BalanceError, PoolType, TxId};
14use zip32::DiversifierIndex;
15
16use crate::{wallet::commitment_tree, AccountUuid};
17
18#[cfg(feature = "transparent-inputs")]
19use {
20 crate::wallet::transparent::SchedulingError,
21 ::transparent::{address::TransparentAddress, keys::TransparentKeyScope},
22 zcash_keys::encoding::TransparentCodecError,
23};
24
25#[derive(Debug)]
27pub enum SqliteClientError {
28 CorruptedData(String),
30
31 Protobuf(prost::DecodeError),
33
34 InvalidNote,
36
37 TableNotEmpty,
39
40 DecodingError(ParseError),
42
43 #[cfg(feature = "transparent-inputs")]
45 TransparentDerivation(bip32::Error),
46
47 #[cfg(feature = "transparent-inputs")]
50 TransparentAddress(TransparentCodecError),
51
52 DbError(rusqlite::Error),
54
55 Io(std::io::Error),
57
58 InvalidMemo(zcash_protocol::memo::Error),
60
61 BlockConflict(BlockHeight),
64
65 NonSequentialBlocks,
67
68 RequestedRewindInvalid {
72 safe_rewind_height: Option<BlockHeight>,
73 requested_height: BlockHeight,
74 },
75
76 AddressGeneration(AddressGenerationError),
78
79 AccountUnknown,
81
82 AccountCollision(AccountUuid),
85
86 UnknownZip32Derivation,
88
89 KeyDerivationError(zip32::AccountId),
91
92 BadAccountData(String),
94
95 Zip32AccountIndexOutOfRange,
97
98 #[cfg(feature = "transparent-inputs")]
101 AddressNotRecognized(TransparentAddress),
102
103 CommitmentTree(ShardTreeError<commitment_tree::Error>),
106
107 CacheMiss(BlockHeight),
109
110 ChainHeightUnknown,
116
117 UnsupportedPoolType(PoolType),
119
120 BalanceError(BalanceError),
122
123 NoteFilterInvalid(NoteFilter),
125
126 #[cfg(feature = "transparent-inputs")]
130 ReachedGapLimit(TransparentKeyScope, u32),
131
132 DiversifierIndexReuse(DiversifierIndex, Box<UnifiedAddress>),
136
137 AddressReuse(String, NonEmpty<TxId>),
142
143 #[cfg(feature = "transparent-inputs")]
145 Scheduling(SchedulingError),
146}
147
148impl error::Error for SqliteClientError {
149 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
150 match &self {
151 SqliteClientError::InvalidMemo(e) => Some(e),
152 SqliteClientError::DbError(e) => Some(e),
153 SqliteClientError::Io(e) => Some(e),
154 SqliteClientError::BalanceError(e) => Some(e),
155 SqliteClientError::AddressGeneration(e) => Some(e),
156 _ => None,
157 }
158 }
159}
160
161impl fmt::Display for SqliteClientError {
162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163 match &self {
164 SqliteClientError::CorruptedData(reason) => {
165 write!(f, "Data DB is corrupted: {}", reason)
166 }
167 SqliteClientError::Protobuf(e) => write!(f, "Failed to parse protobuf-encoded record: {}", e),
168 SqliteClientError::InvalidNote => write!(f, "Invalid note"),
169 SqliteClientError::RequestedRewindInvalid { safe_rewind_height, requested_height } => write!(
170 f,
171 "A rewind for your wallet may only target height {} or greater; the requested height was {}.",
172 safe_rewind_height.map_or("<unavailable>".to_owned(), |h0| format!("{}", h0)),
173 requested_height
174 ),
175 SqliteClientError::DecodingError(e) => write!(f, "{}", e),
176 #[cfg(feature = "transparent-inputs")]
177 SqliteClientError::TransparentDerivation(e) => write!(f, "{:?}", e),
178 #[cfg(feature = "transparent-inputs")]
179 SqliteClientError::TransparentAddress(e) => write!(f, "{}", e),
180 SqliteClientError::TableNotEmpty => write!(f, "Table is not empty"),
181 SqliteClientError::DbError(e) => write!(f, "{}", e),
182 SqliteClientError::Io(e) => write!(f, "{}", e),
183 SqliteClientError::InvalidMemo(e) => write!(f, "{}", e),
184 SqliteClientError::BlockConflict(h) => write!(f, "A block hash conflict occurred at height {}; rewind required.", u32::from(*h)),
185 SqliteClientError::NonSequentialBlocks => write!(f, "`put_blocks` requires that the provided block range be sequential"),
186 SqliteClientError::AddressGeneration(e) => write!(f, "{}", e),
187 SqliteClientError::AccountUnknown => write!(f, "The account with the given ID does not belong to this wallet."),
188 SqliteClientError::UnknownZip32Derivation => write!(f, "ZIP-32 derivation information is not known for this account."),
189 SqliteClientError::KeyDerivationError(zip32_index) => write!(f, "Key derivation failed for ZIP 32 account index {}", u32::from(*zip32_index)),
190 SqliteClientError::BadAccountData(e) => write!(f, "Failed to add account: {}", e),
191 SqliteClientError::Zip32AccountIndexOutOfRange => write!(f, "ZIP 32 account identifiers must be less than 0x7FFFFFFF."),
192 SqliteClientError::AccountCollision(account_uuid) => write!(f, "An account corresponding to the data provided already exists in the wallet with UUID {account_uuid:?}."),
193 #[cfg(feature = "transparent-inputs")]
194 SqliteClientError::AddressNotRecognized(_) => write!(f, "The address associated with a received txo is not identifiable as belonging to the wallet."),
195 SqliteClientError::CommitmentTree(err) => write!(f, "An error occurred accessing or updating note commitment tree data: {}.", err),
196 SqliteClientError::CacheMiss(height) => write!(f, "Requested height {} does not exist in the block cache.", height),
197 SqliteClientError::ChainHeightUnknown => write!(f, "Chain height unknown; please call `update_chain_tip`"),
198 SqliteClientError::UnsupportedPoolType(t) => write!(f, "Pool type is not currently supported: {}", t),
199 SqliteClientError::BalanceError(e) => write!(f, "Balance error: {}", e),
200 SqliteClientError::NoteFilterInvalid(s) => write!(f, "Could not evaluate filter query: {:?}", s),
201 #[cfg(feature = "transparent-inputs")]
202 SqliteClientError::ReachedGapLimit(key_scope, bad_index) => write!(f,
203 "The proposal cannot be constructed until a transaction with outputs to a previously reserved {} address has been mined. \
204 The address at index {bad_index} could not be safely reserved.",
205 match *key_scope {
206 TransparentKeyScope::EXTERNAL => "external transparent",
207 TransparentKeyScope::INTERNAL => "transparent change",
208 TransparentKeyScope::EPHEMERAL => "ephemeral transparent",
209 _ => panic!("Unsupported transparent key scope.")
210 }
211 ),
212 SqliteClientError::DiversifierIndexReuse(i, _) => {
213 write!(
214 f,
215 "An address has already been exposed for diversifier index {}",
216 u128::from(*i)
217 )
218 }
219 SqliteClientError::AddressReuse(address_str, txids) => {
220 write!(f, "The address {address_str} previously used in txid(s) {:?} would be reused.", txids)
221 }
222 #[cfg(feature = "transparent-inputs")]
223 SqliteClientError::Scheduling(err) => {
224 write!(f, "The wallet was unable to schedule an event: {}", err)
225 }
226 }
227 }
228}
229
230impl From<rusqlite::Error> for SqliteClientError {
231 fn from(e: rusqlite::Error) -> Self {
232 SqliteClientError::DbError(e)
233 }
234}
235
236impl From<std::io::Error> for SqliteClientError {
237 fn from(e: std::io::Error) -> Self {
238 SqliteClientError::Io(e)
239 }
240}
241impl From<ParseError> for SqliteClientError {
242 fn from(e: ParseError) -> Self {
243 SqliteClientError::DecodingError(e)
244 }
245}
246
247impl From<prost::DecodeError> for SqliteClientError {
248 fn from(e: prost::DecodeError) -> Self {
249 SqliteClientError::Protobuf(e)
250 }
251}
252
253#[cfg(feature = "transparent-inputs")]
254impl From<bip32::Error> for SqliteClientError {
255 fn from(e: bip32::Error) -> Self {
256 SqliteClientError::TransparentDerivation(e)
257 }
258}
259
260#[cfg(feature = "transparent-inputs")]
261impl From<TransparentCodecError> for SqliteClientError {
262 fn from(e: TransparentCodecError) -> Self {
263 SqliteClientError::TransparentAddress(e)
264 }
265}
266
267impl From<zcash_protocol::memo::Error> for SqliteClientError {
268 fn from(e: zcash_protocol::memo::Error) -> Self {
269 SqliteClientError::InvalidMemo(e)
270 }
271}
272
273impl From<ShardTreeError<commitment_tree::Error>> for SqliteClientError {
274 fn from(e: ShardTreeError<commitment_tree::Error>) -> Self {
275 SqliteClientError::CommitmentTree(e)
276 }
277}
278
279impl From<BalanceError> for SqliteClientError {
280 fn from(e: BalanceError) -> Self {
281 SqliteClientError::BalanceError(e)
282 }
283}
284
285impl From<AddressGenerationError> for SqliteClientError {
286 fn from(e: AddressGenerationError) -> Self {
287 SqliteClientError::AddressGeneration(e)
288 }
289}
290
291#[cfg(feature = "transparent-inputs")]
292impl From<SchedulingError> for SqliteClientError {
293 fn from(value: SchedulingError) -> Self {
294 SqliteClientError::Scheduling(value)
295 }
296}