Require `BlockSource::with_blocks` fail on non-existent `from_height`

Previously this was not clearly specified, and the implementations in
`zcash_client_sqlite` behaved similarly to when `from_height = None`.

Closes zcash/librustzcash#892.
This commit is contained in:
Jack Grigg 2023-08-09 17:31:23 +00:00
parent 81d1928497
commit 44abd3450b
6 changed files with 56 additions and 2 deletions

View File

@ -50,7 +50,8 @@ and this library adheres to Rust's notion of
permits the caller to control the starting position of the scan range. permits the caller to control the starting position of the scan range.
In addition, the `limit` parameter is now required and has type `usize`. In addition, the `limit` parameter is now required and has type `usize`.
- `chain::BlockSource::with_blocks` now takes its limit as an `Option<usize>` - `chain::BlockSource::with_blocks` now takes its limit as an `Option<usize>`
instead of `Option<u32>`. instead of `Option<u32>`. It is also now required to return an error if
`from_height` is set to a block that does not exist in `self`.
- A new `CommitmentTree` variant has been added to `data_api::error::Error` - A new `CommitmentTree` variant has been added to `data_api::error::Error`
- `wallet::{create_spend_to_address, create_proposed_transaction, - `wallet::{create_spend_to_address, create_proposed_transaction,
shield_transparent_funds}` all now require that `WalletCommitmentTrees` be shield_transparent_funds}` all now require that `WalletCommitmentTrees` be

View File

@ -199,6 +199,8 @@ pub trait BlockSource {
/// `from_height`, applying the provided callback to each block. If `from_height` /// `from_height`, applying the provided callback to each block. If `from_height`
/// is `None` then scanning will begin at the first available block. /// is `None` then scanning will begin at the first available block.
/// ///
/// Returns an error if `from_height` is set to a block that does not exist in `self`.
///
/// * `WalletErrT`: the types of errors produced by the wallet operations performed /// * `WalletErrT`: the types of errors produced by the wallet operations performed
/// as part of processing each row. /// as part of processing each row.
/// * `NoteRefT`: the type of note identifiers in the wallet data store, for use in /// * `NoteRefT`: the type of note identifiers in the wallet data store, for use in

View File

@ -25,7 +25,11 @@ and this library adheres to Rust's notion of
a note could be spent with fewer than `min_confirmations` confirmations if the a note could be spent with fewer than `min_confirmations` confirmations if the
wallet did not contain enough observed blocks to satisfy the `min_confirmations` wallet did not contain enough observed blocks to satisfy the `min_confirmations`
value specified; this situation is now treated as an error. value specified; this situation is now treated as an error.
- A `BlockConflict` variant has been added to `zcash_client_sqlite::error::SqliteClientError` - `zcash_client_sqlite::error::SqliteClientError` has new error variants:
- `SqliteClientError::BlockConflict`
- `SqliteClientError::CacheMiss`
- `zcash_client_backend::FsBlockDbError` has a new error variant:
- `FsBlockDbError::CacheMiss`
- `zcash_client_sqlite::FsBlockDb::write_block_metadata` now overwrites any - `zcash_client_sqlite::FsBlockDb::write_block_metadata` now overwrites any
existing metadata entries that have the same height as a new entry. existing metadata entries that have the same height as a new entry.
@ -39,6 +43,8 @@ and this library adheres to Rust's notion of
- Fixed an off-by-one error in the `BlockSource` implementation for the SQLite-backed - Fixed an off-by-one error in the `BlockSource` implementation for the SQLite-backed
`BlockDb` block database which could result in blocks being skipped at the start of `BlockDb` block database which could result in blocks being skipped at the start of
scan ranges. scan ranges.
- `zcash_client_sqlite::{BlockDb, FsBlockDb}::with_blocks` now return an error
if `from_height` is set to a block height that does not exist in the cache.
- `WalletDb::get_transaction` no longer returns an error when called on a transaction - `WalletDb::get_transaction` no longer returns an error when called on a transaction
that has not yet been mined, unless the transaction's consensus branch ID cannot be that has not yet been mined, unless the transaction's consensus branch ID cannot be
determined by other means. determined by other means.

View File

@ -58,8 +58,20 @@ where
]) ])
.map_err(to_chain_error)?; .map_err(to_chain_error)?;
// Only look for the `from_height` in the scanned blocks if it is set.
let mut from_height_found = from_height.is_none();
while let Some(row) = rows.next().map_err(to_chain_error)? { while let Some(row) = rows.next().map_err(to_chain_error)? {
let height = BlockHeight::from_u32(row.get(0).map_err(to_chain_error)?); let height = BlockHeight::from_u32(row.get(0).map_err(to_chain_error)?);
if !from_height_found {
// We will only perform this check on the first row.
let from_height = from_height.expect("can only reach here if set");
if from_height != height {
return Err(to_chain_error(SqliteClientError::CacheMiss(from_height)));
} else {
from_height_found = true;
}
}
let data: Vec<u8> = row.get(1).map_err(to_chain_error)?; let data: Vec<u8> = row.get(1).map_err(to_chain_error)?;
let block = CompactBlock::decode(&data[..]).map_err(to_chain_error)?; let block = CompactBlock::decode(&data[..]).map_err(to_chain_error)?;
if block.height() != height { if block.height() != height {
@ -73,6 +85,11 @@ where
with_row(block)?; with_row(block)?;
} }
if !from_height_found {
let from_height = from_height.expect("can only reach here if set");
return Err(to_chain_error(SqliteClientError::CacheMiss(from_height)));
}
Ok(()) Ok(())
} }
@ -260,8 +277,20 @@ where
) )
.map_err(to_chain_error)?; .map_err(to_chain_error)?;
// Only look for the `from_height` in the scanned blocks if it is set.
let mut from_height_found = from_height.is_none();
for row_result in rows { for row_result in rows {
let cbr = row_result.map_err(to_chain_error)?; let cbr = row_result.map_err(to_chain_error)?;
if !from_height_found {
// We will only perform this check on the first row.
let from_height = from_height.expect("can only reach here if set");
if from_height != cbr.height {
return Err(to_chain_error(FsBlockDbError::CacheMiss(from_height)));
} else {
from_height_found = true;
}
}
let mut block_file = let mut block_file =
File::open(cbr.block_file_path(&cache.blocks_dir)).map_err(to_chain_error)?; File::open(cbr.block_file_path(&cache.blocks_dir)).map_err(to_chain_error)?;
let mut block_data = vec![]; let mut block_data = vec![];
@ -282,6 +311,11 @@ where
with_block(block)?; with_block(block)?;
} }
if !from_height_found {
let from_height = from_height.expect("can only reach here if set");
return Err(to_chain_error(FsBlockDbError::CacheMiss(from_height)));
}
Ok(()) Ok(())
} }

