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