Skip to content

Commit

Permalink
zcash_client_sqlite: Move encoding types & functions into the `encodi…
Browse files Browse the repository at this point in the history
…ng` module.
  • Loading branch information
nuttycom committed Mar 1, 2025
1 parent 0dcb2b2 commit 96f5426
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 123 deletions.
4 changes: 2 additions & 2 deletions zcash_client_sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ use {
};

#[cfg(any(test, feature = "test-dependencies", feature = "transparent-inputs"))]
use crate::wallet::KeyScope;
use crate::wallet::encoding::KeyScope;

#[cfg(any(test, feature = "test-dependencies", not(feature = "orchard")))]
use zcash_protocol::PoolType;
Expand Down Expand Up @@ -894,7 +894,7 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters, CL> WalletTest
txid: &TxId,
protocol: ShieldedProtocol,
) -> Result<Vec<NoteId>, <Self as WalletRead>::Error> {
use crate::wallet::pool_code;
use crate::wallet::encoding::pool_code;

let mut stmt_sent_notes = self.conn.borrow().prepare(
"SELECT output_index
Expand Down
119 changes: 4 additions & 115 deletions zcash_client_sqlite/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ use std::{
time::SystemTime,
};

use encoding::ReceiverFlags;
use encoding::{
account_kind_code, decode_diversifier_index_be, encode_diversifier_index_be, memo_repr,
pool_code, KeyScope, ReceiverFlags,
};
use incrementalmerkletree::{Marking, Retention};
use rusqlite::{self, named_params, params, Connection, OptionalExtension};
use secrecy::{ExposeSecret, SecretVec};
Expand Down Expand Up @@ -201,13 +204,6 @@ fn parse_account_source(
}
}

fn account_kind_code(value: &AccountSource) -> u32 {
match value {
AccountSource::Derived { .. } => 0,
AccountSource::Imported { .. } => 1,
}
}

/// The viewing key that an [`Account`] has available to it.
#[derive(Debug, Clone)]
pub(crate) enum ViewingKey {
Expand Down Expand Up @@ -355,113 +351,6 @@ pub(crate) fn seed_matches_derived_account<P: consensus::Parameters>(
}
}

pub(crate) fn pool_code(pool_type: PoolType) -> i64 {
// These constants are *incidentally* shared with the typecodes
// for unified addresses, but this is exclusively an internal
// implementation detail.
match pool_type {
PoolType::Transparent => 0i64,
PoolType::Shielded(ShieldedProtocol::Sapling) => 2i64,
PoolType::Shielded(ShieldedProtocol::Orchard) => 3i64,
}
}

/// An enumeration of the scopes of keys that are generated by the `zcash_client_sqlite`
/// implementation of the `WalletWrite` trait.
///
/// This extends the [`zip32::Scope`] type to include the custom scope used to generate keys for
/// ephemeral transparent addresses.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum KeyScope {
/// A key scope corresponding to a [`zip32::Scope`].
Zip32(zip32::Scope),
/// An ephemeral transparent address, which is derived from an account's transparent
/// [`AccountPubKey`] with the BIP 44 path `change` level index set to the value `2`.
///
/// [`AccountPubKey`]: zcash_primitives::legacy::keys::AccountPubKey
Ephemeral,
}

impl KeyScope {
pub(crate) const EXTERNAL: KeyScope = KeyScope::Zip32(zip32::Scope::External);
pub(crate) const INTERNAL: KeyScope = KeyScope::Zip32(zip32::Scope::Internal);

pub(crate) fn encode(&self) -> i64 {
match self {
KeyScope::Zip32(zip32::Scope::External) => 0i64,
KeyScope::Zip32(zip32::Scope::Internal) => 1i64,
KeyScope::Ephemeral => 2i64,
}
}

pub(crate) fn decode(code: i64) -> Result<Self, SqliteClientError> {
match code {
0i64 => Ok(KeyScope::EXTERNAL),
1i64 => Ok(KeyScope::INTERNAL),
2i64 => Ok(KeyScope::Ephemeral),
other => Err(SqliteClientError::CorruptedData(format!(
"Invalid key scope code: {}",
other
))),
}
}
}

