From 167bcd86cea9b05839e3f237d1365822939d21b8 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 6 Sep 2022 16:41:50 -0600 Subject: [PATCH] Update migration to handle the raw-tx-absent case. The raw serialized transaction data for a transaction is not always guaranteed to be present, and we cannot correctly calculate the fee paid by a transaction if we don't have the raw data. For such rows that contain only transaction metadata, the fee information will be added at the same time the raw transaction data is added. --- .../init/migrations/add_transaction_views.rs | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs index 03bcee7b6..bd65b607e 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs @@ -1,5 +1,5 @@ //! Functions for initializing the various databases. -use rusqlite::{self, types::ToSql, NO_PARAMS}; +use rusqlite::{self, types::ToSql, OptionalExtension, NO_PARAMS}; use schemer::{self}; use schemer_rusqlite::RusqliteMigration; @@ -56,30 +56,50 @@ impl RusqliteMigration for Migration

{ let mut tx_rows = stmt_list_txs.query(NO_PARAMS)?; while let Some(row) = tx_rows.next()? { let id_tx: i64 = row.get(0)?; - let tx_bytes: Vec = row.get(1)?; + let tx_bytes: Option> = row.get(1)?; let h: u32 = row.get(2)?; let block_height = BlockHeight::from(h); - let tx = Transaction::read( - &tx_bytes[..], - BranchId::for_height(&self.params, block_height), - ) - .map_err(|e| { - WalletMigrationError::CorruptedData(format!( - "Parsing failed for transaction {:?}: {:?}", - id_tx, e - )) - })?; + // If only transaction metadata has been stored, and not transaction data, the fee + // information will eventually be set when the full transaction data is inserted. + if let Some(b) = tx_bytes { + let tx = + Transaction::read(&b[..], BranchId::for_height(&self.params, block_height)) + .map_err(|e| { + WalletMigrationError::CorruptedData(format!( + "Parsing failed for transaction {:?}: {:?}", + id_tx, e + )) + })?; - let fee_paid = tx.fee_paid(|op| { - stmt_find_utxo_value - .query_row(&[op.hash().to_sql()?, op.n().to_sql()?], |row| { - row.get(0).map(|i| Amount::from_i64(i).unwrap()) - }) - .map_err(WalletMigrationError::DbError) - })?; + let fee_paid = tx.fee_paid(|op| { + let op_amount = stmt_find_utxo_value + .query_row(&[op.hash().to_sql()?, op.n().to_sql()?], |row| { + row.get::<_, i64>(0) + }) + .optional() + .map_err(WalletMigrationError::DbError)?; - stmt_set_fee.execute(&[i64::from(fee_paid), id_tx])?; + op_amount.map_or_else( + || { + Err(WalletMigrationError::CorruptedData(format!( + "Unable to find UTXO corresponding to outpoint {:?}", + op + ))) + }, + |i| { + Amount::from_i64(i).map_err(|_| { + WalletMigrationError::CorruptedData(format!( + "UTXO amount out of range in outpoint {:?}", + op + )) + }) + }, + ) + })?; + + stmt_set_fee.execute(&[i64::from(fee_paid), id_tx])?; + } } transaction.execute_batch(