Skip to main content

pepper_sync/keys/
transparent.rs

1//! Transparent keys and addresses
2
3use zcash_address::{ToAddress as _, ZcashAddress};
4use zcash_protocol::consensus;
5use zcash_transparent::address::TransparentAddress;
6use zcash_transparent::keys::{
7    AccountPubKey, IncomingViewingKey as _, NonHardenedChildIndex, TransparentKeyScope,
8};
9use zip32::AccountId;
10
11use crate::wallet::KeyIdInterface;
12
13/// Unique ID for transparent addresses.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
15pub struct TransparentAddressId {
16    account_id: AccountId,
17    scope: TransparentScope,
18    address_index: NonHardenedChildIndex,
19}
20
21impl TransparentAddressId {
22    /// Construct from parts
23    #[must_use]
24    pub fn new(
25        account_id: zip32::AccountId,
26        scope: TransparentScope,
27        address_index: NonHardenedChildIndex,
28    ) -> Self {
29        Self {
30            account_id,
31            scope,
32            address_index,
33        }
34    }
35
36    /// Gets address index
37    #[must_use]
38    pub fn address_index(&self) -> NonHardenedChildIndex {
39        self.address_index
40    }
41
42    /// Gets address scope
43    #[must_use]
44    pub fn scope(&self) -> TransparentScope {
45        self.scope
46    }
47}
48
49impl KeyIdInterface for TransparentAddressId {
50    fn account_id(&self) -> AccountId {
51        self.account_id
52    }
53}
54
55/// Child index for the `change` path level in the BIP44 hierarchy (a.k.a. scope/chain).
56#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
57pub enum TransparentScope {
58    /// External scope
59    External,
60    /// Internal scope (a.k.a. change)
61    Internal,
62    /// Refund scope (a.k.a. ephemeral)
63    Refund,
64}
65
66impl std::fmt::Display for TransparentScope {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        write!(
69            f,
70            "{}",
71            match self {
72                TransparentScope::External => "external",
73                TransparentScope::Internal => "internal",
74                TransparentScope::Refund => "refund",
75            }
76        )
77    }
78}
79
80impl From<TransparentScope> for TransparentKeyScope {
81    fn from(value: TransparentScope) -> Self {
82        match value {
83            TransparentScope::External => TransparentKeyScope::EXTERNAL,
84            TransparentScope::Internal => TransparentKeyScope::INTERNAL,
85            TransparentScope::Refund => TransparentKeyScope::EPHEMERAL,
86        }
87    }
88}
89
90impl TryFrom<u8> for TransparentScope {
91    type Error = std::io::Error;
92
93    fn try_from(value: u8) -> std::io::Result<Self> {
94        match value {
95            0 => Ok(TransparentScope::External),
96            1 => Ok(TransparentScope::Internal),
97            2 => Ok(TransparentScope::Refund),
98            _ => Err(std::io::Error::new(
99                std::io::ErrorKind::InvalidData,
100                "invalid scope value",
101            )),
102        }
103    }
104}
105
106pub(crate) fn derive_address(
107    consensus_parameters: &impl consensus::Parameters,
108    account_pubkey: &AccountPubKey,
109    address_id: TransparentAddressId,
110) -> Result<String, bip32::Error> {
111    let address = match address_id.scope() {
112        TransparentScope::External => {
113            derive_external_address(account_pubkey, address_id.address_index())?
114        }
115        TransparentScope::Internal => {
116            derive_internal_address(account_pubkey, address_id.address_index())?
117        }
118        TransparentScope::Refund => {
119            derive_refund_address(account_pubkey, address_id.address_index())?
120        }
121    };
122
123    Ok(encode_address(consensus_parameters, address))
124}
125
126fn derive_external_address(
127    account_pubkey: &AccountPubKey,
128    address_index: NonHardenedChildIndex,
129) -> Result<TransparentAddress, bip32::Error> {
130    account_pubkey
131        .derive_external_ivk()?
132        .derive_address(address_index)
133}
134
135fn derive_internal_address(
136    account_pubkey: &AccountPubKey,
137    address_index: NonHardenedChildIndex,
138) -> Result<TransparentAddress, bip32::Error> {
139    account_pubkey
140        .derive_internal_ivk()?
141        .derive_address(address_index)
142}
143
144fn derive_refund_address(
145    account_pubkey: &AccountPubKey,
146    address_index: NonHardenedChildIndex,
147) -> Result<TransparentAddress, bip32::Error> {
148    account_pubkey
149        .derive_ephemeral_ivk()?
150        .derive_ephemeral_address(address_index)
151}
152
153/// Encodes transparent address
154pub fn encode_address(
155    consensus_parameters: &impl consensus::Parameters,
156    address: TransparentAddress,
157) -> String {
158    let zcash_address = match address {
159        TransparentAddress::PublicKeyHash(data) => {
160            ZcashAddress::from_transparent_p2pkh(consensus_parameters.network_type(), data)
161        }
162        TransparentAddress::ScriptHash(data) => {
163            ZcashAddress::from_transparent_p2sh(consensus_parameters.network_type(), data)
164        }
165    };
166    zcash_address.to_string()
167}