Skip to content

Commit 1af90e3

Browse files
committed
Most of the rest of the ZIP 320 implementation.
Signed-off-by: Daira-Emma Hopwood <[email protected]>
1 parent bf9d396 commit 1af90e3

File tree

4 files changed

+167
-56
lines changed

4 files changed

+167
-56
lines changed

zcash_client_backend/src/data_api.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -1615,8 +1615,8 @@ pub trait WalletCommitmentTrees {
16151615
/// An error related to tracking of ephemeral transparent addresses.
16161616
#[derive(Debug, Clone, PartialEq, Eq)]
16171617
pub enum AddressTrackingError {
1618-
/// Only ZIP 32 accounts are supported for ZIP 320 (TEX Addresses).
1619-
UnsupportedAccountType,
1618+
/// The account id could not be found.
1619+
AccountNotFound,
16201620

16211621
/// The proposal cannot be constructed until transactions with previously reserved
16221622
/// ephemeral address outputs have been mined.
@@ -1629,9 +1629,9 @@ pub enum AddressTrackingError {
16291629
impl Display for AddressTrackingError {
16301630
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16311631
match self {
1632-
AddressTrackingError::UnsupportedAccountType => write!(
1632+
AddressTrackingError::AccountNotFound => write!(
16331633
f,
1634-
"Only ZIP 32 accounts are supported for ZIP 320 (TEX Addresses)."
1634+
"The account id could not be found."
16351635
),
16361636
AddressTrackingError::ReachedGapLimit => write!(
16371637
f,
@@ -1650,6 +1650,9 @@ impl Display for AddressTrackingError {
16501650
/// This trait serves to allow the corresponding wallet functions to be abstracted
16511651
/// away from any particular data storage substrate.
16521652
pub trait WalletAddressTracking {
1653+
/// The type of the account identifier.
1654+
type AccountId: Copy + Debug + Eq + Hash;
1655+
16531656
/// Reserves the next available address.
16541657
///
16551658
/// To ensure that sufficient information is stored on-chain to allow recovering
@@ -1665,7 +1668,7 @@ pub trait WalletAddressTracking {
16651668
/// [BIP 44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#user-content-Address_gap_limit
16661669
fn reserve_next_address(
16671670
&self,
1668-
uivk: &UnifiedIncomingViewingKey,
1671+
account_id: Self::AccountId,
16691672
) -> Result<TransparentAddress, AddressTrackingError>;
16701673

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

16851689
/// Mark addresses as having been used.
16861690
fn mark_addresses_as_used(
16871691
&self,
1692+
account_id: Self::AccountId,
16881693
address: &[TransparentAddress],
16891694
) -> Result<(), AddressTrackingError>;
16901695

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

17091714
use zcash_primitives::{
@@ -2019,29 +2024,34 @@ pub mod testing {
20192024
}
20202025

20212026
impl WalletAddressTracking for MockWalletDb {
2027+
type AccountId = u32;
2028+
20222029
fn reserve_next_address(
20232030
&self,
2024-
_uivk: &UnifiedIncomingViewingKey,
2031+
_account_id: Self::AccountId,
20252032
) -> Result<TransparentAddress, AddressTrackingError> {
20262033
Err(AddressTrackingError::ReachedGapLimit)
20272034
}
20282035

20292036
fn unreserve_addresses(
20302037
&self,
2038+
_account_id: Self::AccountId,
20312039
_addresses: &[TransparentAddress],
20322040
) -> Result<(), AddressTrackingError> {
20332041
Ok(())
20342042
}
20352043

20362044
fn mark_addresses_as_used(
20372045
&self,
2046+
_account_id: Self::AccountId,
20382047
_addresses: &[TransparentAddress],
20392048
) -> Result<(), AddressTrackingError> {
20402049
Ok(())
20412050
}
20422051

20432052
fn mark_addresses_as_mined(
20442053
&self,
2054+
_account_id: Self::AccountId,
20452055
_addresses: &[TransparentAddress],
20462056
) -> Result<(), AddressTrackingError> {
20472057
Ok(())

zcash_client_backend/src/data_api/wallet.rs

+21-14
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ where
257257
Error = <DbT as InputSource>::Error,
258258
AccountId = <DbT as InputSource>::AccountId,
259259
>,
260-
DbT: WalletCommitmentTrees + WalletAddressTracking,
260+
DbT: WalletCommitmentTrees,
261+
DbT: WalletAddressTracking<AccountId = <DbT as InputSource>::AccountId>,
261262
{
262263
let account = wallet_db
263264
.get_account_for_ufvk(&usk.to_unified_full_viewing_key())
@@ -368,7 +369,8 @@ where
368369
Error = <DbT as InputSource>::Error,
369370
AccountId = <DbT as InputSource>::AccountId,
370371
>,
371-
DbT: WalletCommitmentTrees + WalletAddressTracking,
372+
DbT: WalletCommitmentTrees,
373+
DbT: WalletAddressTracking<AccountId = <DbT as InputSource>::AccountId>,
372374
ParamsT: consensus::Parameters + Clone,
373375
InputsT: InputSelector<InputSource = DbT>,
374376
{
@@ -601,7 +603,8 @@ pub fn create_proposed_transactions<DbT, ParamsT, InputsErrT, FeeRuleT, N>(
601603
>,
602604
>
603605
where
604-
DbT: WalletWrite + WalletCommitmentTrees + WalletAddressTracking,
606+
DbT: WalletWrite + WalletCommitmentTrees,
607+
DbT: WalletAddressTracking<AccountId = <DbT as WalletRead>::AccountId>,
605608
ParamsT: consensus::Parameters + Clone,
606609
FeeRuleT: FeeRule,
607610
{
@@ -654,8 +657,8 @@ fn create_proposed_transaction<DbT, ParamsT, InputsErrT, FeeRuleT, N>(
654657
>,
655658
>
656659
where
657-
DbT: WalletWrite + WalletCommitmentTrees + WalletAddressTracking,
658-
660+
DbT: WalletWrite + WalletCommitmentTrees,
661+
DbT: WalletAddressTracking<AccountId = <DbT as WalletRead>::AccountId>,
659662
ParamsT: consensus::Parameters + Clone,
660663
FeeRuleT: FeeRule,
661664
{
@@ -1096,8 +1099,9 @@ where
10961099
// TODO: make sure that this is supposed to be an ephemeral output (by checking
10971100
// for backlinks) rather than a non-ephemeral transparent change output.
10981101
let ephemeral_addr = wallet_db
1099-
.reserve_next_address(&account.uivk())
1102+
.reserve_next_address(account_id)
11001103
.map_err(InputSelectorError::from)?;
1104+
11011105
ephemeral_addrs.push(ephemeral_addr);
11021106
builder.add_transparent_output(&ephemeral_addr, change_value.value())?;
11031107
transparent_output_meta.push((ephemeral_addr, change_value.value()))
@@ -1221,11 +1225,13 @@ where
12211225
};
12221226
let res = finish();
12231227

1224-
match res {
1225-
Ok(_) => wallet_db.mark_addresses_as_used(&ephemeral_addrs),
1226-
Err(_) => wallet_db.unreserve_addresses(&ephemeral_addrs),
1228+
if !ephemeral_addrs.is_empty() {
1229+
match res {
1230+
Ok(_) => wallet_db.mark_addresses_as_used(account_id, &ephemeral_addrs),
1231+
Err(_) => wallet_db.unreserve_addresses(account_id, &ephemeral_addrs),
1232+
}
1233+
.map_err(InputSelectorError::from)?;
12271234
}
1228-
.map_err(InputSelectorError::from)?;
12291235

12301236
res
12311237
}
@@ -1287,10 +1293,11 @@ pub fn shield_transparent_funds<DbT, ParamsT, InputsT>(
12871293
>
12881294
where
12891295
ParamsT: consensus::Parameters,
1290-
DbT: WalletWrite
1291-
+ WalletCommitmentTrees
1292-
+ WalletAddressTracking
1293-
+ InputSource<Error = <DbT as WalletRead>::Error>,
1296+
DbT: WalletRead,
1297+
DbT: WalletWrite,
1298+
DbT: WalletCommitmentTrees,
1299+
DbT: InputSource<Error = <DbT as WalletRead>::Error>,
1300+
DbT: WalletAddressTracking<AccountId = <DbT as WalletRead>::AccountId>,
12941301
InputsT: ShieldingSelector<InputSource = DbT>,
12951302
{
12961303
let proposal = propose_shielding(

zcash_client_sqlite/src/lib.rs

+41-20
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rusqlite::{self, Connection};
4242
use secrecy::{ExposeSecret, SecretVec};
4343
use shardtree::{error::ShardTreeError, ShardTree};
4444
use std::{
45-
borrow::Borrow, collections::HashMap, convert::AsRef, fmt, num::NonZeroU32, ops::Range,
45+
borrow::Borrow, collections::{HashMap, hash_map::Entry::{Occupied, Vacant}}, convert::AsRef, fmt, num::NonZeroU32, ops::Range,
4646
path::Path, rc::Rc, sync::Mutex,
4747
};
4848
use subtle::ConditionallySelectable;
@@ -66,7 +66,7 @@ use zcash_client_backend::{
6666
wallet::{Note, NoteId, ReceivedNote, Recipient, WalletTransparentOutput},
6767
DecryptedOutput, PoolType, ShieldedProtocol, TransferType,
6868
};
69-
use zcash_keys::{address::Address, keys::UnifiedIncomingViewingKey};
69+
use zcash_keys::address::Address;
7070
use zcash_primitives::{
7171
block::BlockHash,
7272
consensus::{self, BlockHeight},
@@ -171,7 +171,7 @@ pub struct UtxoId(pub i64);
171171
pub struct WalletDb<C, P> {
172172
conn: C,
173173
params: P,
174-
address_tracker: Rc<Mutex<AddressTracker>>,
174+
address_trackers: Rc<Mutex<HashMap<AccountId, AddressTracker>>>,
175175
}
176176

177177
/// A wrapper for a SQLite transaction affecting the wallet database.
@@ -188,11 +188,10 @@ impl<P: consensus::Parameters + Clone> WalletDb<Connection, P> {
188188
pub fn for_path<F: AsRef<Path>>(path: F, params: P) -> Result<Self, rusqlite::Error> {
189189
Connection::open(path).and_then(move |conn| {
190190
rusqlite::vtab::array::load_module(&conn)?;
191-
let tracker = AddressTracker::new(&conn, &params);
192191
Ok(WalletDb {
193192
conn,
194193
params,
195-
address_tracker: Rc::new(Mutex::new(tracker)),
194+
address_trackers: Rc::new(Mutex::new(HashMap::new())),
196195
})
197196
})
198197
}
@@ -205,7 +204,7 @@ impl<P: consensus::Parameters + Clone> WalletDb<Connection, P> {
205204
let mut wdb = WalletDb {
206205
conn: SqlTransaction(&tx),
207206
params: self.params.clone(),
208-
address_tracker: self.address_tracker.clone(),
207+
address_trackers: self.address_trackers.clone(),
209208
};
210209
let result = f(&mut wdb)?;
211210
tx.commit()?;
@@ -1268,7 +1267,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
12681267
)?;
12691268
}
12701269
}
1271-
wdb.mark_addresses_as_mined(&output_addrs).map_err(SqliteClientError::from)?;
1270+
wdb.mark_addresses_as_mined(account_id, &output_addrs).map_err(SqliteClientError::from)?;
12721271
}
12731272
}
12741273

@@ -1582,48 +1581,70 @@ where
15821581
C: Borrow<rusqlite::Connection>,
15831582
P: consensus::Parameters,
15841583
{
1584+
/// Backend-specific account identifier.
1585+
type AccountId = <Self as InputSource>::AccountId;
1586+
15851587
fn reserve_next_address(
15861588
&self,
1587-
uivk: &UnifiedIncomingViewingKey,
1589+
account_id: Self::AccountId,
15881590
) -> Result<TransparentAddress, AddressTrackingError> {
1589-
let mut tracker = self
1590-
.address_tracker
1591+
let mut trackers = self
1592+
.address_trackers
15911593
.lock()
15921594
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
1593-
tracker.reserve_next_address(self, uivk)
1595+
1596+
match trackers.entry(account_id) {
1597+
Occupied(e) => e.into_mut(),
1598+
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
1599+
}.reserve_next_address(self)
15941600
}
15951601

15961602
fn unreserve_addresses(
15971603
&self,
1604+
account_id: Self::AccountId,
15981605
addresses: &[TransparentAddress],
15991606
) -> Result<(), AddressTrackingError> {
1600-
let mut tracker = self
1601-
.address_tracker
1607+
let mut trackers = self
1608+
.address_trackers
16021609
.lock()
16031610
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
1604-
tracker.unreserve_addresses(self, addresses)
1611+
1612+
match trackers.entry(account_id) {
1613+
Occupied(e) => e.into_mut(),
1614+
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
1615+
}.unreserve_addresses(self, addresses)
16051616
}
16061617

16071618
fn mark_addresses_as_used(
16081619
&self,
1620+
account_id: Self::AccountId,
16091621
addresses: &[TransparentAddress],
16101622
) -> Result<(), AddressTrackingError> {
1611-
let mut tracker = self
1612-
.address_tracker
1623+
let mut trackers = self
1624+
.address_trackers
16131625
.lock()
16141626
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
1615-
tracker.mark_addresses_as_used(self, addresses)
1627+
1628+
match trackers.entry(account_id) {
1629+
Occupied(e) => e.into_mut(),
1630+
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
1631+
}.mark_addresses_as_used(self, addresses)
16161632
}
16171633

16181634
fn mark_addresses_as_mined(
16191635
&self,
1636+
account_id: Self::AccountId,
16201637
addresses: &[TransparentAddress],
16211638
) -> Result<(), AddressTrackingError> {
1622-
let mut tracker = self
1623-
.address_tracker
1639+
let mut trackers = self
1640+
.address_trackers
16241641
.lock()
16251642
.map_err(|e| AddressTrackingError::Internal(format!("{:?}", e)))?;
1626-
tracker.mark_addresses_as_mined(self, addresses)
1643+
1644+
match trackers.entry(account_id) {
1645+
Occupied(e) => e.into_mut(),
1646+
Vacant(e) => e.insert(AddressTracker::new(self, account_id)?),
1647+
}.mark_addresses_as_mined(self, addresses)
16271648
}
16281649
}
16291650

0 commit comments

Comments
 (0)