impl From<zip32::Scope> for KeyScope {
fn from(value: zip32::Scope) -> Self {
KeyScope::Zip32(value)
}
}

#[cfg(feature = "transparent-inputs")]
impl From<KeyScope> for TransparentKeyScope {
fn from(value: KeyScope) -> Self {
match value {
KeyScope::Zip32(scope) => scope.into(),
KeyScope::Ephemeral => TransparentKeyScope::custom(2).expect("valid scope"),
}
}
}

impl TryFrom<KeyScope> for zip32::Scope {
type Error = ();

fn try_from(value: KeyScope) -> Result<Self, Self::Error> {
match value {
KeyScope::Zip32(scope) => Ok(scope),
KeyScope::Ephemeral => Err(()),
}
}
}

pub(crate) fn encode_diversifier_index_be(idx: DiversifierIndex) -> [u8; 11] {
let mut di_be = *idx.as_bytes();
di_be.reverse();
di_be
}

pub(crate) fn decode_diversifier_index_be(
di_be: &[u8],
) -> Result<DiversifierIndex, SqliteClientError> {
let mut di_be: [u8; 11] = di_be.try_into().map_err(|_| {
SqliteClientError::CorruptedData("Diversifier index is not an 11-byte value".to_owned())
})?;
di_be.reverse();

Ok(DiversifierIndex::from(di_be))
}

pub(crate) fn memo_repr(memo: Option<&MemoBytes>) -> Option<&[u8]> {
memo.map(|m| {
if m == &MemoBytes::empty() {
// we store the empty memo as a single 0xf6 byte
&[0xf6]
} else {
m.as_slice()
}
})
}

// Returns the highest used account index for a given seed.
pub(crate) fn max_zip32_account_index(
conn: &rusqlite::Connection,
Expand Down
122 changes: 120 additions & 2 deletions zcash_client_sqlite/src/wallet/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,134 @@
//! database.
use bitflags::bitflags;
use transparent::address::TransparentAddress::*;
use transparent::{address::TransparentAddress::*, keys::TransparentKeyScope};
use zcash_address::{
unified::{Container, Receiver},
ConversionError, TryFromAddress,
};
use zcash_client_backend::data_api::AccountSource;
use zcash_keys::{
address::{Address, UnifiedAddress},
keys::{ReceiverRequirement, UnifiedAddressRequest},
};
use zcash_protocol::consensus::NetworkType;
use zcash_protocol::{consensus::NetworkType, memo::MemoBytes, PoolType, ShieldedProtocol};
use zip32::DiversifierIndex;

use crate::error::SqliteClientError;

pub(crate) fn pool_code(pool_type: PoolType) -> i64 {
// These constants are *incidentally* shared with the typecodes
// for unified addresses, but this is exclusively an internal
// implementation detail.
match pool_type {
PoolType::Transparent => 0i64,
PoolType::Shielded(ShieldedProtocol::Sapling) => 2i64,
PoolType::Shielded(ShieldedProtocol::Orchard) => 3i64,
}
}

pub(crate) fn account_kind_code(value: &AccountSource) -> u32 {
match value {
AccountSource::Derived { .. } => 0,
AccountSource::Imported { .. } => 1,
}
}

pub(crate) fn encode_diversifier_index_be(idx: DiversifierIndex) -> [u8; 11] {
let mut di_be = *idx.as_bytes();
di_be.reverse();
di_be
}

pub(crate) fn decode_diversifier_index_be(
di_be: &[u8],
) -> Result<DiversifierIndex, SqliteClientError> {
let mut di_be: [u8; 11] = di_be.try_into().map_err(|_| {
SqliteClientError::CorruptedData("Diversifier index is not an 11-byte value".to_owned())
})?;
di_be.reverse();

Ok(DiversifierIndex::from(di_be))
}

pub(crate) fn memo_repr(memo: Option<&MemoBytes>) -> Option<&[u8]> {
memo.map(|m| {
if m == &MemoBytes::empty() {
// we store the empty memo as a single 0xf6 byte
&[0xf6]
} else {
m.as_slice()
}
})
}

/// An enumeration of the scopes of keys that are generated by the `zcash_client_sqlite`
/// implementation of the `WalletWrite` trait.
///
/// This extends the [`zip32::Scope`] type to include the custom scope used to generate keys for
/// ephemeral transparent addresses.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum KeyScope {
/// A key scope corresponding to a [`zip32::Scope`].
Zip32(zip32::Scope),
/// An ephemeral transparent address, which is derived from an account's transparent
/// [`AccountPubKey`] with the BIP 44 path `change` level index set to the value `2`.
///
/// [`AccountPubKey`]: zcash_primitives::legacy::keys::AccountPubKey
Ephemeral,
}

impl KeyScope {
pub(crate) const EXTERNAL: KeyScope = KeyScope::Zip32(zip32::Scope::External);
pub(crate) const INTERNAL: KeyScope = KeyScope::Zip32(zip32::Scope::Internal);

pub(crate) fn encode(&self) -> i64 {
match self {
KeyScope::Zip32(zip32::Scope::External) => 0i64,
KeyScope::Zip32(zip32::Scope::Internal) => 1i64,
KeyScope::Ephemeral => 2i64,
}
}

pub(crate) fn decode(code: i64) -> Result<Self, SqliteClientError> {
match code {
0i64 => Ok(KeyScope::EXTERNAL),
1i64 => Ok(KeyScope::INTERNAL),
2i64 => Ok(KeyScope::Ephemeral),
other => Err(SqliteClientError::CorruptedData(format!(
"Invalid key scope code: {}",
other
))),
}
}
}

impl From<zip32::Scope> for KeyScope {
fn from(value: zip32::Scope) -> Self {
KeyScope::Zip32(value)
}
}

#[cfg(feature = "transparent-inputs")]
impl From<KeyScope> for TransparentKeyScope {
fn from(value: KeyScope) -> Self {
match value {
KeyScope::Zip32(scope) => scope.into(),
KeyScope::Ephemeral => TransparentKeyScope::custom(2).expect("valid scope"),
}
}
}

impl TryFrom<KeyScope> for zip32::Scope {
type Error = ();

fn try_from(value: KeyScope) -> Result<Self, Self::Error> {
match value {
KeyScope::Zip32(scope) => Ok(scope),
KeyScope::Ephemeral => Err(()),
}
}
}

bitflags! {
/// A set of flags describing the type(s) of outputs that a Zcash address can receive.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn get_transparent_receivers<P: consensus::Parameters>(
params: &P,
account: AccountId,
) -> Result<HashMap<TransparentAddress, Option<TransparentAddressMetadata>>, SqliteClientError> {
use crate::wallet::decode_diversifier_index_be;
use crate::wallet::encoding::decode_diversifier_index_be;

let mut ret: HashMap<TransparentAddress, Option<TransparentAddressMetadata>> = HashMap::new();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
use {
crate::{
wallet::{
decode_diversifier_index_be, encode_diversifier_index_be,
encoding::{decode_diversifier_index_be, encode_diversifier_index_be},
transparent::generate_gap_addresses,
},
GapLimits,
Expand Down
5 changes: 3 additions & 2 deletions zcash_client_sqlite/src/wallet/transparent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ use zip32::Scope;

use super::encoding::ReceiverFlags;
use super::{
account_birthday_internal, chain_tip_height, decode_diversifier_index_be,
encode_diversifier_index_be, get_account_ids, get_account_internal, KeyScope,
account_birthday_internal, chain_tip_height,
encoding::{decode_diversifier_index_be, encode_diversifier_index_be},
get_account_ids, get_account_internal, KeyScope,
};
use crate::{error::SqliteClientError, AccountUuid, TxRef, UtxoId};
use crate::{AccountRef, AddressRef, GapLimits};
Expand Down

0 comments on commit 96f5426

Please sign in to comment.