diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 334cedfa6e..3616019088 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -19,7 +19,6 @@ bls12_381 = "0.3.1" bs58 = { version = "0.4", features = ["check"] } base64 = "0.13" ff = "0.8" -futures = { version = "0.3", package = "futures", features = ["compat", "async-await"] } group = "0.8" hex = "0.4" jubjub = "0.5.1" diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 82a9a9a9b5..20c2a3828f 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -2,64 +2,24 @@ use std::fmt::Debug; use zcash_primitives::{ - consensus::{self, BranchId, NetworkUpgrade}, + consensus::{self, BranchId}, memo::MemoBytes, sapling::prover::TxProver, transaction::{ builder::Builder, components::{amount::DEFAULT_FEE, Amount}, - Transaction, }, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; use crate::{ address::RecipientAddress, - data_api::{error::Error, ReceivedTransaction, SentTransaction, WalletWrite}, - decrypt_transaction, + data_api::{error::Error, SentTransaction, WalletWrite}, wallet::{AccountId, OvkPolicy}, }; pub const ANCHOR_OFFSET: u32 = 10; -/// Scans a [`Transaction`] for any information that can be decrypted by the accounts in -/// the wallet, and saves it to the wallet. -pub fn decrypt_and_store_transaction( - params: &P, - data: &mut D, - tx: &Transaction, -) -> Result<(), E> -where - E: From>, - P: consensus::Parameters, - D: WalletWrite, -{ - // Fetch the ExtendedFullViewingKeys we are tracking - let extfvks = data.get_extended_full_viewing_keys()?; - - // Height is block height for mined transactions, and the "mempool height" (chain height + 1) - // for mempool transactions. - let height = data - .get_tx_height(tx.txid())? - .or(data - .block_height_extrema()? - .map(|(_, max_height)| max_height + 1)) - .or_else(|| params.activation_height(NetworkUpgrade::Sapling)) - .ok_or(Error::SaplingNotActive)?; - - let outputs = decrypt_transaction(params, height, tx, &extfvks); - if outputs.is_empty() { - Ok(()) - } else { - data.store_received_tx(&ReceivedTransaction { - tx, - outputs: &outputs, - })?; - - Ok(()) - } -} - #[allow(clippy::needless_doctest_main)] /// Creates a transaction paying the specified address from the given account. /// diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 02b9f60efd..0f3951fdfb 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -17,7 +17,6 @@ async-trait = "0.1.52" bech32 = "0.9.1" bs58 = { version = "0.4", features = ["check"] } ff = "0.8" -futures = { version = "0.3" } group = "0.8" jubjub = "0.5.1" protobuf = "2.20" @@ -25,7 +24,7 @@ rand_core = "0.5.1" rusqlite = { version = "0.28", features = ["time"] } libsqlite3-sys= { version = "0.25.2", features = ["bundled"] } time = "0.3.20" -tokio = { version = "1.20", features = ["rt"] } +tokio = { version = "1.20", features = ["rt", "rt-multi-thread"] } zcash_client_backend = { version = "0.5", path = "../zcash_client_backend" } zcash_extras = { version = "0.1", path = "../zcash_extras" } zcash_primitives = { version = "0.5", path = "../zcash_primitives" } diff --git a/zcash_client_sqlite/src/for_async/mod.rs b/zcash_client_sqlite/src/for_async/mod.rs index e3488f9fd6..810ca6c244 100644 --- a/zcash_client_sqlite/src/for_async/mod.rs +++ b/zcash_client_sqlite/src/for_async/mod.rs @@ -3,6 +3,7 @@ pub mod wallet_actions; use std::collections::HashMap; use std::path::Path; +use tokio::task::block_in_place; use zcash_client_backend::data_api::{PrunedBlock, ReceivedTransaction, SentTransaction}; use zcash_client_backend::wallet::{AccountId, SpendableNote}; use zcash_extras::{WalletRead, WalletWrite}; @@ -225,44 +226,6 @@ pub struct DataConnStmtCacheAsync

{ wallet_db: WalletDbAsync

, } -impl DataConnStmtCacheAsync

{ - fn transactionally(&mut self, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - self.wallet_db - .inner - .lock() - .unwrap() - .conn - .execute("BEGIN IMMEDIATE", [])?; - match f(self) { - Ok(result) => { - self.wallet_db - .inner - .lock() - .unwrap() - .conn - .execute("COMMIT", [])?; - Ok(result) - } - Err(error) => { - match self.wallet_db.inner.lock().unwrap().conn.execute("ROLLBACK", []) { - Ok(_) => Err(error), - Err(e) => - // Panicking here is probably the right thing to do, because it - // means the database is corrupt. - panic!( - "Rollback failed with error {} while attempting to recover from error {}; database is likely corrupt.", - e, - error - ) - } - } - } - } -} - #[async_trait::async_trait] impl WalletRead for DataConnStmtCacheAsync

{ type Error = SqliteClientError; @@ -367,114 +330,36 @@ impl WalletWrite for DataConnS block: &PrunedBlock, updated_witnesses: &[(Self::NoteRef, IncrementalWitness)], ) -> Result)>, Self::Error> { - // database updates for each block are transactional - self.transactionally(|up| { - let db = up.wallet_db.inner.lock().unwrap(); - // Insert the block into the database. - wallet_actions::insert_block( - &db, - block.block_height, - block.block_hash, - block.block_time, - block.commitment_tree, - )?; - - let mut new_witnesses = vec![]; - for tx in block.transactions { - let tx_row = wallet_actions::put_tx_meta(&db, tx, block.block_height)?; - - // Mark notes as spent and remove them from the scanning cache - for spend in &tx.shielded_spends { - wallet_actions::mark_spent(&db, tx_row, &spend.nf)?; - } - - for output in &tx.shielded_outputs { - let received_note_id = wallet_actions::put_received_note(&db, output, tx_row)?; - - // Save witness for note. - new_witnesses.push((received_note_id, output.witness.clone())); - } - } - - // Insert current new_witnesses into the database. - for (received_note_id, witness) in updated_witnesses.iter().chain(new_witnesses.iter()) - { - if let NoteId::ReceivedNoteId(rnid) = *received_note_id { - wallet_actions::insert_witness(&db, rnid, witness, block.block_height)?; - } else { - return Err(SqliteClientError::InvalidNoteId); - } - } - - // Prune the stored witnesses (we only expect rollbacks of at most 100 blocks). - let below_height = if block.block_height < BlockHeight::from(100) { - BlockHeight::from(0) - } else { - block.block_height - 100 - }; - wallet_actions::prune_witnesses(&db, below_height)?; + use zcash_client_backend::data_api::WalletWrite; - // Update now-expired transactions that didn't get mined. - wallet_actions::update_expired_notes(&db, block.block_height)?; - - Ok(new_witnesses) - }) + // database updates for each block are transactional + let db = self.wallet_db.inner.lock().unwrap(); + let mut update_ops = db.get_update_ops()?; + block_in_place(|| update_ops.advance_by_block(&block, updated_witnesses)) } async fn store_received_tx( &mut self, received_tx: &ReceivedTransaction, ) -> Result { - self.transactionally(|up| { - let db = up.wallet_db.inner.lock().unwrap(); - let tx_ref = wallet_actions::put_tx_data(&db, received_tx.tx, None)?; - - for output in received_tx.outputs { - if output.outgoing { - wallet_actions::put_sent_note(&db, output, tx_ref)?; - } else { - wallet_actions::put_received_note(&db, output, tx_ref)?; - } - } + use zcash_client_backend::data_api::WalletWrite; - Ok(tx_ref) - }) + // database updates for each block are transactional + let db = self.wallet_db.inner.lock().unwrap(); + let mut update_ops = db.get_update_ops()?; + block_in_place(|| update_ops.store_received_tx(&received_tx)) } async fn store_sent_tx( &mut self, sent_tx: &SentTransaction, ) -> Result { - // Update the database atomically, to ensure the result is internally consistent. - self.transactionally(|up| { - let db = up.wallet_db.inner.lock().unwrap(); - let tx_ref = wallet_actions::put_tx_data(&db, sent_tx.tx, Some(sent_tx.created))?; - - // Mark notes as spent. - // - // This locks the notes so they aren't selected again by a subsequent call to - // create_spend_to_address() before this transaction has been mined (at which point the notes - // get re-marked as spent). - // - // Assumes that create_spend_to_address() will never be called in parallel, which is a - // reasonable assumption for a light client such as a mobile phone. - for spend in &sent_tx.tx.shielded_spends { - wallet_actions::mark_spent(&db, tx_ref, &spend.nullifier)?; - } + use zcash_client_backend::data_api::WalletWrite; - wallet_actions::insert_sent_note( - &db, - tx_ref, - sent_tx.output_index, - sent_tx.account, - sent_tx.recipient_address, - sent_tx.value, - sent_tx.memo.as_ref(), - )?; - - // Return the row number of the transaction, so the caller can fetch it for sending. - Ok(tx_ref) - }) + // Update the database atomically, to ensure the result is internally consistent. + let db = self.wallet_db.inner.lock().unwrap(); + let mut update_ops = db.get_update_ops()?; + block_in_place(|| update_ops.store_sent_tx(&sent_tx)) } async fn rewind_to_height(&mut self, block_height: BlockHeight) -> Result<(), Self::Error> {