Skip to content

Commit

Permalink
Merge pull request #1506 from zcash/zcs-fix-migrations
Browse files Browse the repository at this point in the history
zcash_client_sqlite: Fix migration DAG edges
  • Loading branch information
nuttycom committed Aug 21, 2024
2 parents 40ca428 + 3dba2fa commit 876a5a3
Show file tree
Hide file tree
Showing 29 changed files with 318 additions and 85 deletions.
4 changes: 4 additions & 0 deletions zcash_client_sqlite/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this library adheres to Rust's notion of

## [Unreleased]

### Fixed
- The dependencies of the `tx_retrieval_queue` migration have been fixed to
enable migrating wallets containing certain kinds of transactions.

## [0.11.0] - 2024-08-20

`zcash_client_sqlite` now provides capabilities for the management of ephemeral
Expand Down
155 changes: 135 additions & 20 deletions zcash_client_sqlite/src/wallet/init/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use std::rc::Rc;

use schemer_rusqlite::RusqliteMigration;
use secrecy::SecretVec;
use uuid::Uuid;
use zcash_protocol::consensus;

use super::WalletMigrationError;
Expand All @@ -51,26 +52,28 @@ pub(super) fn all_migrations<P: consensus::Parameters + 'static>(
// |
// v_transactions_net
// |
// received_notes_nullable_nf------
// / | \
// / | \
// --------------- shardtree_support sapling_memo_consistency nullifier_map
// / / \ \
// orchard_shardtree add_account_birthdays receiving_key_scopes v_transactions_transparent_history
// | \ | |
// v_sapling_shard_unscanned_ranges \ | v_tx_outputs_use_legacy_false
// | \ | |
// wallet_summaries \ | v_transactions_shielding_balance
// \ \ | |
// \ \ | v_transactions_note_uniqueness
// \ \ | /
// -------------------- full_account_ids
// | \
// orchard_received_notes spend_key_available
// / \
// ensure_orchard_ua_receiver utxos_to_txos
// / \
// ephemeral_addresses tx_retrieval_queue
// received_notes_nullable_nf----------------------
// / | \
// / | \
// --------------- shardtree_support sapling_memo_consistency nullifier_map
// / / \ \ |
// orchard_shardtree add_account_birthdays receiving_key_scopes v_transactions_transparent_history |
// | | \ | | |
// | v_sapling_shard_unscanned_ranges \ | v_tx_outputs_use_legacy_false |
// | | \ | | |
// | wallet_summaries \ | v_transactions_shielding_balance |
// | \ \ | | /
// \ \ \ | v_transactions_note_uniqueness /
// \ \ \ | / /
// \ -------------------- full_account_ids /
// \ / \ /
// \ orchard_received_notes spend_key_available /
// \ / \ / /
// \ ensure_orchard_ua_receiver utxos_to_txos / /
// \ \ | / /
// \ \ ephemeral_addresses / /
// \ \ | / /
// ------------------------------ tx_retrieval_queue ----------------------------
vec![
Box::new(initial_setup::Migration {}),
Box::new(utxos_table::Migration {}),
Expand Down Expand Up @@ -131,8 +134,69 @@ pub(super) fn all_migrations<P: consensus::Parameters + 'static>(
]
}

/// All states of the migration DAG that have been exposed in a public crate release, in
/// the order that crate users would have encountered them.
///
/// Omitted versions had the same migration state as the first prior version that is
/// included.
#[allow(dead_code)]
const PUBLIC_MIGRATION_STATES: &[&[Uuid]] = &[
V_0_4_0, V_0_6_0, V_0_8_0, V_0_9_0, V_0_10_0, V_0_10_3, V_0_11_0, V_0_11_1,
];

/// Leaf migrations in the 0.4.0 release.
const V_0_4_0: &[Uuid] = &[add_transaction_views::MIGRATION_ID];

/// Leaf migrations in the 0.6.0 release.
const V_0_6_0: &[Uuid] = &[v_transactions_net::MIGRATION_ID];

/// Leaf migrations in the 0.8.0 release.
const V_0_8_0: &[Uuid] = &[
nullifier_map::MIGRATION_ID,
v_transactions_note_uniqueness::MIGRATION_ID,
wallet_summaries::MIGRATION_ID,
];

