use std::collections::HashSet;
use schemerz_rusqlite::RusqliteMigration;
use uuid::Uuid;
use super::transparent_ledger_schema;
use crate::wallet::init::WalletMigrationError;
pub const MIGRATION_ID: Uuid = Uuid::from_u128(0xb7c4e2a1_9d3f_4e8b_a6c5_1f0e8d7c6b5a);
const DEPENDENCIES: &[Uuid] = &[transparent_ledger_schema::MIGRATION_ID];
pub(super) struct Migration;
impl schemerz::Migration<Uuid> for Migration {
fn id(&self) -> Uuid {
MIGRATION_ID
}
fn dependencies(&self) -> HashSet<Uuid> {
DEPENDENCIES.iter().copied().collect()
}
fn description(&self) -> &'static str {
"Adds tx_retrieval_queue.policy_generation and Enhance route 2 (private details unsupported)."
}
}
impl RusqliteMigration for Migration {
type Error = WalletMigrationError;
fn up(&self, conn: &rusqlite::Transaction) -> Result<(), Self::Error> {
conn.execute_batch(
"ALTER TABLE tx_retrieval_queue
ADD COLUMN policy_generation INTEGER NOT NULL DEFAULT 0;",
)?;
let view: String = conn.query_row(
"SELECT sql FROM sqlite_master WHERE type = 'view' AND name = 'v_transactions'",
[],
|row| row.get(0),
)?;
conn.execute_batch("DROP VIEW v_transactions")?;
conn.execute_batch(
r#"
CREATE TABLE ironwood_enhance_routing_new (
transaction_id INTEGER PRIMARY KEY
REFERENCES transactions(id_tx) ON DELETE CASCADE,
route INTEGER NOT NULL CHECK (route IN (0, 1, 2)),
history_expiry_height INTEGER
CHECK (history_expiry_height >= 0 AND history_expiry_height < 500000000)
);
INSERT INTO ironwood_enhance_routing_new (
transaction_id, route, history_expiry_height
)
SELECT transaction_id, route, history_expiry_height
FROM ironwood_enhance_routing;
DROP TABLE ironwood_enhance_routing;
ALTER TABLE ironwood_enhance_routing_new RENAME TO ironwood_enhance_routing;
"#,
)?;
conn.execute_batch(&view)?;
Ok(())
}
fn down(&self, _transaction: &rusqlite::Transaction) -> Result<(), Self::Error> {
Err(WalletMigrationError::CannotRevert(MIGRATION_ID))
}
}
#[cfg(test)]
mod tests {
use super::{DEPENDENCIES, MIGRATION_ID};
use crate::wallet::init::migrations::tests::test_migrate;
#[test]
fn migrate() {
test_migrate(&[MIGRATION_ID]);
}
#[test]
fn existing_queue_rows_keep_generation_zero_and_route_two_is_accepted() {
use tempfile::NamedTempFile;
use zcash_protocol::consensus::Network;
use crate::{
WalletDb,
testing::db::{test_clock, test_rng},
wallet::init::WalletMigrator,
};
let file = NamedTempFile::new().unwrap();
let mut db =
WalletDb::for_path(file.path(), Network::TestNetwork, test_clock(), test_rng())
.unwrap();
WalletMigrator::new()
.init_or_migrate_to(&mut db, DEPENDENCIES)
.unwrap();
db.conn
.execute_batch(
"INSERT INTO transactions (id_tx, txid, min_observed_height)
VALUES (1, X'01', 1);
INSERT INTO tx_retrieval_queue (txid, query_type)
VALUES (X'01', 1);
INSERT INTO ironwood_enhance_routing (transaction_id, route)
VALUES (1, 1);",
)
.unwrap();
WalletMigrator::new().init_or_migrate(&mut db).unwrap();
let generation: i64 = db
.conn
.query_row(
"SELECT policy_generation FROM tx_retrieval_queue WHERE txid = X'01'",
[],
|row| row.get(0),
)
.unwrap();
assert_eq!(generation, 0);
db.conn
.execute(
"UPDATE ironwood_enhance_routing SET route = 2 WHERE transaction_id = 1",
[],
)
.unwrap();
let route: i64 = db
.conn
.query_row(
"SELECT route FROM ironwood_enhance_routing WHERE transaction_id = 1",
[],
|row| row.get(0),
)
.unwrap();
assert_eq!(route, 2);
}
}