pepper_sync/keys/
transparent.rs

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