Skip to content

Commit

Permalink
zcash_client_sqlite: Verify sqlite version compatibility on wallet init.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Sep 3, 2024
1 parent c91b34e commit 61584f7
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 11 deletions.
18 changes: 12 additions & 6 deletions zcash_client_sqlite/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ and this library adheres to Rust's notion of

### Changed
- The `v_tx_outputs` view was modified slightly to support older versions of
`sqlite`.

`sqlite`. Queries to the exposed `v_tx_outputs` and `v_transactions` views
are supported for SQLite versions back to `3.19.x`.
- `zcash_client_sqlite::wallet::init::WalletMigrationError` has an additional
variant, `DatabaseNotSupported`. The `init_wallet_db` function now checks
that the sqlite version in use is compatible with the features required by
the wallet and returns this error if not. SQLite version `3.35` or higher
is required for use with `zcash_client_sqlite`.


## [0.11.1] - 2024-08-21

Expand All @@ -23,7 +29,7 @@ and this library adheres to Rust's notion of
`zcash_client_sqlite` now provides capabilities for the management of ephemeral
transparent addresses in support of the creation of ZIP 320 transaction pairs.

In addition, `zcash_client_sqlite` now provides improved tracking of transparent
In addition, `zcash_client_sqlite` now provides improved tracking of transparent
wallet history in support of the API changes in `zcash_client_backend 0.13`,
and the `v_transactions` view has been modified to provide additional metadata
about the relationship of each transaction to the wallet, in particular whether
Expand Down Expand Up @@ -70,11 +76,11 @@ or not the transaction represents a wallet-internal shielding operation.
## [0.10.1] - 2024-03-25

### Fixed
- The `sent_notes` table's `received_note` constraint was excessively restrictive
after zcash/librustzcash#1306. Any databases that have migrations from
- The `sent_notes` table's `received_note` constraint was excessively restrictive
after zcash/librustzcash#1306. Any databases that have migrations from
zcash_client_sqlite 0.10.0 applied should be wiped and restored from seed.
In order to ensure that the incorrect migration is not used, the migration
id for the `full_account_ids` migration has been changed from
id for the `full_account_ids` migration has been changed from
`0x1b104345_f27e_42da_a9e3_1de22694da43` to `0x6d02ec76_8720_4cc6_b646_c4e2ce69221c`

## [0.10.0] - 2024-03-25
Expand Down
2 changes: 1 addition & 1 deletion zcash_client_sqlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ schemer.workspace = true
schemer-rusqlite.workspace = true
time.workspace = true
uuid.workspace = true
regex = "1.4"

# Dependencies used internally:
# (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.)
Expand All @@ -87,7 +88,6 @@ orchard = { workspace = true, features = ["test-dependencies"] }
proptest.workspace = true
rand_chacha.workspace = true
rand_core.workspace = true
regex = "1.4"
tempfile = "3.5.0"
zcash_keys = { workspace = true, features = ["test-dependencies"] }
zcash_note_encryption.workspace = true
Expand Down
51 changes: 51 additions & 0 deletions zcash_client_sqlite/src/wallet/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::fmt;
use std::rc::Rc;

use regex::Regex;
use schemer::{Migrator, MigratorError};
use schemer_rusqlite::RusqliteAdapter;
use secrecy::SecretVec;
Expand All @@ -20,8 +21,15 @@ use crate::{error::SqliteClientError, WalletDb};

mod migrations;

const SQLITE_MAJOR_VERSION: u32 = 3;
const MIN_SQLITE_MINOR_VERSION: u32 = 35;

#[derive(Debug)]
pub enum WalletMigrationError {
/// A feature required by the wallet database is not supported by the version of
/// SQLite that the migration is running against.
DatabaseNotSupported(String),

/// The seed is required for the migration.
SeedRequired,

Expand Down Expand Up @@ -100,6 +108,13 @@ impl From<SqliteClientError> for WalletMigrationError {
impl fmt::Display for WalletMigrationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
WalletMigrationError::DatabaseNotSupported(version) => {
write!(
f,
"The installed SQLite version {} does not support operations required by the wallet.",
version
)
}
WalletMigrationError::SeedRequired => {
write!(
f,
Expand Down Expand Up @@ -305,6 +320,8 @@ fn init_wallet_db_internal<P: consensus::Parameters + 'static>(
) -> Result<(), MigratorError<WalletMigrationError>> {
let seed = seed.map(Rc::new);

verify_sqlite_version_compatibility(&wdb.conn).map_err(MigratorError::Adapter)?;

// Turn off foreign key enforcement, to ensure that table replacement does not break foreign
// key references in table definitions.
//
Expand Down Expand Up @@ -357,6 +374,40 @@ fn init_wallet_db_internal<P: consensus::Parameters + 'static>(
Ok(())
}

/// Verify that the sqlite version in use supports the features required by this library.
/// Note that the version of sqlite available to the database backend may be different
/// from what is used to query the views that are part of the public API.
fn verify_sqlite_version_compatibility(
conn: &rusqlite::Connection,
) -> Result<(), WalletMigrationError> {
let sqlite_version =
conn.query_row("SELECT sqlite_version()", [], |row| row.get::<_, String>(0))?;

let version_re = Regex::new(r"^(?<major>[0-9]+)\.(?<minor>[0-9]+).+$").unwrap();
let captures =
version_re
.captures(&sqlite_version)
.ok_or(WalletMigrationError::DatabaseNotSupported(
"Unknown".to_owned(),
))?;
let parse_int = |value: &str| {
value.parse::<u32>().map_err(|_| {
WalletMigrationError::CorruptedData(format!(
"Cannot decode SQLite major version {}",
&captures["major"]
))
})
};
let major = parse_int(&captures["major"])?;
let minor = parse_int(&captures["minor"])?;

if major != SQLITE_MAJOR_VERSION || minor < MIN_SQLITE_MINOR_VERSION {
Err(WalletMigrationError::DatabaseNotSupported(sqlite_version))
} else {
Ok(())
}
}

#[cfg(test)]
#[allow(deprecated)]
mod tests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! A migration that removes the use of `FALSE` in sqlite view definitions.
//! This is necessary to support older
//! Modifies definitions to avoid keywords that may not be available in older SQLite versions.
use std::collections::HashSet;

use rusqlite;
Expand All @@ -9,7 +8,7 @@ use uuid::Uuid;

use crate::wallet::init::{migrations::tx_retrieval_queue, WalletMigrationError};

pub(super) const MIGRATION_ID: Uuid = Uuid::from_u128(0xc9ed1fb5_b2c3_467f_89dc_2591dcca5562);
pub(super) const MIGRATION_ID: Uuid = Uuid::from_u128(0x156d8c8f_2173_4b59_89b6_75697d5a2103);

const DEPENDENCIES: &[Uuid] = &[tx_retrieval_queue::MIGRATION_ID];

Expand All @@ -25,7 +24,7 @@ impl schemer::Migration for Migration {
}

fn description(&self) -> &'static str {
"Removes the FALSE keyword from the v_tx_outputs view definition"
"Modifies definitions to avoid keywords that may not be available in older SQLite versions."
}
}

Expand Down

0 comments on commit 61584f7

Please sign in to comment.