/// Leaf migrations in the 0.9.0 release.
const V_0_9_0: &[Uuid] = &[
nullifier_map::MIGRATION_ID,
receiving_key_scopes::MIGRATION_ID,
v_transactions_note_uniqueness::MIGRATION_ID,
wallet_summaries::MIGRATION_ID,
];

/// Leaf migrations in the 0.10.0 release.
const V_0_10_0: &[Uuid] = &[
nullifier_map::MIGRATION_ID,
orchard_received_notes::MIGRATION_ID,
orchard_shardtree::MIGRATION_ID,
];

/// Leaf migrations in the 0.10.3 release.
const V_0_10_3: &[Uuid] = &[
ensure_orchard_ua_receiver::MIGRATION_ID,
nullifier_map::MIGRATION_ID,
orchard_shardtree::MIGRATION_ID,
];

/// Leaf migrations in the 0.11.0 release.
const V_0_11_0: &[Uuid] = &[
ensure_orchard_ua_receiver::MIGRATION_ID,
ephemeral_addresses::MIGRATION_ID,
nullifier_map::MIGRATION_ID,
orchard_shardtree::MIGRATION_ID,
spend_key_available::MIGRATION_ID,
tx_retrieval_queue::MIGRATION_ID,
];

/// Leaf migrations in the 0.11.1 release.
const V_0_11_1: &[Uuid] = &[tx_retrieval_queue::MIGRATION_ID];

#[cfg(test)]
mod tests {
use std::collections::HashSet;

use rusqlite::Connection;
use secrecy::Secret;
use tempfile::NamedTempFile;
use uuid::Uuid;
Expand All @@ -157,4 +221,55 @@ mod tests {
Ok(_)
);
}

