Add a migration to add account ID to the utxos table.
This commit is contained in:
parent
ab6f7929b4
commit
14787f574f
|
@ -252,7 +252,7 @@ impl<P: consensus::Parameters> WalletReadTransparent for WalletDb<P> {
|
|||
&self,
|
||||
account: AccountId,
|
||||
) -> Result<HashSet<TransparentAddress>, Self::Error> {
|
||||
wallet::get_transparent_receivers(self, account)
|
||||
wallet::get_transparent_receivers(&self.params, &self.conn, account)
|
||||
}
|
||||
|
||||
fn get_unspent_transparent_outputs(
|
||||
|
|
|
@ -12,9 +12,6 @@ use rusqlite::{named_params, OptionalExtension, ToSql};
|
|||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use zcash_primitives::{
|
||||
block::BlockHash,
|
||||
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
|
||||
|
@ -41,7 +38,8 @@ use zcash_primitives::legacy::TransparentAddress;
|
|||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
crate::UtxoId,
|
||||
rusqlite::params,
|
||||
rusqlite::{params, Connection},
|
||||
std::collections::HashSet,
|
||||
zcash_client_backend::{encoding::AddressCodec, wallet::WalletTransparentOutput},
|
||||
zcash_primitives::{
|
||||
legacy::{keys::IncomingViewingKey, Script},
|
||||
|
@ -276,20 +274,19 @@ pub(crate) fn get_current_address<P: consensus::Parameters>(
|
|||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
pub(crate) fn get_transparent_receivers<P: consensus::Parameters>(
|
||||
wdb: &WalletDb<P>,
|
||||
params: &P,
|
||||
conn: &Connection,
|
||||
account: AccountId,
|
||||
) -> Result<HashSet<TransparentAddress>, SqliteClientError> {
|
||||
let mut ret = HashSet::new();
|
||||
|
||||
// Get all UAs derived
|
||||
let mut ua_query = wdb
|
||||
.conn
|
||||
.prepare("SELECT address FROM addresses WHERE account = :account")?;
|
||||
let mut ua_query = conn.prepare("SELECT address FROM addresses WHERE account = :account")?;
|
||||
let mut rows = ua_query.query(named_params![":account": &u32::from(account)])?;
|
||||
|
||||
while let Some(row) = rows.next()? {
|
||||
let ua_str: String = row.get(0)?;
|
||||
let ua = RecipientAddress::decode(&wdb.params, &ua_str)
|
||||
let ua = RecipientAddress::decode(params, &ua_str)
|
||||
.ok_or_else(|| {
|
||||
SqliteClientError::CorruptedData("Not a valid Zcash recipient address".to_owned())
|
||||
})
|
||||
|
@ -306,12 +303,12 @@ pub(crate) fn get_transparent_receivers<P: consensus::Parameters>(
|
|||
}
|
||||
|
||||
// Get the UFVK for the account.
|
||||
let ufvk_str: String = wdb.conn.query_row(
|
||||
let ufvk_str: String = conn.query_row(
|
||||
"SELECT ufvk FROM accounts WHERE account = :account",
|
||||
[u32::from(account)],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
let ufvk = UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str)
|
||||
let ufvk = UnifiedFullViewingKey::decode(params, &ufvk_str)
|
||||
.map_err(SqliteClientError::CorruptedData)?;
|
||||
|
||||
// Derive the default transparent address (if it wasn't already part of a derived UA).
|
||||
|
|
|
@ -394,8 +394,9 @@ mod tests {
|
|||
fee INTEGER,
|
||||
FOREIGN KEY (block) REFERENCES blocks(height)
|
||||
)",
|
||||
"CREATE TABLE utxos (
|
||||
"CREATE TABLE \"utxos\" (
|
||||
id_utxo INTEGER PRIMARY KEY,
|
||||
received_by_account INTEGER NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
prevout_txid BLOB NOT NULL,
|
||||
prevout_idx INTEGER NOT NULL,
|
||||
|
@ -403,6 +404,7 @@ mod tests {
|
|||
value_zat INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL,
|
||||
spent_in_tx INTEGER,
|
||||
FOREIGN KEY (received_by_account) REFERENCES accounts(account),
|
||||
FOREIGN KEY (spent_in_tx) REFERENCES transactions(id_tx),
|
||||
CONSTRAINT tx_outpoint UNIQUE (prevout_txid, prevout_idx)
|
||||
)",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod add_transaction_views;
|
||||
mod add_utxo_account;
|
||||
mod addresses_table;
|
||||
mod initial_setup;
|
||||
mod ufvk_support;
|
||||
|
@ -27,5 +28,8 @@ pub(super) fn all_migrations<P: consensus::Parameters + 'static>(
|
|||
Box::new(add_transaction_views::Migration {
|
||||
params: params.clone(),
|
||||
}),
|
||||
Box::new(add_utxo_account::Migration {
|
||||
_params: params.clone(),
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -214,13 +214,22 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rusqlite::{self, params};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use zcash_client_backend::keys::UnifiedSpendingKey;
|
||||
use zcash_primitives::zip32::AccountId;
|
||||
|
||||
use crate::{
|
||||
tests,
|
||||
wallet::init::{init_wallet_db, init_wallet_db_internal, migrations::addresses_table},
|
||||
WalletDb,
|
||||
};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
crate::wallet::init::migrations::ufvk_support,
|
||||
rusqlite::params,
|
||||
zcash_client_backend::{encoding::AddressCodec, keys::UnifiedSpendingKey},
|
||||
zcash_client_backend::encoding::AddressCodec,
|
||||
zcash_primitives::{
|
||||
consensus::{BlockHeight, BranchId},
|
||||
legacy::{keys::IncomingViewingKey, Script},
|
||||
|
@ -231,25 +240,29 @@ mod tests {
|
|||
},
|
||||
TransactionData, TxVersion,
|
||||
},
|
||||
zip32::AccountId,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
tests,
|
||||
wallet::init::{init_wallet_db, init_wallet_db_internal, migrations::addresses_table},
|
||||
WalletDb,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn transaction_views() {
|
||||
let data_file = NamedTempFile::new().unwrap();
|
||||
let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap();
|
||||
init_wallet_db_internal(&mut db_data, None, Some(addresses_table::MIGRATION_ID)).unwrap();
|
||||
let usk =
|
||||
UnifiedSpendingKey::from_seed(&tests::network(), &[0u8; 32][..], AccountId::from(0))
|
||||
.unwrap();
|
||||
let ufvk = usk.to_unified_full_viewing_key();
|
||||
|
||||
db_data
|
||||
.conn
|
||||
.execute(
|
||||
"INSERT INTO accounts (account, ufvk) VALUES (0, ?)",
|
||||
params![ufvk.encode(&tests::network())],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
db_data.conn.execute_batch(
|
||||
"INSERT INTO accounts (account, ufvk) VALUES (0, '');
|
||||
INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');
|
||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');
|
||||
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, '');
|
||||
|
||||
INSERT INTO sent_notes (tx, output_pool, output_index, from_account, address, value)
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
//! A migration that adds an identifier for the account that received a UTXO to the utxos table
|
||||
use std::collections::HashSet;
|
||||
|
||||
use rusqlite;
|
||||
use schemer;
|
||||
use schemer_rusqlite::RusqliteMigration;
|
||||
use uuid::Uuid;
|
||||
|
||||
use zcash_primitives::consensus;
|
||||
|
||||
use super::{addresses_table, utxos_table};
|
||||
use crate::wallet::init::WalletMigrationError;
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
crate::{error::SqliteClientError, wallet::get_transparent_receivers},
|
||||
rusqlite::named_params,
|
||||
zcash_client_backend::encoding::AddressCodec,
|
||||
zcash_primitives::zip32::AccountId,
|
||||
};
|
||||
|
||||
/// This migration adds an account identifier column to the UTXOs table.
|
||||
///
|
||||
/// 761884d6-30d8-44ef-b204-0b82551c4ca1
|
||||
pub(super) const MIGRATION_ID: Uuid = Uuid::from_fields(
|
||||
0x761884d6,
|
||||
0x30d8,
|
||||
0x44ef,
|
||||
b"\xb2\x04\x0b\x82\x55\x1c\x4c\xa1",
|
||||
);
|
||||
|
||||
pub(super) struct Migration<P> {
|
||||
pub(super) _params: P,
|
||||
}
|
||||
|
||||
impl<P> schemer::Migration for Migration<P> {
|
||||
fn id(&self) -> Uuid {
|
||||
MIGRATION_ID
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> HashSet<Uuid> {
|
||||
[utxos_table::MIGRATION_ID, addresses_table::MIGRATION_ID]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
"Adds an identifier for the account that received a UTXO to the utxos table"
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
|
||||
type Error = WalletMigrationError;
|
||||
|
||||
fn up(&self, transaction: &rusqlite::Transaction) -> Result<(), WalletMigrationError> {
|
||||
transaction.execute_batch("ALTER TABLE utxos ADD COLUMN received_by_account INTEGER;")?;
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
{
|
||||
let mut stmt_update_utxo_account = transaction.prepare(
|
||||
"UPDATE utxos SET received_by_account = :account WHERE address = :address",
|
||||
)?;
|
||||
|
||||
let mut stmt_fetch_accounts = transaction.prepare("SELECT account FROM accounts")?;
|
||||
|
||||
let mut rows = stmt_fetch_accounts.query([])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let account: u32 = row.get(0)?;
|
||||
let taddrs =
|
||||
get_transparent_receivers(&self._params, transaction, AccountId::from(account))
|
||||
.map_err(|e| match e {
|
||||
SqliteClientError::DbError(e) => WalletMigrationError::DbError(e),
|
||||
SqliteClientError::CorruptedData(s) => {
|
||||
WalletMigrationError::CorruptedData(s)
|
||||
}
|
||||
other => WalletMigrationError::CorruptedData(format!(
|
||||
"Unexpected error in migration: {}",
|
||||
other
|
||||
)),
|
||||
})?;
|
||||
|
||||
for taddr in taddrs {
|
||||
stmt_update_utxo_account.execute(named_params![
|
||||
":account": &account,
|
||||
":address": &taddr.encode(&self._params),
|
||||
])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transaction.execute_batch(
|
||||
"CREATE TABLE utxos_new (
|
||||
id_utxo INTEGER PRIMARY KEY,
|
||||
received_by_account INTEGER NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
prevout_txid BLOB NOT NULL,
|
||||
prevout_idx INTEGER NOT NULL,
|
||||
script BLOB NOT NULL,
|
||||
value_zat INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL,
|
||||
spent_in_tx INTEGER,
|
||||
FOREIGN KEY (received_by_account) REFERENCES accounts(account),
|
||||
FOREIGN KEY (spent_in_tx) REFERENCES transactions(id_tx),
|
||||
CONSTRAINT tx_outpoint UNIQUE (prevout_txid, prevout_idx)
|
||||
);
|
||||
INSERT INTO utxos_new (
|
||||
id_utxo, received_by_account, address,
|
||||
prevout_txid, prevout_idx, script, value_zat,
|
||||
height, spent_in_tx)
|
||||
SELECT
|
||||
id_utxo, received_by_account, address,
|
||||
prevout_txid, prevout_idx, script, value_zat,
|
||||
height, spent_in_tx
|
||||
FROM utxos;",
|
||||
)?;
|
||||
|
||||
transaction.execute_batch(
|
||||
"DROP TABLE utxos;
|
||||
ALTER TABLE utxos_new RENAME TO utxos;",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn down(&self, _transaction: &rusqlite::Transaction) -> Result<(), WalletMigrationError> {
|
||||
// TODO: something better than just panic?
|
||||
panic!("Cannot revert this migration.");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue