1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//! *Parser for all defined Zcash address types.*
//!
//! This crate implements address parsing as a two-phase process, built around the opaque
//! [`ZcashAddress`] type.
//!
//! - [`ZcashAddress`] can be parsed from, and encoded to, strings.
//! - [`ZcashAddress::convert`] or [`ZcashAddress::convert_if_network`] can be used to
//!   convert a parsed address into custom types that implement the [`TryFromAddress`] or
//!   [`TryFromRawAddress`] traits.
//! - Custom types can be converted into a [`ZcashAddress`] via its implementation of the
//!   [`ToAddress`] trait.
//!
//! ```text
//!         s.parse()              .convert()
//!         -------->              --------->
//! Strings           ZcashAddress            Custom types
//!         <--------              <---------
//!         .encode()              ToAddress
//! ```
//!
//! It is important to note that this crate does not depend on any of the Zcash protocol
//! crates (e.g. `sapling-crypto` or `orchard`). This crate has minimal dependencies by
//! design; it focuses solely on parsing, handling those concerns for you, while exposing
//! APIs that enable you to convert the parsed data into the Rust types you want to use.
//!
//! # Using this crate
//!
//! ## I just need to validate Zcash addresses
//!
//! ```
//! # use zcash_address::ZcashAddress;
//! fn is_valid_zcash_address(addr_string: &str) -> bool {
//!     addr_string.parse::<ZcashAddress>().is_ok()
//! }
//! ```
//!
//! ## I want to parse Zcash addresses in a Rust wallet app that uses the `zcash_primitives` transaction builder
//!
//! Use `zcash_client_backend::address::RecipientAddress`, which implements the traits in
//! this crate to parse address strings into protocol types that work with the transaction
//! builder in the `zcash_primitives` crate (as well as the wallet functionality in the
//! `zcash_client_backend` crate itself).
//!
//! > We intend to refactor the key and address types from the `zcash_client_backend` and
//! > `zcash_primitives` crates into a separate crate focused on dealing with Zcash key
//! > material. That crate will then be what you should use.
//!
//! ## I want to parse Unified Addresses
//!
//! See the [`unified::Address`] documentation for examples.
//!
//! While the [`unified::Address`] type does have parsing methods, you should still parse
//! your address strings with [`ZcashAddress`] and then convert; this will ensure that for
//! other Zcash address types you get a [`ConversionError::Unsupported`], which is a
//! better error for your users.
//!
//! ## I want to parse mainnet Zcash addresses in a language that supports C FFI
//!
//! As an example, you could use static functions to create the address types in the
//! target language from the parsed data.
//!
//! ```
//! use std::ffi::{CStr, c_char, c_void};
//! use std::ptr;
//!
//! use zcash_address::{ConversionError, Network, TryFromRawAddress, ZcashAddress};
//!
//! // Functions that return a pointer to a heap-allocated address of the given kind in
//! // the target language. These should be augmented to return any relevant errors.
//! extern {
//!     fn addr_from_sapling(data: *const u8) -> *mut c_void;
//!     fn addr_from_transparent_p2pkh(data: *const u8) -> *mut c_void;
//! }
//!
//! struct ParsedAddress(*mut c_void);
//!
//! impl TryFromRawAddress for ParsedAddress {
//!     type Error = &'static str;
//!
//!     fn try_from_raw_sapling(
//!         data: [u8; 43],
//!     ) -> Result<Self, ConversionError<Self::Error>> {
//!         let parsed = unsafe { addr_from_sapling(data[..].as_ptr()) };
//!         if parsed.is_null() {
//!             Err("Reason for the failure".into())
//!         } else {
//!             Ok(Self(parsed))
//!         }
//!     }
//!
//!     fn try_from_raw_transparent_p2pkh(
//!         data: [u8; 20],
//!     ) -> Result<Self, ConversionError<Self::Error>> {
//!         let parsed = unsafe { addr_from_transparent_p2pkh(data[..].as_ptr()) };
//!         if parsed.is_null() {
//!             Err("Reason for the failure".into())
//!         } else {
//!             Ok(Self(parsed))
//!         }
//!     }
//! }
//!
//! pub extern "C" fn parse_zcash_address(encoded: *const c_char) -> *mut c_void {
//!     let encoded = unsafe { CStr::from_ptr(encoded) }.to_str().expect("valid");
//!
//!     let addr = match ZcashAddress::try_from_encoded(encoded) {
//!         Ok(addr) => addr,
//!         Err(e) => {
//!             // This was either an invalid address encoding, or not a Zcash address.
//!             // You should pass this error back across the FFI.
//!             return ptr::null_mut();
//!         }
//!     };
//!
//!     match addr.convert_if_network::<ParsedAddress>(Network::Main) {
//!         Ok(parsed) => parsed.0,
//!         Err(e) => {
//!             // We didn't implement all of the methods of `TryFromRawAddress`, so if an
//!             // address with one of those kinds is parsed, it will result in an error
//!             // here that should be passed back across the FFI.
//!             ptr::null_mut()
//!         }
//!     }
//! }
//! ```

#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// Catch documentation errors caused by code changes.
#![deny(rustdoc::broken_intra_doc_links)]

mod convert;
mod encoding;
mod kind;

#[cfg(any(test, feature = "test-dependencies"))]
pub mod test_vectors;

