2021-01-12 17:24:18 -08:00
|
|
|
//! Functions for querying information in the wdb database.
|
2021-03-25 21:22:45 -07:00
|
|
|
//!
|
|
|
|
//! These functions should generally not be used directly; instead,
|
|
|
|
//! their functionality is available via the [`WalletRead`] and
|
|
|
|
//! [`WalletWrite`] traits.
|
|
|
|
//!
|
|
|
|
//! [`WalletRead`]: zcash_client_backend::data_api::WalletRead
|
|
|
|
//! [`WalletWrite`]: zcash_client_backend::data_api::WalletWrite
|
2019-03-08 18:23:31 -08:00
|
|
|
|
2021-01-12 17:24:18 -08:00
|
|
|
use ff::PrimeField;
|
|
|
|
use rusqlite::{params, OptionalExtension, ToSql, NO_PARAMS};
|
2021-01-11 17:13:40 -08:00
|
|
|
use std::collections::HashMap;
|
2020-10-29 09:48:26 -07:00
|
|
|
use std::convert::TryFrom;
|
2020-08-06 13:11:25 -07:00
|
|
|
|
2020-08-05 16:01:22 -07:00
|
|
|
use zcash_primitives::{
|
2020-08-25 14:20:12 -07:00
|
|
|
block::BlockHash,
|
2021-08-12 10:21:30 -07:00
|
|
|
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
|
2020-10-29 09:48:26 -07:00
|
|
|
memo::{Memo, MemoBytes},
|
2020-08-18 12:48:59 -07:00
|
|
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
2022-06-13 19:27:55 -07:00
|
|
|
sapling::{keys::DiversifiableFullViewingKey, Node, Note, Nullifier, PaymentAddress},
|
2022-01-20 13:33:29 -08:00
|
|
|
transaction::{components::Amount, Transaction, TxId},
|
2022-01-24 17:02:25 -08:00
|
|
|
zip32::{AccountId, ExtendedFullViewingKey},
|
2020-08-05 16:01:22 -07:00
|
|
|
};
|
2019-03-08 18:23:31 -08:00
|
|
|
|
2020-08-05 16:01:22 -07:00
|
|
|
use zcash_client_backend::{
|
2022-06-13 10:54:32 -07:00
|
|
|
address::RecipientAddress,
|
2021-03-09 19:55:44 -08:00
|
|
|
data_api::error::Error,
|
2022-06-13 10:54:32 -07:00
|
|
|
encoding::{encode_payment_address_p, encode_transparent_address_p},
|
|
|
|
keys::UnifiedFullViewingKey,
|
2022-01-24 17:02:25 -08:00
|
|
|
wallet::{WalletShieldedOutput, WalletTx},
|
2021-01-12 20:10:34 -08:00
|
|
|
DecryptedOutput,
|
2020-08-05 16:01:22 -07:00
|
|
|
};
|
2020-08-05 18:14:45 -07:00
|
|
|
|
2021-04-13 10:02:35 -07:00
|
|
|
use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb, PRUNING_HEIGHT};
|
2021-02-12 13:08:31 -08:00
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
use zcash_primitives::legacy::TransparentAddress;
|
2021-10-04 13:09:02 -07:00
|
|
|
|
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2021-02-12 13:08:31 -08:00
|
|
|
use {
|
|
|
|
crate::UtxoId,
|
2022-01-20 13:33:29 -08:00
|
|
|
zcash_client_backend::{encoding::AddressCodec, wallet::WalletTransparentOutput},
|
|
|
|
zcash_primitives::{
|
|
|
|
legacy::Script,
|
|
|
|
transaction::components::{OutPoint, TxOut},
|
|
|
|
},
|
2021-02-12 13:08:31 -08:00
|
|
|
};
|
2019-03-08 18:23:31 -08:00
|
|
|
|
2020-08-25 14:29:01 -07:00
|
|
|
pub mod init;
|
2020-08-25 14:43:28 -07:00
|
|
|
pub mod transact;
|
2020-08-25 14:29:01 -07:00
|
|
|
|
2022-02-02 10:57:34 -08:00
|
|
|
enum PoolType {
|
|
|
|
Transparent,
|
|
|
|
Sapling,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PoolType {
|
|
|
|
fn typecode(&self) -> i64 {
|
|
|
|
// These constants are *incidentally* shared with the typecodes
|
|
|
|
// for unified addresses, but this is exclusively an internal
|
|
|
|
// implementation detail.
|
|
|
|
match self {
|
|
|
|
PoolType::Transparent => 0i64,
|
|
|
|
PoolType::Sapling => 2i64,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// This trait provides a generalization over shielded output representations.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(note = "This trait will be removed in a future release.")]
|
2021-03-09 19:55:44 -08:00
|
|
|
pub trait ShieldedOutput {
|
|
|
|
fn index(&self) -> usize;
|
|
|
|
fn account(&self) -> AccountId;
|
|
|
|
fn to(&self) -> &PaymentAddress;
|
|
|
|
fn note(&self) -> &Note;
|
2020-10-29 09:48:26 -07:00
|
|
|
fn memo(&self) -> Option<&MemoBytes>;
|
2021-03-09 19:55:44 -08:00
|
|
|
fn is_change(&self) -> Option<bool>;
|
|
|
|
fn nullifier(&self) -> Option<Nullifier>;
|
|
|
|
}
|
|
|
|
|
2022-02-02 09:29:19 -08:00
|
|
|
#[allow(deprecated)]
|
2021-03-09 19:55:44 -08:00
|
|
|
impl ShieldedOutput for WalletShieldedOutput<Nullifier> {
|
|
|
|
fn index(&self) -> usize {
|
|
|
|
self.index
|
|
|
|
}
|
|
|
|
fn account(&self) -> AccountId {
|
|
|
|
self.account
|
|
|
|
}
|
|
|
|
fn to(&self) -> &PaymentAddress {
|
|
|
|
&self.to
|
|
|
|
}
|
|
|
|
fn note(&self) -> &Note {
|
|
|
|
&self.note
|
|
|
|
}
|
2020-10-29 09:48:26 -07:00
|
|
|
fn memo(&self) -> Option<&MemoBytes> {
|
2021-03-09 19:55:44 -08:00
|
|
|
None
|
|
|
|
}
|
|
|
|
fn is_change(&self) -> Option<bool> {
|
|
|
|
Some(self.is_change)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn nullifier(&self) -> Option<Nullifier> {
|
|
|
|
Some(self.nf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 09:29:19 -08:00
|
|
|
#[allow(deprecated)]
|
2021-03-09 19:55:44 -08:00
|
|
|
impl ShieldedOutput for DecryptedOutput {
|
|
|
|
fn index(&self) -> usize {
|
|
|
|
self.index
|
|
|
|
}
|
|
|
|
fn account(&self) -> AccountId {
|
|
|
|
self.account
|
|
|
|
}
|
|
|
|
fn to(&self) -> &PaymentAddress {
|
|
|
|
&self.to
|
|
|
|
}
|
|
|
|
fn note(&self) -> &Note {
|
|
|
|
&self.note
|
|
|
|
}
|
2020-10-29 09:48:26 -07:00
|
|
|
fn memo(&self) -> Option<&MemoBytes> {
|
2021-03-09 19:55:44 -08:00
|
|
|
Some(&self.memo)
|
|
|
|
}
|
|
|
|
fn is_change(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
fn nullifier(&self) -> Option<Nullifier> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 18:23:31 -08:00
|
|
|
/// Returns the address for the account.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use tempfile::NamedTempFile;
|
2020-08-05 16:01:22 -07:00
|
|
|
/// use zcash_primitives::{
|
|
|
|
/// consensus::{self, Network},
|
2022-01-25 08:21:42 -08:00
|
|
|
/// zip32::AccountId,
|
2020-08-05 16:01:22 -07:00
|
|
|
/// };
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2020-09-11 15:17:43 -07:00
|
|
|
/// wallet::get_address,
|
2020-08-05 18:14:45 -07:00
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2022-02-10 08:47:42 -08:00
|
|
|
/// let addr = get_address(&db, AccountId::from(0));
|
2019-03-08 18:23:31 -08:00
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_address instead."
|
|
|
|
)]
|
2020-08-05 16:01:22 -07:00
|
|
|
pub fn get_address<P: consensus::Parameters>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-06 13:11:25 -07:00
|
|
|
account: AccountId,
|
2020-08-05 16:01:22 -07:00
|
|
|
) -> Result<Option<PaymentAddress>, SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
let addr: String = wdb.conn.query_row(
|
2019-03-08 18:23:31 -08:00
|
|
|
"SELECT address FROM accounts
|
|
|
|
WHERE account = ?",
|
2022-02-10 08:47:42 -08:00
|
|
|
&[u32::from(account)],
|
2019-03-08 18:23:31 -08:00
|
|
|
|row| row.get(0),
|
|
|
|
)?;
|
|
|
|
|
2022-06-13 10:54:32 -07:00
|
|
|
RecipientAddress::decode(&wdb.params, &addr)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
SqliteClientError::CorruptedData("Not a valid Zcash recipient address".to_owned())
|
|
|
|
})
|
|
|
|
.map(|addr| match addr {
|
|
|
|
// TODO: Return the UA, not its Sapling component.
|
|
|
|
RecipientAddress::Unified(ua) => ua.sapling().cloned(),
|
|
|
|
_ => None,
|
|
|
|
})
|
2019-03-08 18:23:31 -08:00
|
|
|
}
|
2019-03-08 18:53:38 -08:00
|
|
|
|
2022-06-13 17:57:20 -07:00
|
|
|
/// Returns the [`UnifiedFullViewingKey`]s for the wallet.
|
|
|
|
pub(crate) fn get_unified_full_viewing_keys<P: consensus::Parameters>(
|
|
|
|
wdb: &WalletDb<P>,
|
|
|
|
) -> Result<HashMap<AccountId, UnifiedFullViewingKey>, SqliteClientError> {
|
|
|
|
// Fetch the UnifiedFullViewingKeys we are tracking
|
2021-01-12 17:24:18 -08:00
|
|
|
let mut stmt_fetch_accounts = wdb
|
|
|
|
.conn
|
2022-06-13 10:54:32 -07:00
|
|
|
.prepare("SELECT account, ufvk FROM accounts ORDER BY account ASC")?;
|
2020-08-26 14:47:47 -07:00
|
|
|
|
|
|
|
let rows = stmt_fetch_accounts
|
|
|
|
.query_map(NO_PARAMS, |row| {
|
2022-02-10 08:47:42 -08:00
|
|
|
let acct: u32 = row.get(0)?;
|
2022-06-13 10:54:32 -07:00
|
|
|
let account = AccountId::from(acct);
|
|
|
|
let ufvk_str: String = row.get(1)?;
|
2022-06-13 18:56:46 -07:00
|
|
|
let ufvk = UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str)
|
2022-06-13 10:54:32 -07:00
|
|
|
.map_err(SqliteClientError::CorruptedData);
|
|
|
|
|
2022-06-13 17:57:20 -07:00
|
|
|
Ok((account, ufvk))
|
2020-08-26 14:47:47 -07:00
|
|
|
})
|
|
|
|
.map_err(SqliteClientError::from)?;
|
|
|
|
|
2022-06-13 17:57:20 -07:00
|
|
|
let mut res: HashMap<AccountId, UnifiedFullViewingKey> = HashMap::new();
|
2021-01-11 17:13:40 -08:00
|
|
|
for row in rows {
|
2022-06-13 17:57:20 -07:00
|
|
|
let (account_id, ufvkr) = row?;
|
|
|
|
res.insert(account_id, ufvkr?);
|
2021-01-11 17:13:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(res)
|
2020-08-26 14:47:47 -07:00
|
|
|
}
|
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// Checks whether the specified [`ExtendedFullViewingKey`] is valid and corresponds to the
|
|
|
|
/// specified account.
|
|
|
|
///
|
|
|
|
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::is_valid_account_extfvk instead."
|
|
|
|
)]
|
2020-08-26 14:47:47 -07:00
|
|
|
pub fn is_valid_account_extfvk<P: consensus::Parameters>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-26 14:47:47 -07:00
|
|
|
account: AccountId,
|
|
|
|
extfvk: &ExtendedFullViewingKey,
|
|
|
|
) -> Result<bool, SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
wdb.conn
|
2022-06-13 10:54:32 -07:00
|
|
|
.prepare("SELECT ufvk FROM accounts WHERE account = ?")?
|
|
|
|
.query_row(&[u32::from(account).to_sql()?], |row| {
|
|
|
|
row.get(0).map(|ufvk_str: String| {
|
2022-06-13 18:56:46 -07:00
|
|
|
UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str)
|
2022-06-13 10:54:32 -07:00
|
|
|
.map_err(SqliteClientError::CorruptedData)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.optional()
|
2020-08-26 14:47:47 -07:00
|
|
|
.map_err(SqliteClientError::from)
|
2022-06-13 10:54:32 -07:00
|
|
|
.and_then(|row| {
|
|
|
|
if let Some(ufvk) = row {
|
2022-06-13 19:27:55 -07:00
|
|
|
ufvk.map(|ufvk| {
|
|
|
|
ufvk.sapling().map(|dfvk| dfvk.to_bytes())
|
|
|
|
== Some(DiversifiableFullViewingKey::from(extfvk.clone()).to_bytes())
|
|
|
|
})
|
2022-06-13 10:54:32 -07:00
|
|
|
} else {
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
})
|
2020-08-26 14:47:47 -07:00
|
|
|
}
|
|
|
|
|
2019-03-08 18:53:38 -08:00
|
|
|
/// Returns the balance for the account, including all mined unspent notes that we know
|
|
|
|
/// about.
|
|
|
|
///
|
2020-07-09 14:49:30 -07:00
|
|
|
/// WARNING: This balance is potentially unreliable, as mined notes may become unmined due
|
|
|
|
/// to chain reorgs. You should generally not show this balance to users without some
|
2021-01-15 11:00:14 -08:00
|
|
|
/// caveat. Use [`get_balance_at`] where you need a more reliable indication of the
|
2020-07-09 14:49:30 -07:00
|
|
|
/// wallet balance.
|
|
|
|
///
|
2019-03-08 18:53:38 -08:00
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use tempfile::NamedTempFile;
|
2022-01-25 08:21:42 -08:00
|
|
|
/// use zcash_primitives::{
|
|
|
|
/// consensus::Network,
|
|
|
|
/// zip32::AccountId,
|
|
|
|
/// };
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2020-09-11 15:17:43 -07:00
|
|
|
/// wallet::get_balance,
|
2020-08-05 18:14:45 -07:00
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2022-02-10 08:47:42 -08:00
|
|
|
/// let addr = get_balance(&db, AccountId::from(0));
|
2019-03-08 18:53:38 -08:00
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_balance_at instead."
|
|
|
|
)]
|
2021-03-26 22:17:54 -07:00
|
|
|
pub fn get_balance<P>(wdb: &WalletDb<P>, account: AccountId) -> Result<Amount, SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
let balance = wdb.conn.query_row(
|
2019-03-08 18:53:38 -08:00
|
|
|
"SELECT SUM(value) FROM received_notes
|
|
|
|
INNER JOIN transactions ON transactions.id_tx = received_notes.tx
|
|
|
|
WHERE account = ? AND spent IS NULL AND transactions.block IS NOT NULL",
|
2022-02-10 08:47:42 -08:00
|
|
|
&[u32::from(account)],
|
2019-03-08 18:53:38 -08:00
|
|
|
|row| row.get(0).or(Ok(0)),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
match Amount::from_i64(balance) {
|
|
|
|
Ok(amount) if !amount.is_negative() => Ok(amount),
|
2021-01-12 17:24:18 -08:00
|
|
|
_ => Err(SqliteClientError::CorruptedData(
|
2020-10-19 15:52:48 -07:00
|
|
|
"Sum of values in received_notes is out of range".to_string(),
|
2021-01-12 17:24:18 -08:00
|
|
|
)),
|
2019-03-08 18:53:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-11 17:13:40 -08:00
|
|
|
/// Returns the verified balance for the account at the specified height,
|
2021-01-08 11:49:10 -08:00
|
|
|
/// This may be used to obtain a balance that ignores notes that have been
|
|
|
|
/// received so recently that they are not yet deemed spendable.
|
2019-03-08 18:53:38 -08:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use tempfile::NamedTempFile;
|
2022-01-25 08:21:42 -08:00
|
|
|
/// use zcash_primitives::{
|
|
|
|
/// consensus::{BlockHeight, Network},
|
|
|
|
/// zip32::AccountId,
|
|
|
|
/// };
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2021-01-15 11:00:14 -08:00
|
|
|
/// wallet::get_balance_at,
|
2020-08-05 18:14:45 -07:00
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2022-02-10 08:47:42 -08:00
|
|
|
/// let addr = get_balance_at(&db, AccountId::from(0), BlockHeight::from_u32(0));
|
2019-03-08 18:53:38 -08:00
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_balance_at instead."
|
|
|
|
)]
|
2021-01-15 11:00:14 -08:00
|
|
|
pub fn get_balance_at<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-06 13:11:25 -07:00
|
|
|
account: AccountId,
|
2020-08-26 14:47:47 -07:00
|
|
|
anchor_height: BlockHeight,
|
2020-08-05 18:14:45 -07:00
|
|
|
) -> Result<Amount, SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
let balance = wdb.conn.query_row(
|
2019-03-08 18:53:38 -08:00
|
|
|
"SELECT SUM(value) FROM received_notes
|
|
|
|
INNER JOIN transactions ON transactions.id_tx = received_notes.tx
|
|
|
|
WHERE account = ? AND spent IS NULL AND transactions.block <= ?",
|
2022-02-10 08:47:42 -08:00
|
|
|
&[u32::from(account), u32::from(anchor_height)],
|
2019-03-08 18:53:38 -08:00
|
|
|
|row| row.get(0).or(Ok(0)),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
match Amount::from_i64(balance) {
|
|
|
|
Ok(amount) if !amount.is_negative() => Ok(amount),
|
2021-01-12 17:24:18 -08:00
|
|
|
_ => Err(SqliteClientError::CorruptedData(
|
2020-10-19 15:52:48 -07:00
|
|
|
"Sum of values in received_notes is out of range".to_string(),
|
2021-01-12 17:24:18 -08:00
|
|
|
)),
|
2019-03-08 18:53:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 08:21:10 -07:00
|
|
|
/// Returns the memo for a received note.
|
2019-03-08 19:22:35 -08:00
|
|
|
///
|
2021-01-12 17:24:18 -08:00
|
|
|
/// The note is identified by its row index in the `received_notes` table within the wdb
|
2019-03-08 19:22:35 -08:00
|
|
|
/// database.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use tempfile::NamedTempFile;
|
2021-01-12 17:24:18 -08:00
|
|
|
/// use zcash_primitives::consensus::Network;
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use zcash_client_sqlite::{
|
2020-08-06 13:11:25 -07:00
|
|
|
/// NoteId,
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2020-10-29 09:48:26 -07:00
|
|
|
/// wallet::get_received_memo,
|
2020-08-05 18:14:45 -07:00
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2020-10-29 09:48:26 -07:00
|
|
|
/// let memo = get_received_memo(&db, 27);
|
2020-08-05 18:14:45 -07:00
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_memo instead."
|
|
|
|
)]
|
2021-03-26 22:17:54 -07:00
|
|
|
pub fn get_received_memo<P>(wdb: &WalletDb<P>, id_note: i64) -> Result<Memo, SqliteClientError> {
|
2020-10-29 09:48:26 -07:00
|
|
|
let memo_bytes: Vec<_> = wdb.conn.query_row(
|
2019-03-08 19:22:35 -08:00
|
|
|
"SELECT memo FROM received_notes
|
|
|
|
WHERE id_note = ?",
|
2021-01-19 11:27:48 -08:00
|
|
|
&[id_note],
|
2019-03-08 19:22:35 -08:00
|
|
|
|row| row.get(0),
|
|
|
|
)?;
|
|
|
|
|
2020-10-29 09:48:26 -07:00
|
|
|
MemoBytes::from_bytes(&memo_bytes)
|
|
|
|
.and_then(Memo::try_from)
|
|
|
|
.map_err(SqliteClientError::from)
|
2019-03-08 19:22:35 -08:00
|
|
|
}
|
|
|
|
|
2022-02-02 09:29:19 -08:00
|
|
|
/// Looks up a transaction by its internal database identifier.
|
|
|
|
pub(crate) fn get_transaction<P: Parameters>(
|
2021-08-12 10:21:30 -07:00
|
|
|
wdb: &WalletDb<P>,
|
|
|
|
id_tx: i64,
|
|
|
|
) -> Result<Transaction, SqliteClientError> {
|
|
|
|
let (tx_bytes, block_height): (Vec<_>, BlockHeight) = wdb.conn.query_row(
|
|
|
|
"SELECT raw, block FROM transactions
|
2021-04-13 10:02:35 -07:00
|
|
|
WHERE id_tx = ?",
|
|
|
|
&[id_tx],
|
2021-08-12 10:21:30 -07:00
|
|
|
|row| {
|
|
|
|
let h: u32 = row.get(1)?;
|
|
|
|
Ok((row.get(0)?, BlockHeight::from(h)))
|
|
|
|
},
|
2021-04-13 10:02:35 -07:00
|
|
|
)?;
|
|
|
|
|
2021-08-12 10:21:30 -07:00
|
|
|
Transaction::read(
|
|
|
|
&tx_bytes[..],
|
|
|
|
BranchId::for_height(&wdb.params, block_height),
|
|
|
|
)
|
|
|
|
.map_err(SqliteClientError::from)
|
2021-04-13 10:02:35 -07:00
|
|
|
}
|
|
|
|
|
2021-03-17 08:21:10 -07:00
|
|
|
/// Returns the memo for a sent note.
|
2019-03-08 19:22:35 -08:00
|
|
|
///
|
2021-01-12 17:24:18 -08:00
|
|
|
/// The note is identified by its row index in the `sent_notes` table within the wdb
|
2019-03-08 19:22:35 -08:00
|
|
|
/// database.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use tempfile::NamedTempFile;
|
2021-01-12 17:24:18 -08:00
|
|
|
/// use zcash_primitives::consensus::Network;
|
2020-08-05 18:14:45 -07:00
|
|
|
/// use zcash_client_sqlite::{
|
2020-08-06 13:11:25 -07:00
|
|
|
/// NoteId,
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2020-10-29 09:48:26 -07:00
|
|
|
/// wallet::get_sent_memo,
|
2020-08-05 18:14:45 -07:00
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2020-10-29 09:48:26 -07:00
|
|
|
/// let memo = get_sent_memo(&db, 12);
|
2020-08-05 18:14:45 -07:00
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_memo instead."
|
|
|
|
)]
|
2021-03-26 22:17:54 -07:00
|
|
|
pub fn get_sent_memo<P>(wdb: &WalletDb<P>, id_note: i64) -> Result<Memo, SqliteClientError> {
|
2020-10-29 09:48:26 -07:00
|
|
|
let memo_bytes: Vec<_> = wdb.conn.query_row(
|
2019-03-08 19:22:35 -08:00
|
|
|
"SELECT memo FROM sent_notes
|
|
|
|
WHERE id_note = ?",
|
2021-01-19 11:27:48 -08:00
|
|
|
&[id_note],
|
2019-03-08 19:22:35 -08:00
|
|
|
|row| row.get(0),
|
|
|
|
)?;
|
|
|
|
|
2020-10-29 09:48:26 -07:00
|
|
|
MemoBytes::from_bytes(&memo_bytes)
|
|
|
|
.and_then(Memo::try_from)
|
|
|
|
.map_err(SqliteClientError::from)
|
2019-03-08 19:22:35 -08:00
|
|
|
}
|
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// Returns the minimum and maximum heights for blocks stored in the wallet database.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use tempfile::NamedTempFile;
|
|
|
|
/// use zcash_primitives::consensus::Network;
|
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2021-03-25 21:22:45 -07:00
|
|
|
/// wallet::block_height_extrema,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2021-03-25 21:22:45 -07:00
|
|
|
/// let bounds = block_height_extrema(&db);
|
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::block_height_extrema instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn block_height_extrema<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-25 14:20:12 -07:00
|
|
|
) -> Result<Option<(BlockHeight, BlockHeight)>, rusqlite::Error> {
|
2021-01-12 17:24:18 -08:00
|
|
|
wdb.conn
|
2020-08-25 14:20:12 -07:00
|
|
|
.query_row(
|
|
|
|
"SELECT MIN(height), MAX(height) FROM blocks",
|
|
|
|
NO_PARAMS,
|
|
|
|
|row| {
|
|
|
|
let min_height: u32 = row.get(0)?;
|
|
|
|
let max_height: u32 = row.get(1)?;
|
|
|
|
Ok(Some((
|
|
|
|
BlockHeight::from(min_height),
|
|
|
|
BlockHeight::from(max_height),
|
|
|
|
)))
|
|
|
|
},
|
|
|
|
)
|
|
|
|
//.optional() doesn't work here because a failed aggregate function
|
|
|
|
//produces a runtime error, not an empty set of rows.
|
|
|
|
.or(Ok(None))
|
|
|
|
}
|
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// Returns the block height at which the specified transaction was mined,
|
|
|
|
/// if any.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use tempfile::NamedTempFile;
|
|
|
|
/// use zcash_primitives::consensus::Network;
|
|
|
|
/// use zcash_primitives::transaction::TxId;
|
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2021-03-25 21:22:45 -07:00
|
|
|
/// wallet::get_tx_height,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2021-05-11 09:54:39 -07:00
|
|
|
/// let height = get_tx_height(&db, TxId::from_bytes([0u8; 32]));
|
2021-03-25 21:22:45 -07:00
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_tx_height instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn get_tx_height<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2021-01-12 17:24:18 -08:00
|
|
|
txid: TxId,
|
|
|
|
) -> Result<Option<BlockHeight>, rusqlite::Error> {
|
|
|
|
wdb.conn
|
2020-08-25 14:20:12 -07:00
|
|
|
.query_row(
|
|
|
|
"SELECT block FROM transactions WHERE txid = ?",
|
2021-05-11 09:54:39 -07:00
|
|
|
&[txid.as_ref().to_vec()],
|
2020-08-25 14:20:12 -07:00
|
|
|
|row| row.get(0).map(u32::into),
|
|
|
|
)
|
|
|
|
.optional()
|
|
|
|
}
|
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// Returns the block hash for the block at the specified height,
|
|
|
|
/// if any.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use tempfile::NamedTempFile;
|
|
|
|
/// use zcash_primitives::consensus::{H0, Network};
|
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2021-03-25 21:22:45 -07:00
|
|
|
/// wallet::get_block_hash,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2021-03-25 21:22:45 -07:00
|
|
|
/// let hash = get_block_hash(&db, H0);
|
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_block_hash instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn get_block_hash<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-25 14:20:12 -07:00
|
|
|
block_height: BlockHeight,
|
|
|
|
) -> Result<Option<BlockHash>, rusqlite::Error> {
|
2021-01-12 17:24:18 -08:00
|
|
|
wdb.conn
|
2020-08-25 14:20:12 -07:00
|
|
|
.query_row(
|
|
|
|
"SELECT hash FROM blocks WHERE height = ?",
|
|
|
|
&[u32::from(block_height)],
|
|
|
|
|row| {
|
|
|
|
let row_data = row.get::<_, Vec<_>>(0)?;
|
|
|
|
Ok(BlockHash::from_slice(&row_data))
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.optional()
|
|
|
|
}
|
|
|
|
|
2021-04-13 10:02:35 -07:00
|
|
|
/// Gets the height to which the database must be rewound if any rewind greater than the pruning
|
|
|
|
/// height is attempted.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(note = "This function will be removed in a future release.")]
|
2021-04-13 10:02:35 -07:00
|
|
|
pub fn get_rewind_height<P>(wdb: &WalletDb<P>) -> Result<Option<BlockHeight>, SqliteClientError> {
|
|
|
|
wdb.conn
|
|
|
|
.query_row(
|
|
|
|
"SELECT MIN(tx.block)
|
|
|
|
FROM received_notes n
|
|
|
|
JOIN transactions tx ON tx.id_tx = n.tx
|
|
|
|
WHERE n.spent IS NULL",
|
|
|
|
NO_PARAMS,
|
2021-04-22 13:26:57 -07:00
|
|
|
|row| {
|
|
|
|
row.get(0)
|
|
|
|
.map(|maybe_height: Option<u32>| maybe_height.map(|height| height.into()))
|
|
|
|
},
|
2021-04-13 10:02:35 -07:00
|
|
|
)
|
|
|
|
.map_err(SqliteClientError::from)
|
|
|
|
}
|
|
|
|
|
2020-08-25 14:20:12 -07:00
|
|
|
/// Rewinds the database to the given height.
|
|
|
|
///
|
|
|
|
/// If the requested height is greater than or equal to the height of the last scanned
|
|
|
|
/// block, this function does nothing.
|
2020-12-02 13:36:56 -08:00
|
|
|
///
|
|
|
|
/// This should only be executed inside a transactional context.
|
2022-02-02 09:29:19 -08:00
|
|
|
pub(crate) fn rewind_to_height<P: consensus::Parameters>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-25 14:20:12 -07:00
|
|
|
block_height: BlockHeight,
|
2021-01-13 14:20:11 -08:00
|
|
|
) -> Result<(), SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
let sapling_activation_height = wdb
|
|
|
|
.params
|
2020-08-25 14:20:12 -07:00
|
|
|
.activation_height(NetworkUpgrade::Sapling)
|
2021-01-13 14:20:11 -08:00
|
|
|
.ok_or(SqliteClientError::BackendError(Error::SaplingNotActive))?;
|
2020-08-25 14:20:12 -07:00
|
|
|
|
|
|
|
// Recall where we synced up to previously.
|
2021-01-13 17:06:42 -08:00
|
|
|
let last_scanned_height =
|
|
|
|
wdb.conn
|
|
|
|
.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
|
|
|
|
row.get(0)
|
|
|
|
.map(|h: u32| h.into())
|
2022-02-01 13:02:07 -08:00
|
|
|
.or_else(|_| Ok(sapling_activation_height - 1))
|
2021-01-13 17:06:42 -08:00
|
|
|
})?;
|
2020-08-25 14:20:12 -07:00
|
|
|
|
2022-01-31 15:36:09 -08:00
|
|
|
if block_height < last_scanned_height - PRUNING_HEIGHT {
|
2022-02-02 09:29:19 -08:00
|
|
|
#[allow(deprecated)]
|
2022-02-01 13:02:07 -08:00
|
|
|
if let Some(h) = get_rewind_height(wdb)? {
|
2022-01-31 15:36:09 -08:00
|
|
|
if block_height > h {
|
|
|
|
return Err(SqliteClientError::RequestedRewindInvalid(h, block_height));
|
2021-04-13 10:02:35 -07:00
|
|
|
}
|
|
|
|
}
|
2022-01-31 15:36:09 -08:00
|
|
|
}
|
2020-08-25 14:20:12 -07:00
|
|
|
|
2021-04-13 10:02:35 -07:00
|
|
|
// nothing to do if we're deleting back down to the max height
|
2022-01-31 15:36:09 -08:00
|
|
|
if block_height < last_scanned_height {
|
|
|
|
// Decrement witnesses.
|
|
|
|
wdb.conn.execute(
|
|
|
|
"DELETE FROM sapling_witnesses WHERE block > ?",
|
|
|
|
&[u32::from(block_height)],
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Rewind received notes
|
|
|
|
wdb.conn.execute(
|
|
|
|
"DELETE FROM received_notes
|
|
|
|
WHERE id_note IN (
|
|
|
|
SELECT rn.id_note
|
|
|
|
FROM received_notes rn
|
|
|
|
LEFT OUTER JOIN transactions tx
|
|
|
|
ON tx.id_tx = rn.tx
|
2022-02-01 10:37:43 -08:00
|
|
|
WHERE tx.block IS NOT NULL AND tx.block > ?
|
2022-01-31 15:36:09 -08:00
|
|
|
);",
|
|
|
|
&[u32::from(block_height)],
|
|
|
|
)?;
|
|
|
|
|
2022-02-02 10:57:34 -08:00
|
|
|
// Do not delete sent notes; this can contain data that is not recoverable
|
|
|
|
// from the chain. Wallets must continue to operate correctly in the
|
|
|
|
// presence of stale sent notes that link to unmined transactions.
|
2020-08-25 14:20:12 -07:00
|
|
|
|
2022-01-31 15:36:09 -08:00
|
|
|
// Rewind utxos
|
|
|
|
wdb.conn.execute(
|
|
|
|
"DELETE FROM utxos WHERE height > ?",
|
|
|
|
&[u32::from(block_height)],
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Un-mine transactions.
|
|
|
|
wdb.conn.execute(
|
2022-02-01 10:37:43 -08:00
|
|
|
"UPDATE transactions SET block = NULL, tx_index = NULL WHERE block IS NOT NULL AND block > ?",
|
2022-01-31 15:36:09 -08:00
|
|
|
&[u32::from(block_height)],
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Now that they aren't depended on, delete scanned blocks.
|
|
|
|
wdb.conn.execute(
|
|
|
|
"DELETE FROM blocks WHERE height > ?",
|
|
|
|
&[u32::from(block_height)],
|
|
|
|
)?;
|
2021-01-12 17:24:18 -08:00
|
|
|
}
|
2021-04-13 10:02:35 -07:00
|
|
|
|
|
|
|
Ok(())
|
2020-08-25 14:20:12 -07:00
|
|
|
}
|
2020-08-06 13:11:25 -07:00
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// Returns the commitment tree for the block at the specified height,
|
|
|
|
/// if any.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use tempfile::NamedTempFile;
|
|
|
|
/// use zcash_primitives::consensus::{Network, H0};
|
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2021-03-25 21:22:45 -07:00
|
|
|
/// wallet::get_commitment_tree,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2021-03-25 21:22:45 -07:00
|
|
|
/// let tree = get_commitment_tree(&db, H0);
|
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_commitment_tree instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn get_commitment_tree<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-06 17:13:24 -07:00
|
|
|
block_height: BlockHeight,
|
|
|
|
) -> Result<Option<CommitmentTree<Node>>, SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
wdb.conn
|
2020-08-06 17:13:24 -07:00
|
|
|
.query_row_and_then(
|
|
|
|
"SELECT sapling_tree FROM blocks WHERE height = ?",
|
|
|
|
&[u32::from(block_height)],
|
|
|
|
|row| {
|
|
|
|
let row_data: Vec<u8> = row.get(0)?;
|
|
|
|
CommitmentTree::read(&row_data[..]).map_err(|e| {
|
|
|
|
rusqlite::Error::FromSqlConversionFailure(
|
|
|
|
row_data.len(),
|
|
|
|
rusqlite::types::Type::Blob,
|
|
|
|
Box::new(e),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.optional()
|
|
|
|
.map_err(SqliteClientError::from)
|
|
|
|
}
|
|
|
|
|
2021-03-25 21:22:45 -07:00
|
|
|
/// Returns the incremental witnesses for the block at the specified height,
|
|
|
|
/// if any.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use tempfile::NamedTempFile;
|
|
|
|
/// use zcash_primitives::consensus::{Network, H0};
|
|
|
|
/// use zcash_client_sqlite::{
|
2021-03-26 22:17:54 -07:00
|
|
|
/// WalletDb,
|
2021-03-25 21:22:45 -07:00
|
|
|
/// wallet::get_witnesses,
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
/// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap();
|
2021-03-25 21:22:45 -07:00
|
|
|
/// let witnesses = get_witnesses(&db, H0);
|
|
|
|
/// ```
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_witnesses instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn get_witnesses<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2020-08-18 12:48:59 -07:00
|
|
|
block_height: BlockHeight,
|
|
|
|
) -> Result<Vec<(NoteId, IncrementalWitness<Node>)>, SqliteClientError> {
|
2021-01-12 17:24:18 -08:00
|
|
|
let mut stmt_fetch_witnesses = wdb
|
|
|
|
.conn
|
2020-08-18 12:48:59 -07:00
|
|
|
.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?;
|
|
|
|
let witnesses = stmt_fetch_witnesses
|
|
|
|
.query_map(&[u32::from(block_height)], |row| {
|
2021-01-19 11:27:48 -08:00
|
|
|
let id_note = NoteId::ReceivedNoteId(row.get(0)?);
|
2021-01-12 17:24:18 -08:00
|
|
|
let wdb: Vec<u8> = row.get(1)?;
|
|
|
|
Ok(IncrementalWitness::read(&wdb[..]).map(|witness| (id_note, witness)))
|
2020-08-18 12:48:59 -07:00
|
|
|
})
|
|
|
|
.map_err(SqliteClientError::from)?;
|
|
|
|
|
2021-01-12 20:04:42 -08:00
|
|
|
// unwrap database error & IO error from IncrementalWitness::read
|
|
|
|
let res: Vec<_> = witnesses.collect::<Result<Result<_, _>, _>>()??;
|
2020-08-18 12:48:59 -07:00
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
2021-03-24 15:18:54 -07:00
|
|
|
/// Retrieve the nullifiers for notes that the wallet is tracking
|
|
|
|
/// that have not yet been confirmed as a consequence of the spending
|
|
|
|
/// transaction being included in a block.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_nullifiers instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn get_nullifiers<P>(
|
2021-03-26 22:17:54 -07:00
|
|
|
wdb: &WalletDb<P>,
|
2021-01-15 09:18:43 -08:00
|
|
|
) -> Result<Vec<(AccountId, Nullifier)>, SqliteClientError> {
|
2020-08-18 13:49:00 -07:00
|
|
|
// Get the nullifiers for the notes we are tracking
|
2021-03-25 17:10:46 -07:00
|
|
|
let mut stmt_fetch_nullifiers = wdb.conn.prepare(
|
|
|
|
"SELECT rn.id_note, rn.account, rn.nf, tx.block as block
|
2021-03-24 15:18:54 -07:00
|
|
|
FROM received_notes rn
|
|
|
|
LEFT OUTER JOIN transactions tx
|
|
|
|
ON tx.id_tx = rn.spent
|
2021-03-25 17:10:46 -07:00
|
|
|
WHERE block IS NULL",
|
|
|
|
)?;
|
2020-08-18 13:49:00 -07:00
|
|
|
let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| {
|
2022-02-10 08:47:42 -08:00
|
|
|
let account: u32 = row.get(1)?;
|
2021-01-15 09:18:43 -08:00
|
|
|
let nf_bytes: Vec<u8> = row.get(2)?;
|
2022-02-10 08:47:42 -08:00
|
|
|
Ok((
|
|
|
|
AccountId::from(account),
|
|
|
|
Nullifier::from_slice(&nf_bytes).unwrap(),
|
|
|
|
))
|
2020-08-18 13:49:00 -07:00
|
|
|
})?;
|
|
|
|
|
2021-01-12 20:04:42 -08:00
|
|
|
let res: Vec<_> = nullifiers.collect::<Result<_, _>>()?;
|
2020-08-18 13:49:00 -07:00
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Returns the nullifiers for the notes that this wallet is tracking.
|
2022-02-02 09:29:19 -08:00
|
|
|
pub(crate) fn get_all_nullifiers<P>(
|
2021-04-14 10:20:56 -07:00
|
|
|
wdb: &WalletDb<P>,
|
|
|
|
) -> Result<Vec<(AccountId, Nullifier)>, SqliteClientError> {
|
|
|
|
// Get the nullifiers for the notes we are tracking
|
|
|
|
let mut stmt_fetch_nullifiers = wdb.conn.prepare(
|
|
|
|
"SELECT rn.id_note, rn.account, rn.nf
|
|
|
|
FROM received_notes rn",
|
|
|
|
)?;
|
|
|
|
let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| {
|
2022-02-10 08:47:42 -08:00
|
|
|
let account: u32 = row.get(1)?;
|
2021-04-14 10:20:56 -07:00
|
|
|
let nf_bytes: Vec<u8> = row.get(2)?;
|
2022-02-10 08:47:42 -08:00
|
|
|
Ok((
|
|
|
|
AccountId::from(account),
|
|
|
|
Nullifier::from_slice(&nf_bytes).unwrap(),
|
|
|
|
))
|
2021-04-14 10:20:56 -07:00
|
|
|
})?;
|
|
|
|
|
|
|
|
let res: Vec<_> = nullifiers.collect::<Result<_, _>>()?;
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Returns unspent transparent outputs that have been received by this wallet at the given
|
|
|
|
/// transparent address, such that the block that included the transaction was mined at a
|
|
|
|
/// height less than or equal to the provided `max_height`.
|
2021-10-04 13:09:02 -07:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2022-02-02 09:29:19 -08:00
|
|
|
pub(crate) fn get_unspent_transparent_outputs<P: consensus::Parameters>(
|
2020-12-22 06:10:13 -08:00
|
|
|
wdb: &WalletDb<P>,
|
|
|
|
address: &TransparentAddress,
|
2021-09-03 16:32:40 -07:00
|
|
|
max_height: BlockHeight,
|
2020-12-22 06:10:13 -08:00
|
|
|
) -> Result<Vec<WalletTransparentOutput>, SqliteClientError> {
|
|
|
|
let mut stmt_blocks = wdb.conn.prepare(
|
2021-10-01 10:42:04 -07:00
|
|
|
"SELECT u.prevout_txid, u.prevout_idx, u.script, u.value_zat, u.height, tx.block as block
|
2021-03-26 17:39:43 -07:00
|
|
|
FROM utxos u
|
|
|
|
LEFT OUTER JOIN transactions tx
|
|
|
|
ON tx.id_tx = u.spent_in_tx
|
|
|
|
WHERE u.address = ?
|
|
|
|
AND u.height <= ?
|
|
|
|
AND block IS NULL",
|
2020-12-22 06:10:13 -08:00
|
|
|
)?;
|
|
|
|
|
|
|
|
let addr_str = address.encode(&wdb.params);
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
let rows = stmt_blocks.query_map(params![addr_str, u32::from(max_height)], |row| {
|
2021-10-01 10:42:04 -07:00
|
|
|
let id: Vec<u8> = row.get(0)?;
|
2020-12-22 06:10:13 -08:00
|
|
|
|
|
|
|
let mut txid_bytes = [0u8; 32];
|
|
|
|
txid_bytes.copy_from_slice(&id);
|
2022-02-10 08:47:42 -08:00
|
|
|
let index: u32 = row.get(1)?;
|
2021-10-01 10:42:04 -07:00
|
|
|
let script_pubkey = Script(row.get(2)?);
|
|
|
|
let value = Amount::from_i64(row.get(3)?).unwrap();
|
|
|
|
let height: u32 = row.get(4)?;
|
2020-12-22 06:10:13 -08:00
|
|
|
|
|
|
|
Ok(WalletTransparentOutput {
|
2022-02-10 08:47:42 -08:00
|
|
|
outpoint: OutPoint::new(txid_bytes, index),
|
2021-10-01 10:42:04 -07:00
|
|
|
txout: TxOut {
|
|
|
|
value,
|
|
|
|
script_pubkey,
|
|
|
|
},
|
2020-12-22 06:10:13 -08:00
|
|
|
height: BlockHeight::from(height),
|
|
|
|
})
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let mut utxos = Vec::<WalletTransparentOutput>::new();
|
|
|
|
|
|
|
|
for utxo in rows {
|
|
|
|
utxos.push(utxo.unwrap())
|
|
|
|
}
|
|
|
|
Ok(utxos)
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Inserts information about a scanned block into the database.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletWrite::advance_by_block instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn insert_block<'a, P>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
block_height: BlockHeight,
|
|
|
|
block_hash: BlockHash,
|
|
|
|
block_time: u32,
|
|
|
|
commitment_tree: &CommitmentTree<Node>,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
let mut encoded_tree = Vec::new();
|
|
|
|
commitment_tree.write(&mut encoded_tree).unwrap();
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
stmts.stmt_insert_block.execute(params![
|
|
|
|
u32::from(block_height),
|
|
|
|
&block_hash.0[..],
|
|
|
|
block_time,
|
|
|
|
encoded_tree
|
|
|
|
])?;
|
2021-01-12 17:24:18 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Inserts information about a mined transaction that was observed to
|
2021-03-25 21:22:45 -07:00
|
|
|
/// contain a note related to this wallet into the database.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletWrite::advance_by_block instead."
|
|
|
|
)]
|
2021-03-09 19:55:44 -08:00
|
|
|
pub fn put_tx_meta<'a, P, N>(
|
2021-01-12 17:24:18 -08:00
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
2021-03-09 19:55:44 -08:00
|
|
|
tx: &WalletTx<N>,
|
2021-01-12 17:24:18 -08:00
|
|
|
height: BlockHeight,
|
|
|
|
) -> Result<i64, SqliteClientError> {
|
2021-05-11 09:54:39 -07:00
|
|
|
let txid = tx.txid.as_ref().to_vec();
|
2021-01-12 17:24:18 -08:00
|
|
|
if stmts
|
|
|
|
.stmt_update_tx_meta
|
|
|
|
.execute(params![u32::from(height), (tx.index as i64), txid])?
|
|
|
|
== 0
|
|
|
|
{
|
|
|
|
// It isn't there, so insert our transaction into the database.
|
|
|
|
stmts
|
|
|
|
.stmt_insert_tx_meta
|
|
|
|
.execute(params![txid, u32::from(height), (tx.index as i64),])?;
|
|
|
|
|
|
|
|
Ok(stmts.wallet_db.conn.last_insert_rowid())
|
|
|
|
} else {
|
|
|
|
// It was there, so grab its row number.
|
|
|
|
stmts
|
|
|
|
.stmt_select_tx_ref
|
|
|
|
.query_row(&[txid], |row| row.get(0))
|
|
|
|
.map_err(SqliteClientError::from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Inserts full transaction data into the database.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletWrite::store_decrypted_tx instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn put_tx_data<'a, P>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx: &Transaction,
|
|
|
|
created_at: Option<time::OffsetDateTime>,
|
|
|
|
) -> Result<i64, SqliteClientError> {
|
2021-05-11 09:54:39 -07:00
|
|
|
let txid = tx.txid().as_ref().to_vec();
|
2021-01-12 17:24:18 -08:00
|
|
|
|
|
|
|
let mut raw_tx = vec![];
|
|
|
|
tx.write(&mut raw_tx)?;
|
|
|
|
|
|
|
|
if stmts
|
|
|
|
.stmt_update_tx_data
|
2021-05-12 16:01:17 -07:00
|
|
|
.execute(params![u32::from(tx.expiry_height()), raw_tx, txid,])?
|
2021-01-12 17:24:18 -08:00
|
|
|
== 0
|
|
|
|
{
|
|
|
|
// It isn't there, so insert our transaction into the database.
|
|
|
|
stmts.stmt_insert_tx_data.execute(params![
|
|
|
|
txid,
|
|
|
|
created_at,
|
2021-05-12 16:01:17 -07:00
|
|
|
u32::from(tx.expiry_height()),
|
2021-01-12 17:24:18 -08:00
|
|
|
raw_tx
|
|
|
|
])?;
|
|
|
|
|
|
|
|
Ok(stmts.wallet_db.conn.last_insert_rowid())
|
|
|
|
} else {
|
|
|
|
// It was there, so grab its row number.
|
|
|
|
stmts
|
|
|
|
.stmt_select_tx_ref
|
|
|
|
.query_row(&[txid], |row| row.get(0))
|
|
|
|
.map_err(SqliteClientError::from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Marks a given nullifier as having been revealed in the construction
|
2021-03-25 21:22:45 -07:00
|
|
|
/// of the specified transaction.
|
|
|
|
///
|
|
|
|
/// Marking a note spent in this fashion does NOT imply that the
|
|
|
|
/// spending transaction has been mined.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletWrite::store_sent_tx instead."
|
|
|
|
)]
|
2020-12-22 06:10:13 -08:00
|
|
|
pub fn mark_sapling_note_spent<'a, P>(
|
2021-01-12 17:24:18 -08:00
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx_ref: i64,
|
|
|
|
nf: &Nullifier,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
stmts
|
2020-12-22 06:10:13 -08:00
|
|
|
.stmt_mark_sapling_note_spent
|
2021-01-12 17:24:18 -08:00
|
|
|
.execute(&[tx_ref.to_sql()?, nf.0.to_sql()?])?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Marks the given UTXO as having been spent.
|
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2022-02-02 09:29:19 -08:00
|
|
|
pub(crate) fn mark_transparent_utxo_spent<'a, P>(
|
2020-12-22 06:10:13 -08:00
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx_ref: i64,
|
|
|
|
outpoint: &OutPoint,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
let sql_args: &[(&str, &dyn ToSql)] = &[
|
2022-02-01 13:02:07 -08:00
|
|
|
(":spent_in_tx", &tx_ref),
|
|
|
|
(":prevout_txid", &outpoint.hash().to_vec()),
|
|
|
|
(":prevout_idx", &outpoint.n()),
|
2020-12-22 06:10:13 -08:00
|
|
|
];
|
|
|
|
|
|
|
|
stmts
|
|
|
|
.stmt_mark_transparent_utxo_spent
|
2022-02-01 13:02:07 -08:00
|
|
|
.execute_named(sql_args)?;
|
2020-12-22 06:10:13 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Adds the given received UTXO to the datastore.
|
2021-10-04 13:09:02 -07:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2022-02-02 09:29:19 -08:00
|
|
|
pub(crate) fn put_received_transparent_utxo<'a, P: consensus::Parameters>(
|
2020-12-22 06:10:13 -08:00
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
output: &WalletTransparentOutput,
|
|
|
|
) -> Result<UtxoId, SqliteClientError> {
|
|
|
|
let sql_args: &[(&str, &dyn ToSql)] = &[
|
2021-10-01 10:42:04 -07:00
|
|
|
(
|
2022-02-01 13:02:07 -08:00
|
|
|
":address",
|
2021-10-01 10:42:04 -07:00
|
|
|
&output.address().encode(&stmts.wallet_db.params),
|
|
|
|
),
|
2022-02-01 13:02:07 -08:00
|
|
|
(":prevout_txid", &output.outpoint.hash().to_vec()),
|
|
|
|
(":prevout_idx", &output.outpoint.n()),
|
|
|
|
(":script", &output.txout.script_pubkey.0),
|
|
|
|
(":value_zat", &i64::from(output.txout.value)),
|
|
|
|
(":height", &u32::from(output.height)),
|
2020-12-22 06:10:13 -08:00
|
|
|
];
|
|
|
|
|
|
|
|
stmts
|
|
|
|
.stmt_insert_received_transparent_utxo
|
2022-02-01 13:02:07 -08:00
|
|
|
.execute_named(sql_args)?;
|
2020-12-22 06:10:13 -08:00
|
|
|
|
|
|
|
Ok(UtxoId(stmts.wallet_db.conn.last_insert_rowid()))
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Removes all records of UTXOs that were recorded as having been received
|
2022-02-01 10:37:43 -08:00
|
|
|
/// at block heights greater than the given height.
|
2022-01-20 13:33:29 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::rewind_to_height instead."
|
|
|
|
)]
|
2021-03-26 17:39:43 -07:00
|
|
|
pub fn delete_utxos_above<'a, P: consensus::Parameters>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
taddr: &TransparentAddress,
|
|
|
|
height: BlockHeight,
|
|
|
|
) -> Result<usize, SqliteClientError> {
|
|
|
|
let sql_args: &[(&str, &dyn ToSql)] = &[
|
2022-02-01 13:02:07 -08:00
|
|
|
(":address", &taddr.encode(&stmts.wallet_db.params)),
|
|
|
|
(":above_height", &u32::from(height)),
|
2021-03-26 17:39:43 -07:00
|
|
|
];
|
|
|
|
|
2022-02-01 13:02:07 -08:00
|
|
|
let rows = stmts.stmt_delete_utxos.execute_named(sql_args)?;
|
2021-03-26 17:39:43 -07:00
|
|
|
|
|
|
|
Ok(rows)
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Records the specified shielded output as having been received.
|
|
|
|
///
|
2022-01-31 15:36:09 -08:00
|
|
|
/// This implementation relies on the facts that:
|
2022-01-20 13:33:29 -08:00
|
|
|
/// - A transaction will not contain more than 2^63 shielded outputs.
|
|
|
|
/// - A note value will never exceed 2^63 zatoshis.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future release. Use zcash_client_backend::data_api::WalletWrite::store_decrypted_tx instead."
|
|
|
|
)]
|
|
|
|
#[allow(deprecated)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn put_received_note<'a, P, T: ShieldedOutput>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
output: &T,
|
|
|
|
tx_ref: i64,
|
|
|
|
) -> Result<NoteId, SqliteClientError> {
|
|
|
|
let rcm = output.note().rcm().to_repr();
|
2022-02-10 08:47:42 -08:00
|
|
|
let account = u32::from(output.account());
|
2021-01-12 17:24:18 -08:00
|
|
|
let diversifier = output.to().diversifier().0.to_vec();
|
|
|
|
let value = output.note().value as i64;
|
|
|
|
let rcm = rcm.as_ref();
|
2020-10-29 09:48:26 -07:00
|
|
|
let memo = output.memo().map(|m| m.as_slice());
|
2021-01-12 17:24:18 -08:00
|
|
|
let is_change = output.is_change();
|
|
|
|
let tx = tx_ref;
|
|
|
|
let output_index = output.index() as i64;
|
2021-03-09 12:15:27 -08:00
|
|
|
let nf_bytes = output.nullifier().map(|nf| nf.0.to_vec());
|
2021-01-12 17:24:18 -08:00
|
|
|
|
2021-01-12 20:06:57 -08:00
|
|
|
let sql_args: &[(&str, &dyn ToSql)] = &[
|
2022-02-01 13:02:07 -08:00
|
|
|
(":account", &account),
|
|
|
|
(":diversifier", &diversifier),
|
|
|
|
(":value", &value),
|
|
|
|
(":rcm", &rcm),
|
|
|
|
(":nf", &nf_bytes),
|
|
|
|
(":memo", &memo),
|
|
|
|
(":is_change", &is_change),
|
|
|
|
(":tx", &tx),
|
|
|
|
(":output_index", &output_index),
|
2021-01-12 17:24:18 -08:00
|
|
|
];
|
|
|
|
|
|
|
|
// First try updating an existing received note into the database.
|
2022-02-01 13:02:07 -08:00
|
|
|
if stmts.stmt_update_received_note.execute_named(sql_args)? == 0 {
|
2021-01-12 17:24:18 -08:00
|
|
|
// It isn't there, so insert our note into the database.
|
2022-02-01 13:02:07 -08:00
|
|
|
stmts.stmt_insert_received_note.execute_named(sql_args)?;
|
2021-01-12 17:24:18 -08:00
|
|
|
|
2021-02-03 11:46:47 -08:00
|
|
|
Ok(NoteId::ReceivedNoteId(
|
|
|
|
stmts.wallet_db.conn.last_insert_rowid(),
|
|
|
|
))
|
2021-01-12 17:24:18 -08:00
|
|
|
} else {
|
|
|
|
// It was there, so grab its row number.
|
|
|
|
stmts
|
|
|
|
.stmt_select_received_note
|
|
|
|
.query_row(params![tx_ref, (output.index() as i64)], |row| {
|
2021-01-19 11:27:48 -08:00
|
|
|
row.get(0).map(NoteId::ReceivedNoteId)
|
2021-01-12 17:24:18 -08:00
|
|
|
})
|
|
|
|
.map_err(SqliteClientError::from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Records the incremental witness for the specified note,
|
2021-03-25 21:22:45 -07:00
|
|
|
/// as of the given block height.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future release. Use zcash_client_backend::data_api::WalletWrite::store_decrypted_tx instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn insert_witness<'a, P>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
2021-01-19 11:27:48 -08:00
|
|
|
note_id: i64,
|
2021-01-12 17:24:18 -08:00
|
|
|
witness: &IncrementalWitness<Node>,
|
|
|
|
height: BlockHeight,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
let mut encoded = Vec::new();
|
|
|
|
witness.write(&mut encoded).unwrap();
|
|
|
|
|
|
|
|
stmts
|
|
|
|
.stmt_insert_witness
|
2021-01-19 11:27:48 -08:00
|
|
|
.execute(params![note_id, u32::from(height), encoded])?;
|
2021-01-12 17:24:18 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Removes old incremental witnesses up to the given block height.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::advance_by_block instead."
|
|
|
|
)]
|
2021-03-26 12:50:40 -07:00
|
|
|
pub fn prune_witnesses<P>(
|
|
|
|
stmts: &mut DataConnStmtCache<'_, P>,
|
2021-01-12 17:24:18 -08:00
|
|
|
below_height: BlockHeight,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
stmts
|
|
|
|
.stmt_prune_witnesses
|
|
|
|
.execute(&[u32::from(below_height)])?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Marks notes that have not been mined in transactions
|
2021-03-25 21:22:45 -07:00
|
|
|
/// as expired, up to the given block height.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::advance_by_block instead."
|
|
|
|
)]
|
2021-03-26 12:50:40 -07:00
|
|
|
pub fn update_expired_notes<P>(
|
|
|
|
stmts: &mut DataConnStmtCache<'_, P>,
|
2021-01-12 17:24:18 -08:00
|
|
|
height: BlockHeight,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
stmts.stmt_update_expired.execute(&[u32::from(height)])?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:47:59 -07:00
|
|
|
/// Records information about a note that your wallet created.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::store_decrypted_tx instead."
|
|
|
|
)]
|
|
|
|
#[allow(deprecated)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn put_sent_note<'a, P: consensus::Parameters>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx_ref: i64,
|
2021-03-29 13:35:18 -07:00
|
|
|
output_index: usize,
|
|
|
|
account: AccountId,
|
2021-04-13 10:02:35 -07:00
|
|
|
to: &PaymentAddress,
|
2021-03-29 13:35:18 -07:00
|
|
|
value: Amount,
|
|
|
|
memo: Option<&MemoBytes>,
|
2021-01-12 17:24:18 -08:00
|
|
|
) -> Result<(), SqliteClientError> {
|
2021-03-29 13:35:18 -07:00
|
|
|
let ivalue: i64 = value.into();
|
2021-01-12 17:24:18 -08:00
|
|
|
// Try updating an existing sent note.
|
|
|
|
if stmts.stmt_update_sent_note.execute(params![
|
2022-02-10 08:47:42 -08:00
|
|
|
u32::from(account),
|
2022-02-01 13:02:07 -08:00
|
|
|
encode_payment_address_p(&stmts.wallet_db.params, to),
|
2021-03-29 13:35:18 -07:00
|
|
|
ivalue,
|
|
|
|
&memo.map(|m| m.as_slice()),
|
2021-01-12 17:24:18 -08:00
|
|
|
tx_ref,
|
2022-02-02 10:57:34 -08:00
|
|
|
PoolType::Sapling.typecode(),
|
|
|
|
output_index as i64,
|
2021-01-12 17:24:18 -08:00
|
|
|
])? == 0
|
|
|
|
{
|
|
|
|
// It isn't there, so insert.
|
2022-02-01 13:02:07 -08:00
|
|
|
insert_sent_note(stmts, tx_ref, output_index, account, to, value, memo)?
|
2021-01-12 17:24:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Adds information about a sent transparent UTXO to the database if it does not already
|
2022-01-31 15:36:09 -08:00
|
|
|
/// exist, or updates it if a record for the UTXO already exists.
|
2022-01-20 13:33:29 -08:00
|
|
|
///
|
|
|
|
/// `output_index` is the index within transparent UTXOs of the transaction that contains the recipient output.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::store_decrypted_tx instead."
|
|
|
|
)]
|
|
|
|
#[allow(deprecated)]
|
2021-04-13 10:02:35 -07:00
|
|
|
pub fn put_sent_utxo<'a, P: consensus::Parameters>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx_ref: i64,
|
|
|
|
output_index: usize,
|
2021-04-20 07:04:56 -07:00
|
|
|
account: AccountId,
|
2021-04-13 10:02:35 -07:00
|
|
|
to: &TransparentAddress,
|
|
|
|
value: Amount,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
let ivalue: i64 = value.into();
|
2022-01-31 15:36:09 -08:00
|
|
|
// Try updating an existing sent UTXO.
|
2021-04-13 10:02:35 -07:00
|
|
|
if stmts.stmt_update_sent_note.execute(params![
|
2022-02-10 08:47:42 -08:00
|
|
|
u32::from(account),
|
2022-02-01 13:02:07 -08:00
|
|
|
encode_transparent_address_p(&stmts.wallet_db.params, to),
|
2021-04-13 10:02:35 -07:00
|
|
|
ivalue,
|
|
|
|
(None::<&[u8]>),
|
|
|
|
tx_ref,
|
2022-02-02 10:57:34 -08:00
|
|
|
PoolType::Transparent.typecode(),
|
|
|
|
output_index as i64,
|
2021-04-13 10:02:35 -07:00
|
|
|
])? == 0
|
|
|
|
{
|
|
|
|
// It isn't there, so insert.
|
2022-02-01 13:02:07 -08:00
|
|
|
insert_sent_utxo(stmts, tx_ref, output_index, account, to, value)?
|
2021-04-13 10:02:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-23 22:54:15 -07:00
|
|
|
/// Inserts a sent note into the wallet database.
|
|
|
|
///
|
|
|
|
/// `output_index` is the index within the transaction that contains the recipient output:
|
|
|
|
///
|
|
|
|
/// - If `to` is a Sapling address, this is an index into the Sapling outputs of the
|
|
|
|
/// transaction.
|
|
|
|
/// - If `to` is a transparent address, this is an index into the transparent outputs of
|
|
|
|
/// the transaction.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::store_sent_tx instead."
|
|
|
|
)]
|
2021-01-12 17:24:18 -08:00
|
|
|
pub fn insert_sent_note<'a, P: consensus::Parameters>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx_ref: i64,
|
|
|
|
output_index: usize,
|
|
|
|
account: AccountId,
|
2021-04-13 10:02:35 -07:00
|
|
|
to: &PaymentAddress,
|
2021-01-12 17:24:18 -08:00
|
|
|
value: Amount,
|
2021-03-17 08:21:10 -07:00
|
|
|
memo: Option<&MemoBytes>,
|
2021-01-12 17:24:18 -08:00
|
|
|
) -> Result<(), SqliteClientError> {
|
2021-04-13 10:02:35 -07:00
|
|
|
let to_str = encode_payment_address_p(&stmts.wallet_db.params, to);
|
2021-01-12 17:24:18 -08:00
|
|
|
let ivalue: i64 = value.into();
|
|
|
|
stmts.stmt_insert_sent_note.execute(params![
|
|
|
|
tx_ref,
|
2022-02-02 10:57:34 -08:00
|
|
|
PoolType::Sapling.typecode(),
|
2021-01-12 17:24:18 -08:00
|
|
|
(output_index as i64),
|
2022-02-10 08:47:42 -08:00
|
|
|
u32::from(account),
|
2021-01-12 17:24:18 -08:00
|
|
|
to_str,
|
|
|
|
ivalue,
|
2021-03-17 08:21:10 -07:00
|
|
|
memo.map(|m| m.as_slice().to_vec()),
|
2021-01-12 17:24:18 -08:00
|
|
|
])?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-04-13 10:02:35 -07:00
|
|
|
|
2022-01-20 13:33:29 -08:00
|
|
|
/// Inserts information about a sent transparent UTXO into the wallet database.
|
|
|
|
///
|
|
|
|
/// `output_index` is the index within transparent UTXOs of the transaction that contains the recipient output.
|
2022-02-02 09:29:19 -08:00
|
|
|
#[deprecated(
|
|
|
|
note = "This method will be removed in a future update. Use zcash_client_backend::data_api::WalletWrite::store_sent_tx instead."
|
|
|
|
)]
|
2021-04-13 10:02:35 -07:00
|
|
|
pub fn insert_sent_utxo<'a, P: consensus::Parameters>(
|
|
|
|
stmts: &mut DataConnStmtCache<'a, P>,
|
|
|
|
tx_ref: i64,
|
|
|
|
output_index: usize,
|
2021-04-20 07:04:56 -07:00
|
|
|
account: AccountId,
|
2021-04-13 10:02:35 -07:00
|
|
|
to: &TransparentAddress,
|
|
|
|
value: Amount,
|
|
|
|
) -> Result<(), SqliteClientError> {
|
|
|
|
let to_str = encode_transparent_address_p(&stmts.wallet_db.params, to);
|
|
|
|
let ivalue: i64 = value.into();
|
|
|
|
stmts.stmt_insert_sent_note.execute(params![
|
|
|
|
tx_ref,
|
2022-02-02 10:57:34 -08:00
|
|
|
PoolType::Transparent.typecode(),
|
2022-02-10 08:47:42 -08:00
|
|
|
output_index as i64,
|
|
|
|
u32::from(account),
|
2021-04-13 10:02:35 -07:00
|
|
|
to_str,
|
|
|
|
ivalue,
|
2022-02-02 10:57:34 -08:00
|
|
|
(None::<&[u8]>),
|
2021-04-13 10:02:35 -07:00
|
|
|
])?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-03-08 18:53:38 -08:00
|
|
|
#[cfg(test)]
|
2022-02-02 09:29:19 -08:00
|
|
|
#[allow(deprecated)]
|
2019-03-08 18:53:38 -08:00
|
|
|
mod tests {
|
|
|
|
use tempfile::NamedTempFile;
|
2020-08-05 18:14:45 -07:00
|
|
|
|
2021-03-31 14:59:36 -07:00
|
|
|
use zcash_primitives::transaction::components::Amount;
|
2019-03-08 18:53:38 -08:00
|
|
|
|
2020-10-19 14:20:34 -07:00
|
|
|
use zcash_client_backend::data_api::WalletRead;
|
2020-08-05 18:14:45 -07:00
|
|
|
|
2021-03-31 14:59:36 -07:00
|
|
|
use crate::{tests, wallet::init::init_wallet_db, AccountId, WalletDb};
|
2019-03-08 18:53:38 -08:00
|
|
|
|
2020-09-10 15:55:34 -07:00
|
|
|
use super::{get_address, get_balance};
|
2020-08-05 18:14:45 -07:00
|
|
|
|
2019-03-08 18:53:38 -08:00
|
|
|
#[test]
|
|
|
|
fn empty_database_has_no_balance() {
|
|
|
|
let data_file = NamedTempFile::new().unwrap();
|
2021-03-26 22:17:54 -07:00
|
|
|
let db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap();
|
2021-01-12 20:32:23 -08:00
|
|
|
init_wallet_db(&db_data).unwrap();
|
2019-03-08 18:53:38 -08:00
|
|
|
|
|
|
|
// Add an account to the wallet
|
2021-03-31 14:59:36 -07:00
|
|
|
tests::init_test_accounts_table(&db_data);
|
2019-03-08 18:53:38 -08:00
|
|
|
|
|
|
|
// The account should be empty
|
2022-02-10 08:47:42 -08:00
|
|
|
assert_eq!(
|
|
|
|
get_balance(&db_data, AccountId::from(0)).unwrap(),
|
|
|
|
Amount::zero()
|
|
|
|
);
|
2019-03-08 18:53:38 -08:00
|
|
|
|
2020-09-10 15:55:34 -07:00
|
|
|
// We can't get an anchor height, as we have not scanned any blocks.
|
2021-08-27 12:45:36 -07:00
|
|
|
assert_eq!((&db_data).get_target_and_anchor_heights(10).unwrap(), None);
|
2019-03-08 18:53:38 -08:00
|
|
|
|
|
|
|
// An invalid account has zero balance
|
2022-02-10 08:47:42 -08:00
|
|
|
assert!(get_address(&db_data, AccountId::from(1)).is_err());
|
|
|
|
assert_eq!(
|
|
|
|
get_balance(&db_data, AccountId::from(0)).unwrap(),
|
|
|
|
Amount::zero()
|
|
|
|
);
|
2019-03-08 18:53:38 -08:00
|
|
|
}
|
|
|
|
}
|