View File

@ -85,6 +85,8 @@ pub enum SqliteClientError {
/// An error occurred in inserting data into or accessing data from one of the wallet's note /// An error occurred in inserting data into or accessing data from one of the wallet's note
/// commitment trees. /// commitment trees.
CommitmentTree(ShardTreeError<commitment_tree::Error>), CommitmentTree(ShardTreeError<commitment_tree::Error>),
CacheMiss(BlockHeight),
} }
impl error::Error for SqliteClientError { impl error::Error for SqliteClientError {
@ -128,6 +130,7 @@ impl fmt::Display for SqliteClientError {
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
SqliteClientError::AddressNotRecognized(_) => write!(f, "The address associated with a received txo is not identifiable as belonging to the wallet."), 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::CommitmentTree(err) => write!(f, "An error occurred accessing or updating note commitment tree data: {}.", err),
SqliteClientError::CacheMiss(height) => write!(f, "Requested height {} does not exist in the block cache.", height),
} }
} }
} }

View File

@ -883,6 +883,7 @@ pub enum FsBlockDbError {
InvalidBlockstoreRoot(PathBuf), InvalidBlockstoreRoot(PathBuf),
InvalidBlockPath(PathBuf), InvalidBlockPath(PathBuf),
CorruptedData(String), CorruptedData(String),
CacheMiss(BlockHeight),
} }
#[cfg(feature = "unstable")] #[cfg(feature = "unstable")]
@ -1047,6 +1048,13 @@ impl std::fmt::Display for FsBlockDbError {
e, e,
) )
} }
FsBlockDbError::CacheMiss(height) => {
write!(
f,
"Requested height {} does not exist in the block cache",
height
)
}
} }
} }
} }