pub use convert::{
    ConversionError, ToAddress, TryFromAddress, TryFromRawAddress, UnsupportedAddress,
};
pub use encoding::ParseError;
pub use kind::unified;
pub use zcash_protocol::consensus::NetworkType as Network;

/// A Zcash address.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ZcashAddress {
    net: Network,
    kind: AddressKind,
}

/// Known kinds of Zcash addresses.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum AddressKind {
    Sprout([u8; 64]),
    Sapling([u8; 43]),
    Unified(unified::Address),
    P2pkh([u8; 20]),
    P2sh([u8; 20]),
    Tex([u8; 20]),
}

impl ZcashAddress {
    /// Encodes this Zcash address in its canonical string representation.
    ///
    /// This provides the encoded string representation of the address as defined by the
    /// [Zcash protocol specification](https://zips.z.cash/protocol.pdf) and/or
    /// [ZIP 316](https://zips.z.cash/zip-0316). The [`Display` implementation] can also
    /// be used to produce this encoding using [`address.to_string()`].
    ///
    /// [`Display` implementation]: std::fmt::Display
    /// [`address.to_string()`]: std::string::ToString
    pub fn encode(&self) -> String {
        format!("{}", self)
    }

    /// Attempts to parse the given string as a Zcash address.
    ///
    /// This simply calls [`s.parse()`], leveraging the [`FromStr` implementation].
    ///
    /// [`s.parse()`]: std::primitive::str::parse
    /// [`FromStr` implementation]: ZcashAddress#impl-FromStr
    ///
    /// # Errors
    ///
    /// - If the parser can detect that the string _must_ contain an address encoding used
    ///   by Zcash, [`ParseError::InvalidEncoding`] will be returned if any subsequent
    ///   part of that encoding is invalid.
    ///
    /// - In all other cases, [`ParseError::NotZcash`] will be returned on failure.
    ///
    /// # Examples
    ///
    /// ```
    /// use zcash_address::ZcashAddress;
    ///
    /// let encoded = "zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9sly";
    /// let addr = ZcashAddress::try_from_encoded(&encoded);
    /// assert_eq!(encoded.parse(), addr);
    /// ```
    pub fn try_from_encoded(s: &str) -> Result<Self, ParseError> {
        s.parse()
    }

    /// Converts this address into another type.
    ///
    /// `convert` can convert into any type that implements the [`TryFromAddress`] trait.
    /// This enables `ZcashAddress` to be used as a common parsing and serialization
    /// interface for Zcash addresses, while delegating operations on those addresses
    /// (such as constructing transactions) to downstream crates.
    ///
    /// If you want to get the encoded string for this address, use the [`encode`]
    /// method or the [`Display` implementation] via [`address.to_string()`] instead.
    ///
    /// [`encode`]: Self::encode
    /// [`Display` implementation]: std::fmt::Display
    /// [`address.to_string()`]: std::string::ToString
    pub fn convert<T: TryFromAddress>(self) -> Result<T, ConversionError<T::Error>> {
        match self.kind {
            AddressKind::Sprout(data) => T::try_from_sprout(self.net, data),
            AddressKind::Sapling(data) => T::try_from_sapling(self.net, data),
            AddressKind::Unified(data) => T::try_from_unified(self.net, data),
            AddressKind::P2pkh(data) => T::try_from_transparent_p2pkh(self.net, data),
            AddressKind::P2sh(data) => T::try_from_transparent_p2sh(self.net, data),
            AddressKind::Tex(data) => T::try_from_tex(self.net, data),
        }
    }

    /// Converts this address into another type, if it matches the expected network.
    ///
    /// `convert_if_network` can convert into any type that implements the
    /// [`TryFromRawAddress`] trait. This enables `ZcashAddress` to be used as a common
    /// parsing and serialization interface for Zcash addresses, while delegating
    /// operations on those addresses (such as constructing transactions) to downstream
    /// crates.
    ///
    /// If you want to get the encoded string for this address, use the [`encode`]
    /// method or the [`Display` implementation] via [`address.to_string()`] instead.
    ///
    /// [`encode`]: Self::encode
    /// [`Display` implementation]: std::fmt::Display
    /// [`address.to_string()`]: std::string::ToString
    pub fn convert_if_network<T: TryFromRawAddress>(
        self,
        net: Network,
    ) -> Result<T, ConversionError<T::Error>> {
        let network_matches = self.net == net;
        // The Sprout and transparent address encodings use the same prefix for testnet
        // and regtest, so we need to allow parsing testnet addresses as regtest.
        let regtest_exception =
            network_matches || (self.net == Network::Test && net == Network::Regtest);

        match self.kind {
            AddressKind::Sprout(data) if regtest_exception => T::try_from_raw_sprout(data),
            AddressKind::Sapling(data) if network_matches => T::try_from_raw_sapling(data),
            AddressKind::Unified(data) if network_matches => T::try_from_raw_unified(data),
            AddressKind::P2pkh(data) if regtest_exception => {
                T::try_from_raw_transparent_p2pkh(data)
            }
            AddressKind::P2sh(data) if regtest_exception => T::try_from_raw_transparent_p2sh(data),
            AddressKind::Tex(data) if network_matches => T::try_from_raw_tex(data),
            _ => Err(ConversionError::IncorrectNetwork {
                expected: net,
                actual: self.net,
            }),
        }
    }
}