#[test]
fn migrate_between_releases_without_data() {
let data_file = NamedTempFile::new().unwrap();
let mut db_data = WalletDb::for_path(data_file.path(), Network::TestNetwork).unwrap();

let seed = [0xab; 32].to_vec();

let mut prev_state = HashSet::new();
let mut ensure_migration_state_changed = |conn: &Connection| {
let new_state = conn
.prepare_cached("SELECT * FROM schemer_migrations")
.unwrap()
.query_map([], |row| row.get::<_, [u8; 16]>(0).map(Uuid::from_bytes))
.unwrap()
.collect::<Result<HashSet<Uuid>, _>>()
.unwrap();
assert!(prev_state != new_state);
prev_state = new_state;
};

let mut prev_leaves: &[Uuid] = &[];
for migrations in super::PUBLIC_MIGRATION_STATES {
assert_matches!(
init_wallet_db_internal(
&mut db_data,
Some(Secret::new(seed.clone())),
migrations,
false
),
Ok(_)
);

// If we have any new leaves, ensure the migration state changed. This lets us
// represent releases that changed the graph edges without introducing any new
// migrations.
if migrations.iter().any(|m| !prev_leaves.contains(m)) {
ensure_migration_state_changed(&db_data.conn);
}

prev_leaves = *migrations;
}

// Now check that we can migrate from the last public release to the current
// migration state in this branch.
assert_matches!(
init_wallet_db_internal(&mut db_data, Some(Secret::new(seed)), &[], false),
Ok(_)
);
// We don't ensure that the migration state changed, because it may not have.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::shardtree_support;

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

const DEPENDENCIES: [Uuid; 1] = [shardtree_support::MIGRATION_ID];
const DEPENDENCIES: &[Uuid] = &[shardtree_support::MIGRATION_ID];

pub(super) struct Migration<P> {
pub(super) params: P,
Expand All @@ -24,7 +24,7 @@ impl<P> schemer::Migration for Migration<P> {
}

fn dependencies(&self) -> HashSet<Uuid> {
DEPENDENCIES.into_iter().collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down Expand Up @@ -91,7 +91,7 @@ mod tests {
init_wallet_db_internal(
&mut db_data,
Some(Secret::new(seed_bytes.clone())),
&DEPENDENCIES,
DEPENDENCIES,
false,
)
.unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ use crate::wallet::init::WalletMigrationError;

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

const DEPENDENCIES: &[Uuid] = &[
add_utxo_account::MIGRATION_ID,
sent_notes_to_internal::MIGRATION_ID,
];

pub(crate) struct Migration;

impl schemer::Migration for Migration {
Expand All @@ -26,12 +31,7 @@ impl schemer::Migration for Migration {
}

fn dependencies(&self) -> HashSet<Uuid> {
[
add_utxo_account::MIGRATION_ID,
sent_notes_to_internal::MIGRATION_ID,
]
.into_iter()
.collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use {
/// This migration adds an account identifier column to the UTXOs table.
pub(super) const MIGRATION_ID: Uuid = Uuid::from_u128(0x761884d6_30d8_44ef_b204_0b82551c4ca1);

const DEPENDENCIES: &[Uuid] = &[utxos_table::MIGRATION_ID, addresses_table::MIGRATION_ID];

pub(super) struct Migration<P> {
pub(super) _params: P,
}
Expand All @@ -38,9 +40,7 @@ impl<P> schemer::Migration for Migration<P> {
}

fn dependencies(&self) -> HashSet<Uuid> {
[utxos_table::MIGRATION_ID, addresses_table::MIGRATION_ID]
.into_iter()
.collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use super::ufvk_support;
/// the `accounts` table.
pub(super) const MIGRATION_ID: Uuid = Uuid::from_u128(0xd956978c_9c87_4d6e_815d_fb8f088d094c);

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

pub(crate) struct Migration<P: consensus::Parameters> {
pub(crate) params: P,
}
Expand All @@ -30,7 +32,7 @@ impl<P: consensus::Parameters> schemer::Migration for Migration<P> {
}

fn dependencies(&self) -> HashSet<Uuid> {
[ufvk_support::MIGRATION_ID].into_iter().collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::{wallet::init::WalletMigrationError, UA_ORCHARD, UA_TRANSPARENT};

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

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

pub(super) struct Migration<P> {
pub(super) params: P,
}
Expand All @@ -25,7 +27,7 @@ impl<P> schemer::Migration for Migration<P> {
}

fn dependencies(&self) -> HashSet<Uuid> {
[orchard_received_notes::MIGRATION_ID].into_iter().collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::utxos_to_txos;

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

const DEPENDENCIES: [Uuid; 1] = [utxos_to_txos::MIGRATION_ID];
const DEPENDENCIES: &[Uuid] = &[utxos_to_txos::MIGRATION_ID];

#[allow(dead_code)]
pub(super) struct Migration<P> {
Expand All @@ -29,7 +29,7 @@ impl<P> schemer::Migration for Migration<P> {
}

fn dependencies(&self) -> HashSet<Uuid> {
DEPENDENCIES.into_iter().collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down Expand Up @@ -210,7 +210,7 @@ mod tests {
init_wallet_db_internal(
&mut db_data,
Some(Secret::new(seed0.clone())),
&super::DEPENDENCIES,
super::DEPENDENCIES,
false,
)
.unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ pub(crate) struct Migration<P: consensus::Parameters> {
pub(super) params: P,
}

const DEPENDENCIES: &[Uuid] = &[
receiving_key_scopes::MIGRATION_ID,
add_account_birthdays::MIGRATION_ID,
v_transactions_note_uniqueness::MIGRATION_ID,
wallet_summaries::MIGRATION_ID,
];

impl<P: consensus::Parameters> schemer::Migration for Migration<P> {
fn id(&self) -> Uuid {
MIGRATION_ID
}

fn dependencies(&self) -> HashSet<Uuid> {
[
receiving_key_scopes::MIGRATION_ID,
add_account_birthdays::MIGRATION_ID,
v_transactions_note_uniqueness::MIGRATION_ID,
wallet_summaries::MIGRATION_ID,
]
.into_iter()
.collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use super::received_notes_nullable_nf;

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

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

pub(super) struct Migration;

impl schemer::Migration for Migration {
Expand All @@ -21,9 +23,7 @@ impl schemer::Migration for Migration {
}

fn dependencies(&self) -> HashSet<Uuid> {
[received_notes_nullable_nf::MIGRATION_ID]
.into_iter()
.collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use crate::wallet::{init::WalletMigrationError, pool_code};

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

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

pub(super) struct Migration;

impl schemer::Migration for Migration {
Expand All @@ -20,7 +22,7 @@ impl schemer::Migration for Migration {
}

fn dependencies(&self) -> HashSet<Uuid> {
[full_account_ids::MIGRATION_ID].into_iter().collect()
DEPENDENCIES.iter().copied().collect()
}

fn description(&self) -> &'static str {
Expand Down
Loading

0 comments on commit 876a5a3

Please sign in to comment.