1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
//! Error types for problems that may arise when reading or storing wallet data to SQLite.
use std::error;
use std::fmt;
use std::ops::Range;
#[cfg(feature = "orchard")]
use incrementalmerkletree::Position;
use nonempty::NonEmpty;
#[cfg(feature = "orchard")]
use shardtree::error::InsertionError;
use shardtree::error::ShardTreeError;
#[cfg(feature = "transparent-key-import")]
use uuid::Uuid;
use zcash_address::ParseError;
use zcash_client_backend::data_api::NoteFilter;
use zcash_client_backend::data_api::error::RewindError;
use zcash_client_backend::data_api::ll;
use zcash_client_backend::data_api::ll::wallet::PutBlocksError;
use zcash_client_backend::wallet::OutputRef;
use zcash_keys::address::UnifiedAddress;
use zcash_keys::keys::AddressGenerationError;
use zcash_protocol::{PoolType, ShieldedPool, TxId, consensus::BlockHeight, value::BalanceError};
use zip32::DiversifierIndex;
use crate::{
AccountUuid,
wallet::{commitment_tree, common::ErrUnsupportedPool},
};
#[cfg(feature = "transparent-inputs")]
use {
crate::wallet::transparent::SchedulingError,
::transparent::{address::TransparentAddress, keys::TransparentKeyScope},
zcash_keys::{
encoding::TransparentCodecError, keys::transparent::gap_limits::GapAddressesError,
},
};
/// The primary error type for the SQLite wallet backend.
#[derive(Debug)]
#[non_exhaustive]
pub enum SqliteClientError {
/// Decoding of a stored value from its serialized form has failed.
CorruptedData(String),
/// An error occurred decoding a protobuf message.
Protobuf(prost::DecodeError),
/// The rcm value for a note cannot be decoded to a valid JubJub point.
InvalidNote,
/// Illegal attempt to reinitialize an already-initialized wallet database.
TableNotEmpty,
/// A Zcash key or address decoding error
DecodingError(ParseError),
/// An error produced in legacy transparent address derivation
#[cfg(feature = "transparent-inputs")]
TransparentDerivation(bip32::Error),
/// An error encountered in decoding a transparent address from its
/// serialized form.
#[cfg(feature = "transparent-inputs")]
TransparentAddress(TransparentCodecError),
/// Wrapper for rusqlite errors.
DbError(rusqlite::Error),
/// Wrapper for errors from the IO subsystem
Io(std::io::Error),
/// A received memo cannot be interpreted as a UTF-8 string.
InvalidMemo(zcash_protocol::memo::Error),
/// An attempt to update block data would overwrite the current hash for a block with a
/// different hash. This indicates that a required rewind was not performed.
BlockConflict(BlockHeight),
/// A range of blocks provided to the database as a unit was non-sequential
NonSequentialBlocks,
/// A requested rewind would violate invariants of the storage layer. The payload returned with
/// this error is (safe rewind height, requested height). If no safe rewind height can be
/// determined, the safe rewind height member will be `None`.
RequestedRewindInvalid {
/// The height to which it is possible to safely rewind, or `None` if no safe
/// rewind height could be determined.
safe_rewind_height: Option<BlockHeight>,
/// The block height that was requested for the rewind.
requested_height: BlockHeight,
},
/// An error occurred in generating a Zcash address.
AddressGeneration(AddressGenerationError),
/// The account for which information was requested does not belong to the wallet.
AccountUnknown,
/// The account being added collides with an existing account in the wallet with the given ID.
/// The collision can be on the seed and ZIP-32 account index, or a shared IVK component.
AccountCollision(AccountUuid),
/// The account was imported, and ZIP-32 derivation information is not known for it.
UnknownZip32Derivation,
/// An error occurred deriving a spending key from a seed and a ZIP-32 account index.
KeyDerivationError(zip32::AccountId),
/// An error occurred while processing an account due to a failure in deriving the account's keys.
BadAccountData(String),
/// A caller attempted to construct a new account with an invalid ZIP 32 account identifier.
Zip32AccountIndexOutOfRange,
/// The address associated with a record being inserted was not recognized as
/// belonging to the wallet.
#[cfg(feature = "transparent-inputs")]
AddressNotRecognized(TransparentAddress),
/// An error occurred in inserting data into or accessing data from one of the wallet's note
/// commitment trees.
CommitmentTree(ShardTreeError<commitment_tree::Error>),
/// An error occurred while inserting the note commitment data for a range of scanned blocks
/// into one of the wallet's note commitment trees during a `put_blocks` operation. The `pool`
/// and `block_range` fields record the shielded pool whose note commitment tree was being
/// updated and the range of block heights (start-inclusive, end-exclusive) that were being
/// added to the wallet when the error occurred.
PutBlocksCommitmentTree {
/// The shielded pool whose note commitment tree was being updated when the error occurred.
pool: ShieldedPool,
/// The range of block heights that were being added to the wallet when the error
/// occurred.
block_range: Range<BlockHeight>,
/// The underlying note commitment tree error.
error: ShardTreeError<commitment_tree::Error>,
},
/// An error occurred in one of the wallet's note commitment trees while truncating the wallet
/// to the specified block height. The `pool` and `height` fields record the shielded pool
/// whose note commitment tree was being updated and the block height that the wallet was being
/// truncated to when the error occurred.
TruncateCommitmentTree {
/// The shielded pool whose note commitment tree was being updated when the error occurred.
pool: ShieldedPool,
/// The block height that the wallet was being truncated to when the error occurred.
height: BlockHeight,
/// The underlying note commitment tree error.
error: ShardTreeError<commitment_tree::Error>,
},
/// The caller-supplied frontier passed to an Orchard or Ironwood
/// historical witness generation helper is inconsistent with the shard
/// data reconstructed from the wallet at the requested height.
///
/// [`WalletDb::generate_orchard_witnesses_at_historical_height`]:
/// crate::WalletDb::generate_orchard_witnesses_at_historical_height
/// [`WalletDb::generate_ironwood_witnesses_at_historical_height`]:
/// crate::WalletDb::generate_ironwood_witnesses_at_historical_height
#[cfg(feature = "orchard")]
HistoricalFrontierInvalid(InsertionError),
/// A witness could not be generated for the specified position at the
/// specified historical height in a call to an Orchard or Ironwood
/// historical witness generation helper.
///
/// The wallet most likely has not synced through `height`, the checkpoint
/// at `height` has been pruned, or `position` does not belong to the
/// wallet.
///
/// [`WalletDb::generate_orchard_witnesses_at_historical_height`]:
/// crate::WalletDb::generate_orchard_witnesses_at_historical_height
/// [`WalletDb::generate_ironwood_witnesses_at_historical_height`]:
/// crate::WalletDb::generate_ironwood_witnesses_at_historical_height
#[cfg(feature = "orchard")]
HistoricalWitnessUnavailable {
/// The note commitment tree position for which a witness was
/// requested.
position: Position,
/// The historical height at which the witness was requested.
height: BlockHeight,
},
/// The block at the specified height was not available from the block cache.
CacheMiss(BlockHeight),
/// The height of the chain was not available; a call to [`WalletWrite::update_chain_tip`] is
/// required before the requested operation can succeed.
///
/// [`WalletWrite::update_chain_tip`]:
/// zcash_client_backend::data_api::WalletWrite::update_chain_tip
ChainHeightUnknown,
/// Unsupported pool type
UnsupportedPoolType(PoolType),
/// An error occurred in computing wallet balance
BalanceError(BalanceError),
/// A note selection query contained an invalid constant or was otherwise not supported.
NoteFilterInvalid(NoteFilter),
/// An address cannot be reserved, or a proposal cannot be constructed until a transaction
/// containing outputs belonging to a previously reserved address has been mined. The error
/// contains the index that could not safely be reserved.
#[cfg(feature = "transparent-inputs")]
ReachedGapLimit(TransparentKeyScope, u32),
/// The backend encountered an attempt to reuse a diversifier index to generate an address
/// having different receivers from an address that had previously been exposed for that
/// diversifier index. Returns the previously exposed address.
DiversifierIndexReuse(DiversifierIndex, Box<UnifiedAddress>),
/// The wallet attempted to create a transaction that would use of one of the wallet's
/// previously-used addresses, potentially creating a problem with on-chain transaction
/// linkability. The returned value contains the string encoding of the address and the txid(s)
/// of the transactions in which it is known to have been used.
AddressReuse(String, NonEmpty<TxId>),
/// The wallet found one or more notes that given a certain context would be
/// ineligible and shouldn't be considered in the involved db operation.
IneligibleNotes,
/// The wallet encountered an error when attempting to schedule wallet operations.
#[cfg(feature = "transparent-inputs")]
Scheduling(SchedulingError),
/// The caller responded to a [`TransactionsInvolvingAddress`] request by querying a range of
/// block heights ending at a height that did not match the (exclusive) end of the requested
/// range.
///
/// [`TransactionsInvolvingAddress`]: zcash_client_backend::data_api::TransactionsInvolvingAddress
#[cfg(feature = "transparent-inputs")]
NotificationMismatch {
/// The expected ending block height.
expected: BlockHeight,
/// The actual ending block height returned.
actual: BlockHeight,
},
/// An attempt to import a standalone transparent address failed because it had already been
/// imported to a different account.
#[cfg(feature = "transparent-key-import")]
StandaloneImportConflict(Uuid),
/// An error returned by a [`FeeRule`] during transparent input selection. The underlying
/// error is boxed so the storage layer does not need to know every fee rule's error type.
///
/// [`FeeRule`]: zcash_primitives::transaction::fees::FeeRule
#[cfg(feature = "transparent-inputs")]
FeeRuleError(Box<dyn error::Error + Send + Sync>),
/// A `zcash_client_backend` error value carried a variant that this crate has no
/// translation for; the wrapped value carries the error itself.
///
/// Those error types are `#[non_exhaustive]`, so a variant introduced by a future
/// `zcash_client_backend` release can reach this crate before there is a specific
/// [`SqliteClientError`] counterpart for it. This error is therefore unreachable with
/// the `zcash_client_backend` release this crate is built against; encountering it
/// means this crate needs updating to translate the new variant.
BackendError(BackendError),
}
/// A `zcash_client_backend` error carrying a variant that this crate has no translation for,
/// as reported by [`SqliteClientError::BackendError`].
///
/// Each variant names the operation whose error type is wrapped. The errors are boxed because
/// both are generic over [`SqliteClientError`] itself.
#[derive(Debug)]
#[non_exhaustive]
pub enum BackendError {
/// An error reported while inserting scanned blocks into the wallet.
PutBlocks(Box<PutBlocksError<SqliteClientError, commitment_tree::Error>>),
/// An error reported while rewinding the wallet to a previous chain state.
Rewind(Box<RewindError<AccountUuid, SqliteClientError>>),
}
impl fmt::Display for BackendError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BackendError::PutBlocks(_) => write!(f, "block insertion"),
BackendError::Rewind(_) => write!(f, "rewind to a previous chain state"),
}
}
}
impl error::Error for SqliteClientError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self {
SqliteClientError::InvalidMemo(e) => Some(e),
SqliteClientError::DbError(e) => Some(e),
SqliteClientError::Io(e) => Some(e),
SqliteClientError::BalanceError(e) => Some(e),
SqliteClientError::AddressGeneration(e) => Some(e),
#[cfg(feature = "orchard")]
SqliteClientError::HistoricalFrontierInvalid(e) => Some(e),
#[cfg(feature = "transparent-inputs")]
SqliteClientError::FeeRuleError(e) => Some(&**e),
_ => None,
}
}
}
#[cfg(feature = "transparent-inputs")]
impl From<GapAddressesError<SqliteClientError>> for SqliteClientError {
fn from(err: GapAddressesError<SqliteClientError>) -> Self {
match err {
GapAddressesError::Storage(e) => e,
GapAddressesError::AddressGeneration(e) => SqliteClientError::AddressGeneration(e),
GapAddressesError::AccountUnknown => SqliteClientError::AccountUnknown,
}
}
}
impl fmt::Display for SqliteClientError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
SqliteClientError::CorruptedData(reason) => {
write!(f, "Data DB is corrupted: {reason}")
}
SqliteClientError::Protobuf(e) => {
write!(f, "Failed to parse protobuf-encoded record: {e}")
}
SqliteClientError::InvalidNote => write!(f, "Invalid note"),
SqliteClientError::RequestedRewindInvalid {
safe_rewind_height,
requested_height,
} => write!(
f,
"A rewind for your wallet may only target height {} or greater; the requested height was {}.",
safe_rewind_height.map_or("<unavailable>".to_owned(), |h0| format!("{h0}")),
requested_height
),
SqliteClientError::DecodingError(e) => write!(f, "{e}"),
#[cfg(feature = "transparent-inputs")]
SqliteClientError::TransparentDerivation(e) => write!(f, "{e:?}"),
#[cfg(feature = "transparent-inputs")]
SqliteClientError::TransparentAddress(e) => write!(f, "{e}"),
SqliteClientError::TableNotEmpty => write!(f, "Table is not empty"),
SqliteClientError::DbError(e) => write!(f, "{e}"),
SqliteClientError::Io(e) => write!(f, "{e}"),
SqliteClientError::InvalidMemo(e) => write!(f, "{e}"),
SqliteClientError::BlockConflict(h) => write!(
f,
"A block hash conflict occurred at height {}; rewind required.",
u32::from(*h)
),
SqliteClientError::NonSequentialBlocks => write!(
f,
"`put_blocks` requires that the provided block range be sequential"
),
SqliteClientError::AddressGeneration(e) => write!(f, "{e}"),
SqliteClientError::AccountUnknown => write!(
f,
"The account with the given ID does not belong to this wallet."
),
SqliteClientError::UnknownZip32Derivation => write!(
f,
"ZIP-32 derivation information is not known for this account."
),
SqliteClientError::KeyDerivationError(zip32_index) => write!(
f,
"Key derivation failed for ZIP 32 account index {}",
u32::from(*zip32_index)
),
SqliteClientError::BadAccountData(e) => write!(f, "Failed to add account: {e}"),
SqliteClientError::Zip32AccountIndexOutOfRange => write!(
f,
"ZIP 32 account identifiers must be less than 0x7FFFFFFF."
),
SqliteClientError::AccountCollision(account_uuid) => write!(
f,
"An account corresponding to the data provided already exists in the wallet with UUID {account_uuid:?}."
),
#[cfg(feature = "transparent-inputs")]
SqliteClientError::AddressNotRecognized(_) => write!(
f,
"The address associated with a received txo is not identifiable as belonging to the wallet."
),
SqliteClientError::CommitmentTree(err) => write!(
f,
"An error occurred accessing or updating note commitment tree data: {err}."
),
SqliteClientError::PutBlocksCommitmentTree {
pool,
block_range,
error,
} => write!(
f,
"An error occurred updating the {pool:?} note commitment tree while adding blocks in the range {}..{}: {error}.",
u32::from(block_range.start),
u32::from(block_range.end),
),
SqliteClientError::TruncateCommitmentTree {
pool,
height,
error,
} => write!(
f,
"An error occurred updating the {pool:?} note commitment tree while truncating the wallet to height {}: {error}.",
u32::from(*height),
),
#[cfg(feature = "orchard")]
SqliteClientError::HistoricalFrontierInvalid(err) => write!(
f,
"The frontier supplied to historical witness generation is inconsistent with the wallet's shard data: {err}"
),
#[cfg(feature = "orchard")]
SqliteClientError::HistoricalWitnessUnavailable { position, height } => write!(
f,
"No witness is available for position {} at height {height} (the wallet may need to sync through this height).",
u64::from(*position),
),
SqliteClientError::CacheMiss(height) => write!(
f,
"Requested height {height} does not exist in the block cache."
),
SqliteClientError::ChainHeightUnknown => {
write!(f, "Chain height unknown; please call `update_chain_tip`")
}
SqliteClientError::UnsupportedPoolType(t) => {
write!(f, "Pool type is not currently supported: {t}")
}
SqliteClientError::BalanceError(e) => write!(f, "Balance error: {e}"),
SqliteClientError::NoteFilterInvalid(s) => {
write!(f, "Could not evaluate filter query: {s:?}")
}
#[cfg(feature = "transparent-inputs")]
SqliteClientError::ReachedGapLimit(key_scope, bad_index) => write!(
f,
"The proposal cannot be constructed until a transaction with outputs to a previously reserved {} address has been mined. \
The address at index {bad_index} could not be safely reserved.",
match *key_scope {
TransparentKeyScope::EXTERNAL => "external transparent",
TransparentKeyScope::INTERNAL => "transparent change",
TransparentKeyScope::EPHEMERAL => "ephemeral transparent",
_ => panic!("Unsupported transparent key scope."),
}
),
SqliteClientError::DiversifierIndexReuse(i, _) => {
write!(
f,
"An address has already been exposed for diversifier index {}",
u128::from(*i)
)
}
SqliteClientError::AddressReuse(address_str, txids) => {
write!(
f,
"The address {address_str} previously used in txid(s) {txids:?} would be reused."
)
}
#[cfg(feature = "transparent-inputs")]
SqliteClientError::Scheduling(err) => {
write!(f, "The wallet was unable to schedule an event: {err}")
}
#[cfg(feature = "transparent-inputs")]
SqliteClientError::NotificationMismatch { expected, actual } => {
write!(
f,
"The client performed an address check over a block range that did not match the requested range; expected as_of_height: {expected}, actual as_of_height: {actual}"
)
}
SqliteClientError::IneligibleNotes => {
write!(
f,
"Query found notes that are considered ineligible in its context"
)
}
#[cfg(feature = "transparent-key-import")]
SqliteClientError::StandaloneImportConflict(uuid) => {
write!(
f,
"The given standalone transparent address is already managed by account {uuid}"
)
}
#[cfg(feature = "transparent-inputs")]
SqliteClientError::FeeRuleError(e) => write!(f, "Fee rule error: {e}"),
SqliteClientError::BackendError(e) => write!(
f,
"The zcash_client_backend error reported for {e} is not one this version of \
zcash_client_sqlite recognizes; this crate must be updated to handle it."
),
}
}
}
impl From<rusqlite::Error> for SqliteClientError {
fn from(e: rusqlite::Error) -> Self {
SqliteClientError::DbError(e)
}
}
impl From<std::io::Error> for SqliteClientError {
fn from(e: std::io::Error) -> Self {
SqliteClientError::Io(e)
}
}
impl From<ParseError> for SqliteClientError {
fn from(e: ParseError) -> Self {
SqliteClientError::DecodingError(e)
}
}
impl From<prost::DecodeError> for SqliteClientError {
fn from(e: prost::DecodeError) -> Self {
SqliteClientError::Protobuf(e)
}
}
#[cfg(feature = "transparent-inputs")]
impl From<bip32::Error> for SqliteClientError {
fn from(e: bip32::Error) -> Self {
SqliteClientError::TransparentDerivation(e)
}
}
#[cfg(feature = "transparent-inputs")]
impl From<TransparentCodecError> for SqliteClientError {
fn from(e: TransparentCodecError) -> Self {
SqliteClientError::TransparentAddress(e)
}
}
impl From<zcash_protocol::memo::Error> for SqliteClientError {
fn from(e: zcash_protocol::memo::Error) -> Self {
SqliteClientError::InvalidMemo(e)
}
}
impl From<ShardTreeError<commitment_tree::Error>> for SqliteClientError {
fn from(e: ShardTreeError<commitment_tree::Error>) -> Self {
SqliteClientError::CommitmentTree(e)
}
}
impl From<BalanceError> for SqliteClientError {
fn from(e: BalanceError) -> Self {
SqliteClientError::BalanceError(e)
}
}
impl From<AddressGenerationError> for SqliteClientError {
fn from(e: AddressGenerationError) -> Self {
SqliteClientError::AddressGeneration(e)
}
}
/// `zip317::FeeError` does not implement `std::error::Error`, so we wrap it in order to box
/// it as the payload of [`SqliteClientError::FeeRuleError`].
#[cfg(feature = "transparent-inputs")]
#[derive(Debug)]
struct FeeErrorWrapper(zcash_primitives::transaction::fees::zip317::FeeError);
#[cfg(feature = "transparent-inputs")]
impl fmt::Display for FeeErrorWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[cfg(feature = "transparent-inputs")]
impl error::Error for FeeErrorWrapper {}
#[cfg(feature = "transparent-inputs")]
impl From<zcash_primitives::transaction::fees::zip317::FeeError> for SqliteClientError {
fn from(e: zcash_primitives::transaction::fees::zip317::FeeError) -> Self {
SqliteClientError::FeeRuleError(Box::new(FeeErrorWrapper(e)))
}
}
#[cfg(feature = "transparent-inputs")]
impl From<SchedulingError> for SqliteClientError {
fn from(value: SchedulingError) -> Self {
SqliteClientError::Scheduling(value)
}
}
impl From<PutBlocksError<SqliteClientError, commitment_tree::Error>> for SqliteClientError {
fn from(value: PutBlocksError<SqliteClientError, commitment_tree::Error>) -> Self {
match value {
ll::wallet::PutBlocksError::NonSequentialBlocks { .. } => {
SqliteClientError::NonSequentialBlocks
}
ll::wallet::PutBlocksError::Storage(e) => e,
ll::wallet::PutBlocksError::ShardTree(e) => SqliteClientError::from(e),
ll::wallet::PutBlocksError::ShardTreeForBlockRange {
pool,
block_range,
error,
} => SqliteClientError::PutBlocksCommitmentTree {
pool,
block_range,
error,
},
#[cfg(feature = "transparent-inputs")]
ll::wallet::PutBlocksError::GapAddresses(e) => SqliteClientError::from(e),
// `PutBlocksError` is `#[non_exhaustive]`, so a variant introduced by a future
// `zcash_client_backend` release reaches this conversion with no counterpart
// here until this crate is updated to map it. Report it rather than panicking.
other => SqliteClientError::BackendError(BackendError::PutBlocks(Box::new(other))),
}
}
}
impl ErrUnsupportedPool for SqliteClientError {
fn unsupported_pool_type(pool_type: PoolType) -> Self {
SqliteClientError::UnsupportedPoolType(pool_type)
}
}
/// A local LockError type for which we can write a From<rusqlite::Error> impl.
pub(crate) enum LockError {
/// Wrapper for storage errors.
Storage(rusqlite::Error),
/// The wrapped output reference was not found, or the output it refers to was already locked.
LockFailure(OutputRef),
}
impl From<rusqlite::Error> for LockError {
fn from(value: rusqlite::Error) -> Self {
LockError::Storage(value)
}
}
impl From<LockError> for zcash_client_backend::data_api::error::LockError<SqliteClientError> {
fn from(value: LockError) -> Self {
match value {
LockError::Storage(error) => zcash_client_backend::data_api::error::LockError::Storage(
SqliteClientError::from(error),
),
LockError::LockFailure(output) => {
zcash_client_backend::data_api::error::LockError::LockFailure(output)
}
}
}
}