Skip to content

Commit

Permalink
Most of the rest of the ZIP 320 implementation.
Browse files Browse the repository at this point in the history
Signed-off-by: Daira-Emma Hopwood <[email protected]>
  • Loading branch information
daira committed May 18, 2024
1 parent bf9d396 commit 1af90e3
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 56 deletions.
24 changes: 17 additions & 7 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1615,8 +1615,8 @@ pub trait WalletCommitmentTrees {
/// An error related to tracking of ephemeral transparent addresses.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AddressTrackingError {
/// Only ZIP 32 accounts are supported for ZIP 320 (TEX Addresses).
UnsupportedAccountType,
/// The account id could not be found.
AccountNotFound,

/// The proposal cannot be constructed until transactions with previously reserved
/// ephemeral address outputs have been mined.
Expand All @@ -1629,9 +1629,9 @@ pub enum AddressTrackingError {
impl Display for AddressTrackingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AddressTrackingError::UnsupportedAccountType => write!(
AddressTrackingError::AccountNotFound => write!(
f,
"Only ZIP 32 accounts are supported for ZIP 320 (TEX Addresses)."
"The account id could not be found."
),
AddressTrackingError::ReachedGapLimit => write!(
f,
Expand All @@ -1650,6 +1650,9 @@ impl Display for AddressTrackingError {
/// This trait serves to allow the corresponding wallet functions to be abstracted
/// away from any particular data storage substrate.
pub trait WalletAddressTracking {
/// The type of the account identifier.
type AccountId: Copy + Debug + Eq + Hash;

/// Reserves the next available address.
///
/// To ensure that sufficient information is stored on-chain to allow recovering
Expand All @@ -1665,7 +1668,7 @@ pub trait WalletAddressTracking {
/// [BIP 44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#user-content-Address_gap_limit
fn reserve_next_address(
&self,
uivk: &UnifiedIncomingViewingKey,
account_id: Self::AccountId,
) -> Result<TransparentAddress, AddressTrackingError>;

/// Frees previously reserved ephemeral transparent addresses.
Expand All @@ -1679,12 +1682,14 @@ pub trait WalletAddressTracking {
/// account.
fn unreserve_addresses(
&self,
account_id: Self::AccountId,
address: &[TransparentAddress],
) -> Result<(), AddressTrackingError>;

/// Mark addresses as having been used.
fn mark_addresses_as_used(
&self,
account_id: Self::AccountId,
address: &[TransparentAddress],
) -> Result<(), AddressTrackingError>;

Expand All @@ -1693,6 +1698,7 @@ pub trait WalletAddressTracking {
/// index if necessary.
fn mark_addresses_as_mined(
&self,
account_id: Self::AccountId,
addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError>;
}
Expand All @@ -1703,7 +1709,6 @@ pub mod testing {
use secrecy::{ExposeSecret, SecretVec};
use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree};
use std::{collections::HashMap, convert::Infallible, num::NonZeroU32};
use zcash_keys::keys::UnifiedIncomingViewingKey;
use zip32::fingerprint::SeedFingerprint;

use zcash_primitives::{
Expand Down Expand Up @@ -2019,29 +2024,34 @@ pub mod testing {
}

impl WalletAddressTracking for MockWalletDb {
type AccountId = u32;

fn reserve_next_address(
&self,
_uivk: &UnifiedIncomingViewingKey,
_account_id: Self::AccountId,
) -> Result<TransparentAddress, AddressTrackingError> {
Err(AddressTrackingError::ReachedGapLimit)
}

fn unreserve_addresses(
&self,
_account_id: Self::AccountId,
_addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError> {
Ok(())
}

fn mark_addresses_as_used(
&self,
_account_id: Self::AccountId,
_addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError> {
Ok(())
}

fn mark_addresses_as_mined(
&self,
_account_id: Self::AccountId,
_addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError> {
Ok(())
Expand Down
35 changes: 21 additions & 14 deletions zcash_client_backend/src/data_api/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@ where
Error = <DbT as InputSource>::Error,
AccountId = <DbT as InputSource>::AccountId,
>,
DbT: WalletCommitmentTrees + WalletAddressTracking,
DbT: WalletCommitmentTrees,
DbT: WalletAddressTracking<AccountId = <DbT as InputSource>::AccountId>,
{
let account = wallet_db
.get_account_for_ufvk(&usk.to_unified_full_viewing_key())
Expand Down Expand Up @@ -368,7 +369,8 @@ where
Error = <DbT as InputSource>::Error,
AccountId = <DbT as InputSource>::AccountId,
>,
DbT: WalletCommitmentTrees + WalletAddressTracking,
DbT: WalletCommitmentTrees,
DbT: WalletAddressTracking<AccountId = <DbT as InputSource>::AccountId>,
ParamsT: consensus::Parameters + Clone,
InputsT: InputSelector<InputSource = DbT>,
{
Expand Down Expand Up @@ -601,7 +603,8 @@ pub fn create_proposed_transactions<DbT, ParamsT, InputsErrT, FeeRuleT, N>(
>,
>
where
DbT: WalletWrite + WalletCommitmentTrees + WalletAddressTracking,
DbT: WalletWrite + WalletCommitmentTrees,
DbT: WalletAddressTracking<AccountId = <DbT as WalletRead>::AccountId>,
ParamsT: consensus::Parameters + Clone,
FeeRuleT: FeeRule,
{
Expand Down Expand Up @@ -654,8 +657,8 @@ fn create_proposed_transaction<DbT, ParamsT, InputsErrT, FeeRuleT, N>(
>,
>
where
DbT: WalletWrite + WalletCommitmentTrees + WalletAddressTracking,

DbT: WalletWrite + WalletCommitmentTrees,
DbT: WalletAddressTracking<AccountId = <DbT as WalletRead>::AccountId>,
ParamsT: consensus::Parameters + Clone,
FeeRuleT: FeeRule,
{
Expand Down Expand Up @@ -1096,8 +1099,9 @@ where
// TODO: make sure that this is supposed to be an ephemeral output (by checking
// for backlinks) rather than a non-ephemeral transparent change output.
let ephemeral_addr = wallet_db
.reserve_next_address(&account.uivk())
.reserve_next_address(account_id)
.map_err(InputSelectorError::from)?;

ephemeral_addrs.push(ephemeral_addr);
builder.add_transparent_output(&ephemeral_addr, change_value.value())?;
transparent_output_meta.push((ephemeral_addr, change_value.value()))
Expand Down Expand Up @@ -1221,11 +1225,13 @@ where
};
let res = finish();

match res {
Ok(_) => wallet_db.mark_addresses_as_used(&ephemeral_addrs),
Err(_) => wallet_db.unreserve_addresses(&ephemeral_addrs),
if !ephemeral_addrs.is_empty() {
match res {
Ok(_) => wallet_db.mark_addresses_as_used(account_id, &ephemeral_addrs),
Err(_) => wallet_db.unreserve_addresses(account_id, &ephemeral_addrs),
}
.map_err(InputSelectorError::from)?;
}
.map_err(InputSelectorError::from)?;

res
}
Expand Down Expand Up @@ -1287,10 +1293,11 @@ pub fn shield_transparent_funds<DbT, ParamsT, InputsT>(
>
where
ParamsT: consensus::Parameters,
DbT: WalletWrite
+ WalletCommitmentTrees
+ WalletAddressTracking
+ InputSource<Error = <DbT as WalletRead>::Error>,
DbT: WalletRead,
DbT: WalletWrite,
DbT: WalletCommitmentTrees,
DbT: InputSource<Error = <DbT as WalletRead>::Error>,
DbT: WalletAddressTracking<AccountId = <DbT as WalletRead>::AccountId>,
InputsT: ShieldingSelector<InputSource = DbT>,
{
let proposal = propose_shielding(
Expand Down
61 changes: 41 additions & 20 deletions zcash_client_sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use rusqlite::{self, Connection};
use secrecy::{ExposeSecret, SecretVec};
use shardtree::{error::ShardTreeError, ShardTree};
use std::{
borrow::Borrow, collections::HashMap, convert::AsRef, fmt, num::NonZeroU32, ops::Range,
borrow::Borrow, collections::{HashMap, hash_map::Entry::{Occupied, Vacant}}, convert::AsRef, fmt, num::NonZeroU32, ops::Range,
path::Path, rc::Rc, sync::Mutex,
};
use subtle::ConditionallySelectable;
Expand All @@ -66,7 +66,7 @@ use zcash_client_backend::{
wallet::{Note, NoteId, ReceivedNote, Recipient, WalletTransparentOutput},
DecryptedOutput, PoolType, ShieldedProtocol, TransferType,
};
use zcash_keys::{address::Address, keys::UnifiedIncomingViewingKey};
use zcash_keys::address::Address;
use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
Expand Down Expand Up @@ -171,7 +171,7 @@ pub struct UtxoId(pub i64);
pub struct WalletDb<C, P> {
conn: C,
params: P,
address_tracker: Rc<Mutex<AddressTracker>>,
address_trackers: Rc<Mutex<HashMap<AccountId, AddressTracker>>>,
}

/// A wrapper for a SQLite transaction affecting the wallet database.
Expand All @@ -188,11 +188,10 @@ impl<P: consensus::Parameters + Clone> WalletDb<Connection, P> {
pub fn for_path<F: AsRef<Path>>(path: F, params: P) -> Result<Self, rusqlite::Error> {
Connection::open(path).and_then(move |conn| {
rusqlite::vtab::array::load_module(&conn)?;
let tracker = AddressTracker::new(&conn, &params);
Ok(WalletDb {
conn,
params,
address_tracker: Rc::new(Mutex::new(tracker)),
address_trackers: Rc::new(Mutex::new(HashMap::new())),
})
})
}
Expand All @@ -205,7 +204,7 @@ impl<P: consensus::Parameters + Clone> WalletDb<Connection, P> {
let mut wdb = WalletDb {
conn: SqlTransaction(&tx),
params: self.params.clone(),
address_tracker: self.address_tracker.clone(),
address_trackers: self.address_trackers.clone(),
};
let result = f(&mut wdb)?;
tx.commit()?;
Expand Down Expand Up @@ -1268,7 +1267,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
)?;
}
}
wdb.mark_addresses_as_mined(&output_addrs).map_err(SqliteClientError::from)?;
wdb.mark_addresses_as_mined(account_id, &output_addrs).map_err(SqliteClientError::from)?;
}
}

Expand Down Expand Up @@ -1582,48 +1581,70 @@ where
C: Borrow<rusqlite::Connection>,
P: consensus::Parameters,
{
/// Backend-specific account identifier.
type AccountId = <Self as InputSource>::AccountId;

fn reserve_next_address(
&self,
uivk: &UnifiedIncomingViewingKey,
account_id: Self::AccountId,
) -> Result<TransparentAddress, AddressTrackingError> {
let mut tracker = self
.address_tracker
let mut trackers = self
.address_trackers
.lock()
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
tracker.reserve_next_address(self, uivk)

match trackers.entry(account_id) {
Occupied(e) => e.into_mut(),
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
}.reserve_next_address(self)
}

fn unreserve_addresses(
&self,
account_id: Self::AccountId,
addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError> {
let mut tracker = self
.address_tracker
let mut trackers = self
.address_trackers
.lock()
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
tracker.unreserve_addresses(self, addresses)

match trackers.entry(account_id) {
Occupied(e) => e.into_mut(),
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
}.unreserve_addresses(self, addresses)
}

fn mark_addresses_as_used(
&self,
account_id: Self::AccountId,
addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError> {
let mut tracker = self
.address_tracker
let mut trackers = self
.address_trackers
.lock()
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
tracker.mark_addresses_as_used(self, addresses)

match trackers.entry(account_id) {
Occupied(e) => e.into_mut(),
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
}.mark_addresses_as_used(self, addresses)
}

fn mark_addresses_as_mined(
&self,
account_id: Self::AccountId,
addresses: &[TransparentAddress],
) -> Result<(), AddressTrackingError> {
let mut tracker = self
.address_tracker
let mut trackers = self
.address_trackers
.lock()
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
tracker.mark_addresses_as_mined(self, addresses)

match trackers.entry(account_id) {
Occupied(e) => e.into_mut(),
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
}.mark_addresses_as_mined(self, addresses)
}
}

Expand Down
Loading

0 comments on commit 1af90e3

Please sign in to comment.