Skip to content

Commit

Permalink
fix review notes
Browse files Browse the repository at this point in the history
  • Loading branch information
borngraced committed Nov 1, 2023
1 parent b381253 commit 04e7b51
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 176 deletions.
1 change: 0 additions & 1 deletion zcash_client_backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
44 changes: 2 additions & 42 deletions zcash_client_backend/src/data_api/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<N, E, P, D>(
params: &P,
data: &mut D,
tx: &Transaction,
) -> Result<(), E>
where
E: From<Error<N>>,
P: consensus::Parameters,
D: WalletWrite<Error = E>,
{
// 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.
///
Expand Down
3 changes: 1 addition & 2 deletions zcash_client_sqlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@ 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"
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" }
Expand Down
147 changes: 16 additions & 131 deletions zcash_client_sqlite/src/for_async/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -225,44 +226,6 @@ pub struct DataConnStmtCacheAsync<P> {
wallet_db: WalletDbAsync<P>,
}

impl<P: consensus::Parameters> DataConnStmtCacheAsync<P> {
fn transactionally<F, A>(&mut self, f: F) -> Result<A, SqliteClientError>
where
F: FnOnce(&mut Self) -> Result<A, SqliteClientError>,
{
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<P: consensus::Parameters + Send + Sync + 'static> WalletRead for DataConnStmtCacheAsync<P> {
type Error = SqliteClientError;
Expand Down Expand Up @@ -367,114 +330,36 @@ impl<P: consensus::Parameters + Send + Sync + 'static> WalletWrite for DataConnS
block: &PrunedBlock,
updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, 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::TxRef, Self::Error> {
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<Self::TxRef, Self::Error> {
// 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> {
Expand Down

0 comments on commit 04e7b51

Please sign in to comment.