zcash_client_sqlite: Reprocess wallet transactions on upgrade to restore additional transparent history.

This commit is contained in:
Kris Nuttycombe 2024-08-16 15:18:12 -06:00
parent c22a2a4c92
commit 484659dddb
4 changed files with 75 additions and 6 deletions

View File

@ -87,8 +87,8 @@ use {
#[cfg(feature = "transparent-inputs")]
use {
zcash_keys::encoding::AddressCodec,
zcash_client_backend::wallet::TransparentAddressMetadata,
zcash_keys::encoding::AddressCodec,
zcash_primitives::{legacy::TransparentAddress, transaction::components::OutPoint},
};

View File

@ -53,6 +53,9 @@ pub enum WalletMigrationError {
/// Reverting the specified migration is not supported.
CannotRevert(Uuid),
/// Some other unexpected violation of database business rules occurred
Other(SqliteClientError),
}
impl From<rusqlite::Error> for WalletMigrationError {
@ -79,6 +82,21 @@ impl From<AddressGenerationError> for WalletMigrationError {
}
}
impl From<SqliteClientError> for WalletMigrationError {
fn from(value: SqliteClientError) -> Self {
match value {
SqliteClientError::CorruptedData(err) => WalletMigrationError::CorruptedData(err),
SqliteClientError::DbError(err) => WalletMigrationError::DbError(err),
SqliteClientError::CommitmentTree(err) => WalletMigrationError::CommitmentTree(err),
SqliteClientError::BalanceError(err) => WalletMigrationError::BalanceError(err),
SqliteClientError::AddressGeneration(err) => {
WalletMigrationError::AddressGeneration(err)
}
other => WalletMigrationError::Other(other),
}
}
}
impl fmt::Display for WalletMigrationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
@ -106,6 +124,13 @@ impl fmt::Display for WalletMigrationError {
WalletMigrationError::CannotRevert(uuid) => {
write!(f, "Reverting migration {} is not supported", uuid)
}
WalletMigrationError::Other(err) => {
write!(
f,
"Unexpected violation of database business rules: {}",
err
)
}
}
}
}
@ -117,6 +142,7 @@ impl std::error::Error for WalletMigrationError {
WalletMigrationError::BalanceError(e) => Some(e),
WalletMigrationError::CommitmentTree(e) => Some(e),
WalletMigrationError::AddressGeneration(e) => Some(e),
WalletMigrationError::Other(e) => Some(e),
_ => None,
}
}

View File

@ -125,7 +125,9 @@ pub(super) fn all_migrations<P: consensus::Parameters + 'static>(
params: params.clone(),
}),
Box::new(spend_key_available::Migration),
Box::new(tx_retrieval_queue::Migration),
Box::new(tx_retrieval_queue::Migration {
params: params.clone(),
}),
]
}

View File

@ -4,17 +4,21 @@ use rusqlite::{named_params, Transaction};
use schemer_rusqlite::RusqliteMigration;
use std::collections::HashSet;
use uuid::Uuid;
use zcash_client_backend::data_api::DecryptedTransaction;
use zcash_primitives::transaction::builder::DEFAULT_TX_EXPIRY_DELTA;
use zcash_protocol::consensus::{self, BlockHeight, BranchId};
use crate::wallet::init::WalletMigrationError;
use crate::wallet::{self, init::WalletMigrationError};
use super::utxos_to_txos;
pub(super) const MIGRATION_ID: Uuid = Uuid::from_u128(0xfec02b61_3988_4b4f_9699_98977fac9e7f);
pub(crate) struct Migration;
pub(super) struct Migration<P> {
pub(super) params: P,
}
impl schemer::Migration for Migration {
impl<P> schemer::Migration for Migration<P> {
fn id(&self) -> Uuid {
MIGRATION_ID
}
@ -28,7 +32,7 @@ impl schemer::Migration for Migration {
}
}
impl RusqliteMigration for Migration {
impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
type Error = WalletMigrationError;
fn up(&self, transaction: &Transaction) -> Result<(), WalletMigrationError> {
@ -75,6 +79,43 @@ impl RusqliteMigration for Migration {
named_params![":default_expiry_delta": DEFAULT_TX_EXPIRY_DELTA],
)?;
// Call `decrypt_and_store_transaction` for each transaction known to the wallet to
// populate the enhancement queues with any transparent history information that we don't
// already have.
let mut stmt_transactions =
transaction.prepare("SELECT raw, mined_height FROM transactions")?;
let mut rows = stmt_transactions.query([])?;
while let Some(row) = rows.next()? {
let tx_data = row.get::<_, Option<Vec<u8>>>(0)?;
let mined_height = row.get::<_, Option<u32>>(1)?.map(BlockHeight::from);
if let Some(tx_data) = tx_data {
let tx = zcash_primitives::transaction::Transaction::read(
&tx_data[..],
// We assume unmined transactions are created with the current consensus branch ID.
mined_height
.map_or(BranchId::Sapling, |h| BranchId::for_height(&self.params, h)),
)
.map_err(|_| {
WalletMigrationError::CorruptedData(
"Could not read serialized transaction data.".to_owned(),
)
})?;
wallet::store_decrypted_tx(
transaction,
&self.params,
DecryptedTransaction::new(
mined_height,
&tx,
vec![],
#[cfg(feature = "orchard")]
vec![],
),
)?;
}
}
Ok(())
}