Correct the hierarchy of errors.

This commit is contained in:
Kris Nuttycombe 2021-01-13 15:20:11 -07:00
parent aa72e070ce
commit 5a21580f13
8 changed files with 133 additions and 142 deletions

View File

@ -227,6 +227,10 @@ pub trait WalletWrite: WalletRead {
/// Record a note as having been received, along with its nullifier and the transaction
/// within which the note was created.
///
/// Implementations of this method should be exclusively additive with respect to stored
/// data; passing `None` for the nullifier should not be interpreted as deleting nullifier
/// information from the underlying store.
fn put_received_note<T: ShieldedOutput>(
&mut self,
output: &T,
@ -375,7 +379,7 @@ pub mod testing {
pub struct MockBlockSource {}
impl BlockSource for MockBlockSource {
type Error = Error<(), u32>;
type Error = Error<u32>;
fn with_blocks<F>(
&self,
@ -393,7 +397,7 @@ pub mod testing {
pub struct MockWalletDB {}
impl WalletRead for MockWalletDB {
type Error = Error<(), u32>;
type Error = Error<u32>;
type NoteRef = u32;
type TxRef = TxId;

View File

@ -40,13 +40,13 @@ use crate::{
/// - `Err(e)` if there was an error during validation unrelated to chain validity.
///
/// This function does not mutate either of the databases.
pub fn validate_chain<'db, E0, N, E, P, C>(
pub fn validate_chain<N, E, P, C>(
parameters: &P,
cache: &C,
validate_from: Option<(BlockHeight, BlockHash)>,
) -> Result<(), E>
where
E: From<Error<E0, N>>,
E: From<Error<N>>,
P: consensus::Parameters,
C: BlockSource<Error = E>,
{
@ -68,18 +68,18 @@ where
cache.with_blocks(from_height, None, move |block| {
let current_height = block.height();
let result = if current_height != prev_height + 1 {
Err(ChainInvalid::block_height_discontinuity(prev_height + 1, current_height).into())
Err(ChainInvalid::block_height_discontinuity(prev_height + 1, current_height))
} else {
match prev_hash {
None => Ok(()),
Some(h) if h == block.prev_hash() => Ok(()),
Some(_) => Err(ChainInvalid::prev_hash_mismatch(current_height).into()),
Some(_) => Err(ChainInvalid::prev_hash_mismatch(current_height)),
}
};
prev_height = current_height;
prev_hash = Some(block.hash());
result
result.map_err(E::from)
})
}
@ -130,7 +130,7 @@ where
/// ```
///
/// [`init_blocks_table`]: crate::init::init_blocks_table
pub fn scan_cached_blocks<E, E0, N, P, C, D>(
pub fn scan_cached_blocks<E, N, P, C, D>(
params: &P,
cache: &C,
data: &mut D,
@ -141,7 +141,7 @@ where
C: BlockSource<Error = E>,
D: WalletWrite<Error = E, NoteRef = N>,
N: Copy + Debug,
E: From<Error<E0, N>>,
E: From<Error<N>>,
{
let sapling_activation_height = params
.activation_height(NetworkUpgrade::Sapling)

View File

@ -22,26 +22,27 @@ pub enum ChainInvalid {
}
#[derive(Debug)]
pub enum Error<DbError, NoteId> {
pub enum Error<NoteId> {
/// Unable to create a new spend because the wallet balance is not sufficient.
InsufficientBalance(Amount, Amount),
/// Chain validation detected an error in the block at the specified block height.
InvalidChain(BlockHeight, ChainInvalid),
/// A provided extfvk is not associated with the specified account.
InvalidExtSK(AccountId),
/// The root of an output's witness tree in a newly arrived transaction does not correspond to
/// root of the stored commitment tree at the recorded height.
/// The root of an output's witness tree in a newly arrived transaction does
/// not correspond to root of the stored commitment tree at the recorded height.
///
/// The `usize` member of this struct is the index of the shielded output within
/// the transaction where the witness root does not match.
InvalidNewWitnessAnchor(usize, TxId, BlockHeight, Node),
/// The root of an output's witness tree in a previously stored transaction does not correspond to
/// root of the current commitment tree.
/// The root of an output's witness tree in a previously stored transaction
/// does not correspond to root of the current commitment tree.
InvalidWitnessAnchor(NoteId, BlockHeight),
/// 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),
/// Wrapper for errors from the underlying data store.
Database(DbError),
/// An error occurred decoding a protobuf message.
Protobuf(protobuf::ProtobufError),
/// The wallet attempted a sapling-only operation at a block
@ -50,19 +51,19 @@ pub enum Error<DbError, NoteId> {
}
impl ChainInvalid {
pub fn prev_hash_mismatch<E, N>(at_height: BlockHeight) -> Error<E, N> {
pub fn prev_hash_mismatch<N>(at_height: BlockHeight) -> Error<N> {
Error::InvalidChain(at_height, ChainInvalid::PrevHashMismatch)
}
pub fn block_height_discontinuity<E, N>(
pub fn block_height_discontinuity<N>(
at_height: BlockHeight,
found: BlockHeight,
) -> Error<E, N> {
) -> Error<N> {
Error::InvalidChain(at_height, ChainInvalid::BlockHeightDiscontinuity(found))
}
}
impl<E: fmt::Display, N: fmt::Display> fmt::Display for Error<E, N> {
impl<N: fmt::Display> fmt::Display for Error<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
Error::InsufficientBalance(have, need) => write!(
@ -88,37 +89,29 @@ impl<E: fmt::Display, N: fmt::Display> fmt::Display for Error<E, N> {
),
Error::ScanRequired => write!(f, "Must scan blocks first"),
Error::Builder(e) => write!(f, "{:?}", e),
Error::Database(e) => write!(f, "{}", e),
Error::Protobuf(e) => write!(f, "{}", e),
Error::SaplingNotActive => write!(f, "Could not determine Sapling upgrade activation height."),
}
}
}
impl<E: error::Error + 'static, N: error::Error + 'static> error::Error for Error<E, N> {
impl<N: error::Error + 'static> error::Error for Error<N> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self {
Error::Builder(e) => Some(e),
Error::Database(e) => Some(e),
Error::Protobuf(e) => Some(e),
_ => None,
}
}
}
impl<E, N> From<builder::Error> for Error<E, N> {
impl<N> From<builder::Error> for Error<N> {
fn from(e: builder::Error) -> Self {
Error::Builder(e)
}
}
//impl<E, N> From<std::io::Error> for Error<E, N> {
// fn from(e: std::io::Error) -> Self {
// Error::Io(e)
// }
//}
impl<E, N> From<protobuf::ProtobufError> for Error<E, N> {
impl<N> From<protobuf::ProtobufError> for Error<N> {
fn from(e: protobuf::ProtobufError) -> Self {
Error::Protobuf(e)
}

View File

@ -24,13 +24,13 @@ pub const ANCHOR_OFFSET: u32 = 10;
/// Scans a [`Transaction`] for any information that can be decrypted by the accounts in
/// the wallet, and saves it to the wallet.
pub fn decrypt_and_store_transaction<'db, E0, N, E, P, D>(
pub fn decrypt_and_store_transaction<'db, N, E, P, D>(
params: &P,
mut data: &'db D,
tx: &Transaction,
) -> Result<(), E>
where
E: From<Error<E0, N>>,
E: From<Error<N>>,
P: consensus::Parameters,
&'db D: WalletWrite<Error = E>,
{
@ -144,8 +144,8 @@ where
/// Err(e) => (),
/// }
/// ```
pub fn create_spend_to_address<E0, N, E, P, D, R>(
data: &mut D,
pub fn create_spend_to_address<E, N, P, D, R>(
wallet_db: &mut D,
params: &P,
prover: impl TxProver,
account: AccountId,
@ -154,21 +154,18 @@ pub fn create_spend_to_address<E0, N, E, P, D, R>(
value: Amount,
memo: Option<Memo>,
ovk_policy: OvkPolicy,
) -> Result<R, Error<E, N>>
) -> Result<R, E>
where
E0: Into<Error<E, N>>,
E: From<Error<N>>,
P: consensus::Parameters + Clone,
R: Copy + Debug,
D: WalletWrite<Error = E0, TxRef = R>,
D: WalletWrite<Error = E, TxRef = R>,
{
// Check that the ExtendedSpendingKey we have been given corresponds to the
// ExtendedFullViewingKey for the account we are spending from.
let extfvk = ExtendedFullViewingKey::from(extsk);
if !data
.is_valid_account_extfvk(account, &extfvk)
.map_err(|e| e.into())?
{
return Err(Error::InvalidExtSK(account));
if !wallet_db.is_valid_account_extfvk(account, &extfvk)? {
return Err(E::from(Error::InvalidExtSK(account)));
}
// Apply the outgoing viewing key policy.
@ -179,20 +176,18 @@ where
};
// Target the next block, assuming we are up-to-date.
let (height, anchor_height) = data
let (height, anchor_height) = wallet_db
.get_target_and_anchor_heights()
.map_err(|e| e.into())
.and_then(|x| x.ok_or(Error::ScanRequired))?;
.and_then(|x| x.ok_or(Error::ScanRequired.into()))?;
let target_value = value + DEFAULT_FEE;
let spendable_notes = data
.select_spendable_notes(account, target_value, anchor_height)
.map_err(|e| e.into())?;
let spendable_notes = wallet_db
.select_spendable_notes(account, target_value, anchor_height)?;
// Confirm we were able to select sufficient value
let selected_value = spendable_notes.iter().map(|n| n.note_value).sum();
if selected_value < target_value {
return Err(Error::InsufficientBalance(selected_value, target_value));
return Err(E::from(Error::InsufficientBalance(selected_value, target_value)));
}
// Create the transaction
@ -210,19 +205,20 @@ where
let merkle_path = selected.witness.path().expect("the tree is not empty");
builder.add_sapling_spend(extsk.clone(), selected.diversifier, note, merkle_path)?
builder.add_sapling_spend(extsk.clone(), selected.diversifier, note, merkle_path)
.map_err(Error::Builder)?
}
match to {
RecipientAddress::Shielded(to) => {
builder.add_sapling_output(ovk, to.clone(), value, memo.clone())
}
RecipientAddress::Shielded(to) =>
builder.add_sapling_output(ovk, to.clone(), value, memo.clone()),
RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value),
}?;
RecipientAddress::Transparent(to) =>
builder.add_transparent_output(&to, value),
}.map_err(Error::Builder)?;
let consensus_branch_id = BranchId::for_height(params, height);
let (tx, tx_metadata) = builder.build(consensus_branch_id, &prover)?;
let (tx, tx_metadata) = builder.build(consensus_branch_id, &prover).map_err(Error::Builder)?;
// We only called add_sapling_output() once.
let output_index = match tx_metadata.output_index(0) {
@ -231,7 +227,7 @@ where
};
// Update the database atomically, to ensure the result is internally consistent.
data.transactionally(|up| {
wallet_db.transactionally(|up| {
let created = time::OffsetDateTime::now_utc();
let tx_ref = up.put_tx_data(&tx, Some(created))?;

View File

@ -26,14 +26,18 @@
//! wallet::init::{init_wallet_db},
//! };
//!
//! let network = Network::TestNetwork;
//! let cache_file = NamedTempFile::new().unwrap();
//! let db_cache = BlockDB::for_path(cache_file).unwrap();
//! let db_file = NamedTempFile::new().unwrap();
//! let db_read = WalletDB::for_path(db_file, network).unwrap();
//! init_wallet_db(&db_read).unwrap();
//! # use zcash_client_sqlite::NoteId;
//! # use zcash_client_sqlite::error::SqliteClientError;
//!
//! let mut db_data = db_read.get_update_ops().unwrap();
//! # fn main() -> Result<(), SqliteClientError> {
//! let network = Network::TestNetwork;
//! let cache_file = NamedTempFile::new()?;
//! let db_cache = BlockDB::for_path(cache_file)?;
//! let db_file = NamedTempFile::new()?;
//! let db_read = WalletDB::for_path(db_file, network)?;
//! init_wallet_db(&db_read)?;
//!
//! let mut db_data = db_read.get_update_ops()?;
//!
//! // 1) Download new CompactBlocks into db_cache.
//!
@ -41,15 +45,15 @@
//! //
//! // Given that we assume the server always gives us correct-at-the-time blocks, any
//! // errors are in the blocks we have previously cached or scanned.
//! if let Err(e) = validate_chain(&network, &db_cache, db_data.get_max_height_hash().unwrap()) {
//! if let Err(e) = validate_chain(&network, &db_cache, db_data.get_max_height_hash()?) {
//! match e {
//! Error::InvalidChain(upper_bound, _) => {
//! BackendError(Error::InvalidChain(lower_bound, _)) => {
//! // a) Pick a height to rewind to.
//! //
//! // This might be informed by some external chain reorg information, or
//! // heuristics such as the platform, available bandwidth, size of recent
//! // CompactBlocks, etc.
//! let rewind_height = upper_bound - 10;
//! let rewind_height = lower_bound - 10;
//!
//! // b) Rewind scanned block information.
//! db_data.rewind_to_height(rewind_height);
@ -64,8 +68,9 @@
//! // CompactBlocks, tell it to go back and download from rewind_height
//! // onwards.
//! }
//! _ => {
//! // Handle other errors.
//! e => {
//! // Handle or return other errors.
//! return Err(e);
//! }
//! }
//! }
@ -75,7 +80,8 @@
//! // At this point, the cache and scanned data are locally consistent (though not
//! // necessarily consistent with the latest chain tip - this would be discovered the
//! // next time this codepath is executed after new blocks are received).
//! scan_cached_blocks(&network, &db_cache, &mut db_data, None).unwrap();
//! scan_cached_blocks(&network, &db_cache, &mut db_data, None)
//! # }
//! ```
use protobuf::parse_from_bytes;
@ -86,8 +92,8 @@ use zcash_primitives::consensus::BlockHeight;
use zcash_client_backend::{data_api::error::Error, proto::compact_formats::CompactBlock};
use crate::{
error::{db_error, SqliteClientError},
BlockDB, NoteId,
error::{SqliteClientError},
BlockDB,
};
pub mod init;
@ -102,17 +108,16 @@ pub fn with_blocks<F>(
from_height: BlockHeight,
limit: Option<u32>,
mut with_row: F,
) -> Result<(), Error<SqliteClientError, NoteId>>
) -> Result<(), SqliteClientError>
where
F: FnMut(CompactBlock) -> Result<(), Error<SqliteClientError, NoteId>>,
F: FnMut(CompactBlock) -> Result<(), SqliteClientError>,
{
// Fetch the CompactBlocks we need to scan
let mut stmt_blocks = cache
.0
.prepare(
"SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC LIMIT ?",
)
.map_err(db_error)?;
)?;
let rows = stmt_blocks
.query_map(
@ -123,20 +128,18 @@ where
data: row.get(1)?,
})
},
)
.map_err(db_error)?;
)?;
for row_result in rows {
let cbr = row_result.map_err(db_error)?;
let cbr = row_result?;
let block: CompactBlock = parse_from_bytes(&cbr.data).map_err(Error::from)?;
if block.height() != cbr.height {
return Err(Error::Database(SqliteClientError::CorruptedData(format!(
return Err(SqliteClientError::CorruptedData(format!(
"Block height {} did not match row's height field value {}",
block.height(),
cbr.height
)))
.into());
)));
}
with_row(block)?;
@ -163,6 +166,7 @@ mod tests {
use crate::{
chain::init::init_cache_database,
error::SqliteClientError,
tests::{
self, fake_compact_block, fake_compact_block_spending, insert_into_cache,
sapling_activation_height,
@ -321,8 +325,8 @@ mod tests {
&db_cache,
(&db_data).get_max_height_hash().unwrap(),
) {
Err(Error::InvalidChain(upper_bound, _)) => {
assert_eq!(upper_bound, sapling_activation_height() + 2)
Err(SqliteClientError::BackendError(Error::InvalidChain(lower_bound, _))) => {
assert_eq!(lower_bound, sapling_activation_height() + 2)
}
_ => panic!(),
}
@ -393,8 +397,8 @@ mod tests {
&db_cache,
(&db_data).get_max_height_hash().unwrap(),
) {
Err(Error::InvalidChain(upper_bound, _)) => {
assert_eq!(upper_bound, sapling_activation_height() + 3)
Err(SqliteClientError::BackendError(Error::InvalidChain(lower_bound, _))) => {
assert_eq!(lower_bound, sapling_activation_height() + 3)
}
_ => panic!(),
}
@ -502,17 +506,17 @@ mod tests {
);
insert_into_cache(&db_cache, &cb3);
match scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None) {
Ok(_) => panic!("Should have failed"),
Err(e) => {
Err(SqliteClientError::BackendError(e)) => {
assert_eq!(
e.to_string(),
ChainInvalid::block_height_discontinuity::<rusqlite::Error, NoteId>(
ChainInvalid::block_height_discontinuity::<NoteId>(
sapling_activation_height() + 1,
sapling_activation_height() + 2
)
.to_string()
);
}
Ok(_) | Err(_) => panic!("Should have failed"),
}
// If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both

View File

@ -1,7 +1,7 @@
use std::error;
use std::fmt;
use zcash_client_backend::data_api::error::Error;
use zcash_client_backend::data_api;
use crate::NoteId;
@ -25,6 +25,8 @@ pub enum SqliteClientError {
Io(std::io::Error),
/// A received memo cannot be interpreted as a UTF-8 string.
InvalidMemo(std::str::Utf8Error),
/// Wrapper for errors from zcash_client_backend
BackendError(data_api::error::Error<NoteId>),
}
impl error::Error for SqliteClientError {
@ -53,6 +55,7 @@ impl fmt::Display for SqliteClientError {
SqliteClientError::DbError(e) => write!(f, "{}", e),
SqliteClientError::Io(e) => write!(f, "{}", e),
SqliteClientError::InvalidMemo(e) => write!(f, "{}", e),
SqliteClientError::BackendError(e) => write!(f, "{}", e),
}
}
}
@ -75,12 +78,16 @@ impl From<bech32::Error> for SqliteClientError {
}
}
impl From<bs58::decode::Error> for SqliteClientError {
fn from(e: bs58::decode::Error) -> Self {
SqliteClientError::Base58(e)
}
}
pub fn db_error(r: rusqlite::Error) -> Error<SqliteClientError, NoteId> {
Error::Database(SqliteClientError::DbError(r))
impl From<data_api::error::Error<NoteId>> for SqliteClientError {
fn from(e: data_api::error::Error<NoteId>) -> Self {
SqliteClientError::BackendError(e)
}
}

View File

@ -43,14 +43,14 @@ use zcash_primitives::{
use zcash_client_backend::{
address::RecipientAddress,
data_api::{error::Error, BlockSource, ShieldedOutput, WalletRead, WalletWrite},
data_api::{BlockSource, ShieldedOutput, WalletRead, WalletWrite},
encoding::encode_payment_address,
proto::compact_formats::CompactBlock,
wallet::{AccountId, SpendableNote, WalletTx},
DecryptedOutput,
};
use crate::error::{db_error, SqliteClientError};
use crate::error::{SqliteClientError};
pub mod chain;
pub mod error;
@ -158,30 +158,30 @@ impl<P: consensus::Parameters> WalletDB<P> {
}
impl<P: consensus::Parameters> WalletRead for WalletDB<P> {
type Error = Error<SqliteClientError, NoteId>;
type Error = SqliteClientError;
type NoteRef = NoteId;
type TxRef = i64;
fn block_height_extrema(&self) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error> {
wallet::block_height_extrema(self).map_err(db_error)
wallet::block_height_extrema(self).map_err(SqliteClientError::from)
}
fn get_block_hash(&self, block_height: BlockHeight) -> Result<Option<BlockHash>, Self::Error> {
wallet::get_block_hash(self, block_height).map_err(db_error)
wallet::get_block_hash(self, block_height).map_err(SqliteClientError::from)
}
fn get_tx_height(&self, txid: TxId) -> Result<Option<BlockHeight>, Self::Error> {
wallet::get_tx_height(self, txid).map_err(db_error)
wallet::get_tx_height(self, txid).map_err(SqliteClientError::from)
}
fn get_extended_full_viewing_keys(
&self,
) -> Result<HashMap<AccountId, ExtendedFullViewingKey>, Self::Error> {
wallet::get_extended_full_viewing_keys(self).map_err(Error::Database)
wallet::get_extended_full_viewing_keys(self)
}
fn get_address(&self, account: AccountId) -> Result<Option<PaymentAddress>, Self::Error> {
wallet::get_address(self, account).map_err(Error::Database)
wallet::get_address(self, account)
}
fn is_valid_account_extfvk(
@ -189,11 +189,11 @@ impl<P: consensus::Parameters> WalletRead for WalletDB<P> {
account: AccountId,
extfvk: &ExtendedFullViewingKey,
) -> Result<bool, Self::Error> {
wallet::is_valid_account_extfvk(self, account, extfvk).map_err(Error::Database)
wallet::is_valid_account_extfvk(self, account, extfvk)
}
fn get_balance(&self, account: AccountId) -> Result<Amount, Self::Error> {
wallet::get_balance(self, account).map_err(Error::Database)
wallet::get_balance(self, account)
}
fn get_verified_balance(
@ -201,36 +201,36 @@ impl<P: consensus::Parameters> WalletRead for WalletDB<P> {
account: AccountId,
anchor_height: BlockHeight,
) -> Result<Amount, Self::Error> {
wallet::get_verified_balance(self, account, anchor_height).map_err(Error::Database)
wallet::get_verified_balance(self, account, anchor_height)
}
fn get_received_memo_as_utf8(
&self,
id_note: Self::NoteRef,
) -> Result<Option<String>, Self::Error> {
wallet::get_received_memo_as_utf8(self, id_note).map_err(Error::Database)
wallet::get_received_memo_as_utf8(self, id_note)
}
fn get_sent_memo_as_utf8(&self, id_note: Self::NoteRef) -> Result<Option<String>, Self::Error> {
wallet::get_sent_memo_as_utf8(self, id_note).map_err(Error::Database)
wallet::get_sent_memo_as_utf8(self, id_note)
}
fn get_commitment_tree(
&self,
block_height: BlockHeight,
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
wallet::get_commitment_tree(self, block_height).map_err(Error::Database)
wallet::get_commitment_tree(self, block_height)
}
fn get_witnesses(
&self,
block_height: BlockHeight,
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
wallet::get_witnesses(self, block_height).map_err(Error::Database)
wallet::get_witnesses(self, block_height)
}
fn get_nullifiers(&self) -> Result<Vec<(Nullifier, AccountId)>, Self::Error> {
wallet::get_nullifiers(self).map_err(Error::Database)
wallet::get_nullifiers(self)
}
fn get_spendable_notes(
@ -238,7 +238,7 @@ impl<P: consensus::Parameters> WalletRead for WalletDB<P> {
account: AccountId,
anchor_height: BlockHeight,
) -> Result<Vec<SpendableNote>, Self::Error> {
wallet::transact::get_spendable_notes(self, account, anchor_height).map_err(Error::Database)
wallet::transact::get_spendable_notes(self, account, anchor_height)
}
fn select_spendable_notes(
@ -248,7 +248,6 @@ impl<P: consensus::Parameters> WalletRead for WalletDB<P> {
anchor_height: BlockHeight,
) -> Result<Vec<SpendableNote>, Self::Error> {
wallet::transact::select_spendable_notes(self, account, target_value, anchor_height)
.map_err(Error::Database)
}
}
@ -278,7 +277,7 @@ pub struct DataConnStmtCache<'a, P> {
}
impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> {
type Error = Error<SqliteClientError, NoteId>;
type Error = SqliteClientError;
type NoteRef = NoteId;
type TxRef = i64;
@ -377,16 +376,10 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
where
F: FnOnce(&mut Self) -> Result<A, Self::Error>,
{
self.wallet_db
.conn
.execute("BEGIN IMMEDIATE", NO_PARAMS)
.map_err(db_error)?;
self.wallet_db.conn.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
match f(self) {
Ok(result) => {
self.wallet_db
.conn
.execute("COMMIT", NO_PARAMS)
.map_err(db_error)?;
self.wallet_db.conn.execute("COMMIT", NO_PARAMS)?;
Ok(result)
}
Err(error) => {
@ -414,7 +407,6 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
commitment_tree: &CommitmentTree<Node>,
) -> Result<(), Self::Error> {
wallet::insert_block(self, block_height, block_hash, block_time, commitment_tree)
.map_err(Error::Database)
}
fn rewind_to_height(&mut self, block_height: BlockHeight) -> Result<(), Self::Error> {
@ -426,7 +418,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
tx: &WalletTx,
height: BlockHeight,
) -> Result<Self::TxRef, Self::Error> {
wallet::put_tx_meta(self, tx, height).map_err(Error::Database)
wallet::put_tx_meta(self, tx, height)
}
fn put_tx_data(
@ -434,11 +426,11 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
tx: &Transaction,
created_at: Option<time::OffsetDateTime>,
) -> Result<Self::TxRef, Self::Error> {
wallet::put_tx_data(self, tx, created_at).map_err(Error::Database)
wallet::put_tx_data(self, tx, created_at)
}
fn mark_spent(&mut self, tx_ref: Self::TxRef, nf: &Nullifier) -> Result<(), Self::Error> {
wallet::mark_spent(self, tx_ref, nf).map_err(Error::Database)
wallet::mark_spent(self, tx_ref, nf)
}
// Assumptions:
@ -450,7 +442,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
nf_opt: &Option<Nullifier>,
tx_ref: Self::TxRef,
) -> Result<Self::NoteRef, Self::Error> {
wallet::put_received_note(self, output, nf_opt, tx_ref).map_err(Error::Database)
wallet::put_received_note(self, output, nf_opt, tx_ref)
}
fn insert_witness(
@ -459,15 +451,15 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
witness: &IncrementalWitness<Node>,
height: BlockHeight,
) -> Result<(), Self::Error> {
wallet::insert_witness(self, note_id, witness, height).map_err(Error::Database)
wallet::insert_witness(self, note_id, witness, height)
}
fn prune_witnesses(&mut self, below_height: BlockHeight) -> Result<(), Self::Error> {
wallet::prune_witnesses(self, below_height).map_err(Error::Database)
wallet::prune_witnesses(self, below_height)
}
fn update_expired_notes(&mut self, height: BlockHeight) -> Result<(), Self::Error> {
wallet::update_expired_notes(self, height).map_err(Error::Database)
wallet::update_expired_notes(self, height)
}
fn put_sent_note(
@ -475,7 +467,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
output: &DecryptedOutput,
tx_ref: Self::TxRef,
) -> Result<(), Self::Error> {
wallet::put_sent_note(self, output, tx_ref).map_err(Error::Database)
wallet::put_sent_note(self, output, tx_ref)
}
fn insert_sent_note(
@ -488,7 +480,6 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
memo: Option<Memo>,
) -> Result<(), Self::Error> {
wallet::insert_sent_note(self, tx_ref, output_index, account, to, value, memo)
.map_err(Error::Database)
}
}
@ -501,7 +492,7 @@ impl BlockDB {
}
impl BlockSource for BlockDB {
type Error = Error<SqliteClientError, NoteId>;
type Error = SqliteClientError;
fn with_blocks<F>(
&self,

View File

@ -27,7 +27,7 @@ use zcash_client_backend::{
};
use crate::{
error::{db_error, SqliteClientError},
error::{SqliteClientError},
DataConnStmtCache, NoteId, WalletDB,
};
@ -340,11 +340,11 @@ pub fn get_block_hash<P>(
pub fn rewind_to_height<P: consensus::Parameters>(
wdb: &WalletDB<P>,
block_height: BlockHeight,
) -> Result<(), Error<SqliteClientError, NoteId>> {
) -> Result<(), SqliteClientError> {
let sapling_activation_height = wdb
.params
.activation_height(NetworkUpgrade::Sapling)
.ok_or(Error::SaplingNotActive)?;
.ok_or(SqliteClientError::BackendError(Error::SaplingNotActive))?;
// Recall where we synced up to previously.
let last_scanned_height = wdb
@ -353,8 +353,7 @@ pub fn rewind_to_height<P: consensus::Parameters>(
row.get(0)
.map(|h: u32| h.into())
.or(Ok(sapling_activation_height - 1))
})
.map_err(db_error)?;
})?;
// nothing to do if we're deleting back down to the max height
if block_height >= last_scanned_height {
@ -365,24 +364,21 @@ pub fn rewind_to_height<P: consensus::Parameters>(
.execute(
"DELETE FROM sapling_witnesses WHERE block > ?",
&[u32::from(block_height)],
)
.map_err(db_error)?;
)?;
// Un-mine transactions.
wdb.conn
.execute(
"UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?",
&[u32::from(block_height)],
)
.map_err(db_error)?;
)?;
// Now that they aren't depended on, delete scanned blocks.
wdb.conn
.execute(
"DELETE FROM blocks WHERE height > ?",
&[u32::from(block_height)],
)
.map_err(db_error)?;
)?;
Ok(())
}