Skip to main content

zcash_address/kind/unified/
address.rs

1use zcash_protocol::{PoolType, constants};
2
3use super::{ParseError, Typecode, private::SealedItem};
4
5use alloc::vec::Vec;
6use core::convert::{TryFrom, TryInto};
7
8/// The set of known Receivers for Unified Addresses.
9///
10/// Defined in [ZIP 316](https://zips.z.cash/zip-0316).
11#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12pub enum Receiver {
13    Orchard([u8; 43]),
14    Sapling([u8; 43]),
15    P2pkh([u8; 20]),
16    P2sh([u8; 20]),
17    Unknown { typecode: u32, data: Vec<u8> },
18}
19
20impl TryFrom<(u32, &[u8])> for Receiver {
21    type Error = ParseError;
22
23    fn try_from((typecode, addr): (u32, &[u8])) -> Result<Self, Self::Error> {
24        match typecode.try_into()? {
25            Typecode::P2pkh => addr.try_into().map(Receiver::P2pkh),
26            Typecode::P2sh => addr.try_into().map(Receiver::P2sh),
27            Typecode::Sapling => addr.try_into().map(Receiver::Sapling),
28            Typecode::Orchard => addr.try_into().map(Receiver::Orchard),
29            // Preserve unknown typecodes for forward compatibility.
30            Typecode::Unknown(_) => Ok(Receiver::Unknown {
31                typecode,
32                data: addr.to_vec(),
33            }),
34        }
35        .map_err(|e| {
36            ParseError::InvalidEncoding(format!("Invalid address for typecode {}: {}", typecode, e))
37        })
38    }
39}
40
41impl SealedItem for Receiver {
42    fn typecode(&self) -> Typecode {
43        match self {
44            Receiver::P2pkh(_) => Typecode::P2pkh,
45            Receiver::P2sh(_) => Typecode::P2sh,
46            Receiver::Sapling(_) => Typecode::Sapling,
47            Receiver::Orchard(_) => Typecode::Orchard,
48            Receiver::Unknown { typecode, .. } => Typecode::Unknown(*typecode),
49        }
50    }
51
52    fn data(&self) -> &[u8] {
53        match self {
54            Receiver::P2pkh(data) => data,
55            Receiver::P2sh(data) => data,
56            Receiver::Sapling(data) => data,
57            Receiver::Orchard(data) => data,
58            Receiver::Unknown { data, .. } => data,
59        }
60    }
61}
62
63/// A Unified Address.
64///
65/// # Examples
66///
67/// ```
68/// # use core::convert::Infallible;
69/// # use zcash_protocol::consensus::NetworkType;
70/// use zcash_address::{
71///     unified::{self, Container, Encoding},
72///     ConversionError, TryFromAddress, ZcashAddress,
73/// };
74///
75/// # #[cfg(not(feature = "std"))]
76/// # fn main() {}
77/// # #[cfg(feature = "std")]
78/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
79/// # let address_from_user = || "u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx";
80/// let example_ua: &str = address_from_user();
81///
82/// // We can parse this directly as a `unified::Address`:
83/// let (network, ua) = unified::Address::decode(example_ua)?;
84///
85/// // Or we can parse via `ZcashAddress` (which you should do):
86/// struct MyUnifiedAddress(unified::Address);
87/// impl TryFromAddress for MyUnifiedAddress {
88///     // In this example we aren't checking the validity of the
89///     // inner Unified Address, but your code should do so!
90///     type Error = Infallible;
91///
92///     fn try_from_unified(
93///         _net: NetworkType,
94///         ua: unified::Address
95///     ) -> Result<Self, ConversionError<Self::Error>> {
96///         Ok(MyUnifiedAddress(ua))
97///     }
98/// }
99/// let addr: ZcashAddress = example_ua.parse()?;
100/// let parsed = addr.convert_if_network::<MyUnifiedAddress>(network)?;
101/// assert_eq!(parsed.0, ua);
102///
103/// // We can obtain the receivers for the UA in preference order
104/// // (the order in which wallets should prefer to use them):
105/// let receivers: Vec<unified::Receiver> = ua.items();
106///
107/// // And we can create the UA from a list of receivers:
108/// let new_ua = unified::Address::try_from_items(receivers)?;
109/// assert_eq!(new_ua, ua);
110/// # Ok(())
111/// # }
112/// ```
113#[derive(Clone, Debug, PartialEq, Eq, Hash)]
114pub struct Address(pub(crate) Vec<Receiver>);
115
116impl Address {
117    /// Returns whether this address has the ability to receive transfers of the given pool type.
118    pub fn has_receiver_of_type(&self, pool_type: PoolType) -> bool {
119        self.0.iter().any(|r| match r {
120            Receiver::Orchard(_) => pool_type == PoolType::ORCHARD,
121            Receiver::Sapling(_) => pool_type == PoolType::SAPLING,
122            Receiver::P2pkh(_) | Receiver::P2sh(_) => pool_type == PoolType::TRANSPARENT,
123            Receiver::Unknown { .. } => false,
124        })
125    }
126
127    /// Returns whether this address contains the given receiver.
128    pub fn contains_receiver(&self, receiver: &Receiver) -> bool {
129        self.0.contains(receiver)
130    }
131
132    /// Returns whether this address can receive a memo.
133    pub fn can_receive_memo(&self) -> bool {
134        self.0
135            .iter()
136            .any(|r| matches!(r, Receiver::Sapling(_) | Receiver::Orchard(_)))
137    }
138}
139
140impl super::private::SealedContainer for Address {
141    /// The HRP for a Bech32m-encoded mainnet Unified Address.
142    ///
143    /// Defined in [ZIP 316][zip-0316].
144    ///
145    /// [zip-0316]: https://zips.z.cash/zip-0316
146    const MAINNET: &'static str = constants::mainnet::HRP_UNIFIED_ADDRESS;
147
148    /// The HRP for a Bech32m-encoded testnet Unified Address.
149    ///
150    /// Defined in [ZIP 316][zip-0316].
151    ///
152    /// [zip-0316]: https://zips.z.cash/zip-0316
153    const TESTNET: &'static str = constants::testnet::HRP_UNIFIED_ADDRESS;
154
155    /// The HRP for a Bech32m-encoded regtest Unified Address.
156    const REGTEST: &'static str = constants::regtest::HRP_UNIFIED_ADDRESS;
157
158    fn from_inner(receivers: Vec<Self::Item>) -> Self {
159        Self(receivers)
160    }
161}
162
163impl super::Encoding for Address {}
164impl super::Container for Address {
165    type Item = Receiver;
166
167    fn items_as_parsed(&self) -> &[Receiver] {
168        &self.0
169    }
170}
171
172#[cfg(any(test, feature = "test-dependencies"))]
173pub mod testing {
174    use alloc::vec::Vec;
175
176    use proptest::{
177        array::{uniform11, uniform20, uniform32},
178        collection::vec,
179        prelude::*,
180        sample::select,
181        strategy::Strategy,
182    };
183    use zcash_encoding::MAX_COMPACT_SIZE;
184
185    use super::{Address, Receiver};
186    use crate::unified::Typecode;
187
188    prop_compose! {
189        fn uniform43()(a in uniform11(0u8..), b in uniform32(0u8..)) -> [u8; 43] {
190            let mut c = [0; 43];
191            c[..11].copy_from_slice(&a);
192            c[11..].copy_from_slice(&b);
193            c
194        }
195    }
196
197    /// A strategy to generate an arbitrary transparent typecode.
198    pub fn arb_transparent_typecode() -> impl Strategy<Value = Typecode> {
199        select(vec![Typecode::P2pkh, Typecode::P2sh])
200    }
201
202    /// A strategy to generate an arbitrary shielded (Sapling, Orchard, or unknown) typecode.
203    pub fn arb_shielded_typecode() -> impl Strategy<Value = Typecode> {
204        prop_oneof![
205            Just(Typecode::Sapling),
206            Just(Typecode::Orchard),
207            ((<u32>::from(Typecode::Orchard) + 1)..MAX_COMPACT_SIZE).prop_map(Typecode::Unknown)
208        ]
209    }
210
211    /// A strategy to generate an arbitrary valid set of typecodes without
212    /// duplication and containing only one of P2sh and P2pkh transparent
213    /// typecodes. The resulting vector will be sorted in encoding order.
214    pub fn arb_typecodes() -> impl Strategy<Value = Vec<Typecode>> {
215        prop::option::of(arb_transparent_typecode()).prop_flat_map(|transparent| {
216            prop::collection::hash_set(arb_shielded_typecode(), 1..4).prop_map(move |xs| {
217                let mut typecodes: Vec<_> = xs.into_iter().chain(transparent).collect();
218                typecodes.sort_unstable_by(Typecode::encoding_order);
219                typecodes
220            })
221        })
222    }
223
224    /// Generates an arbitrary Unified address containing receivers corresponding to the provided
225    /// set of typecodes. The receivers of this address are likely to not represent valid protocol
226    /// receivers, and should only be used for testing parsing and/or encoding functions that do
227    /// not concern themselves with the validity of the underlying receivers.
228    pub fn arb_unified_address_for_typecodes(
229        typecodes: Vec<Typecode>,
230    ) -> impl Strategy<Value = Vec<Receiver>> {
231        typecodes
232            .into_iter()
233            .map(|tc| match tc {
234                Typecode::P2pkh => uniform20(0u8..).prop_map(Receiver::P2pkh).boxed(),
235                Typecode::P2sh => uniform20(0u8..).prop_map(Receiver::P2sh).boxed(),
236                Typecode::Sapling => uniform43().prop_map(Receiver::Sapling).boxed(),
237                Typecode::Orchard => uniform43().prop_map(Receiver::Orchard).boxed(),
238                Typecode::Unknown(typecode) => vec(any::<u8>(), 32..256)
239                    .prop_map(move |data| Receiver::Unknown { typecode, data })
240                    .boxed(),
241            })
242            .collect::<Vec<_>>()
243    }
244
245    /// Generates an arbitrary Unified address. The receivers of this address are likely to not
246    /// represent valid protocol receivers, and should only be used for testing parsing and/or
247    /// encoding functions that do not concern themselves with the validity of the underlying
248    /// receivers.
249    pub fn arb_unified_address() -> impl Strategy<Value = Address> {
250        arb_typecodes()
251            .prop_flat_map(arb_unified_address_for_typecodes)
252            .prop_map(Address)
253    }
254}
255
256#[cfg(any(test, feature = "test-dependencies"))]
257pub mod test_vectors;
258
259#[cfg(test)]
260mod tests {
261    use alloc::borrow::ToOwned;
262
263    use assert_matches::assert_matches;
264    use zcash_protocol::consensus::NetworkType;
265
266    use crate::{
267        kind::unified::{Container, Encoding, private::SealedContainer},
268        unified::address::testing::arb_unified_address,
269    };
270
271    use proptest::{prelude::*, sample::select};
272
273    use super::{Address, ParseError, Receiver, Typecode};
274
275    proptest! {
276        #[test]
277        fn ua_roundtrip(
278            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
279            ua in arb_unified_address(),
280        ) {
281            let encoded = ua.encode(&network);
282            let decoded = Address::decode(&encoded);
283            prop_assert_eq!(&decoded, &Ok((network, ua)));
284            let reencoded = decoded.unwrap().1.encode(&network);
285            prop_assert_eq!(reencoded, encoded);
286        }
287    }
288
289    #[test]
290    fn padding() {
291        // The test cases below use `Address(vec![Receiver::Orchard([1; 43])])` as base.
292
293        // Invalid padding ([0xff; 16] instead of [0x75, 0x00, 0x00, 0x00...])
294        let invalid_padding = [
295            0xe6, 0x59, 0xd1, 0xed, 0xf7, 0x4b, 0xe3, 0x5e, 0x5a, 0x54, 0x0e, 0x41, 0x5d, 0x2f,
296            0x0c, 0x0d, 0x33, 0x42, 0xbd, 0xbe, 0x9f, 0x82, 0x62, 0x01, 0xc1, 0x1b, 0xd4, 0x1e,
297            0x42, 0x47, 0x86, 0x23, 0x05, 0x4b, 0x98, 0xd7, 0x76, 0x86, 0xa5, 0xe3, 0x1b, 0xd3,
298            0x03, 0xca, 0x24, 0x44, 0x8e, 0x72, 0xc1, 0x4a, 0xc6, 0xbf, 0x3f, 0x2b, 0xce, 0xa7,
299            0x7b, 0x28, 0x69, 0xc9, 0x84,
300        ];
301        assert_eq!(
302            Address::parse_internal(Address::MAINNET, &invalid_padding[..]),
303            Err(ParseError::InvalidEncoding(
304                "Invalid padding bytes".to_owned()
305            ))
306        );
307
308        // Short padding (padded to 15 bytes instead of 16)
309        let truncated_padding = [
310            0x9a, 0x56, 0x12, 0xa3, 0x43, 0x45, 0xe0, 0x82, 0x6c, 0xac, 0x24, 0x8b, 0x3b, 0x45,
311            0x72, 0x9a, 0x53, 0xd5, 0xf8, 0xda, 0xec, 0x07, 0x7c, 0xba, 0x9f, 0xa8, 0xd2, 0x97,
312            0x5b, 0xda, 0x73, 0x1b, 0xd2, 0xd1, 0x32, 0x6b, 0x7b, 0x36, 0xdd, 0x57, 0x84, 0x2a,
313            0xa0, 0x21, 0x23, 0x89, 0x73, 0x85, 0xe1, 0x4b, 0x3e, 0x95, 0xb7, 0xd4, 0x67, 0xbc,
314            0x4b, 0x31, 0xee, 0x5a,
315        ];
316        assert_eq!(
317            Address::parse_internal(Address::MAINNET, &truncated_padding[..]),
318            Err(ParseError::InvalidEncoding(
319                "Invalid padding bytes".to_owned()
320            ))
321        );
322    }
323
324    #[test]
325    fn truncated() {
326        // The test cases below start from an encoding of
327        //     `Address(vec![Receiver::Orchard([1; 43]), Receiver::Sapling([2; 43])])`
328        // with the receiver data truncated, but valid padding.
329
330        // - Missing the last data byte of the Sapling receiver.
331        let truncated_sapling_data = [
332            0xaa, 0xb0, 0x6e, 0x7b, 0x26, 0x7a, 0x22, 0x17, 0x39, 0xfa, 0x07, 0x69, 0xe9, 0x32,
333            0x2b, 0xac, 0x8c, 0x9e, 0x5e, 0x8a, 0xd9, 0x24, 0x06, 0x5a, 0x13, 0x79, 0x3a, 0x8d,
334            0xb4, 0x52, 0xfa, 0x18, 0x4e, 0x33, 0x4d, 0x8c, 0x17, 0x77, 0x4d, 0x63, 0x69, 0x34,
335            0x22, 0x70, 0x3a, 0xea, 0x30, 0x82, 0x5a, 0x6b, 0x37, 0xd1, 0x0d, 0xbe, 0x20, 0xab,
336            0x82, 0x86, 0x98, 0x34, 0x6a, 0xd8, 0x45, 0x40, 0xd0, 0x25, 0x60, 0xbf, 0x1e, 0xb6,
337            0xeb, 0x06, 0x85, 0x70, 0x4c, 0x42, 0xbc, 0x19, 0x14, 0xef, 0x7a, 0x05, 0xa0, 0x71,
338            0xb2, 0x63, 0x80, 0xbb, 0xdc, 0x12, 0x08, 0x48, 0x28, 0x8f, 0x1c, 0x9e, 0xc3, 0x42,
339            0xc6, 0x5e, 0x68, 0xa2, 0x78, 0x6c, 0x9e,
340        ];
341        assert_matches!(
342            Address::parse_internal(Address::MAINNET, &truncated_sapling_data[..]),
343            Err(ParseError::InvalidEncoding(_))
344        );
345
346        // - Truncated after the typecode of the Sapling receiver.
347        let truncated_after_sapling_typecode = [
348            0x87, 0x7a, 0xdf, 0x79, 0x6b, 0xe3, 0xb3, 0x40, 0xef, 0xe4, 0x5d, 0xc2, 0x91, 0xa2,
349            0x81, 0xfc, 0x7d, 0x76, 0xbb, 0xb0, 0x58, 0x98, 0x53, 0x59, 0xd3, 0x3f, 0xbc, 0x4b,
350            0x86, 0x59, 0x66, 0x62, 0x75, 0x92, 0xba, 0xcc, 0x31, 0x1e, 0x60, 0x02, 0x3b, 0xd8,
351            0x4c, 0xdf, 0x36, 0xa1, 0xac, 0x82, 0x57, 0xed, 0x0c, 0x98, 0x49, 0x8f, 0x49, 0x7e,
352            0xe6, 0x70, 0x36, 0x5b, 0x7b, 0x9e,
353        ];
354        assert_matches!(
355            Address::parse_internal(Address::MAINNET, &truncated_after_sapling_typecode[..]),
356            Err(ParseError::InvalidEncoding(_))
357        );
358    }
359
360    #[test]
361    fn duplicate_typecode() {
362        // Construct and serialize an invalid UA. This must be done using private
363        // methods, as the public API does not permit construction of such invalid values.
364        let ua = Address(vec![Receiver::Sapling([1; 43]), Receiver::Sapling([2; 43])]);
365        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
366        assert_eq!(
367            Address::parse_internal(Address::MAINNET, &encoded[..]),
368            Err(ParseError::DuplicateTypecode(Typecode::Sapling))
369        );
370    }
371
372    #[test]
373    fn p2pkh_and_p2sh() {
374        // Construct and serialize an invalid UA. This must be done using private
375        // methods, as the public API does not permit construction of such invalid values.
376        let ua = Address(vec![Receiver::P2pkh([0; 20]), Receiver::P2sh([0; 20])]);
377        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
378        // ensure that decoding catches the error
379        assert_eq!(
380            Address::parse_internal(Address::MAINNET, &encoded[..]),
381            Err(ParseError::BothP2phkAndP2sh)
382        );
383    }
384
385    #[test]
386    fn addresses_out_of_order() {
387        // Construct and serialize an invalid UA. This must be done using private
388        // methods, as the public API does not permit construction of such invalid values.
389        let ua = Address(vec![Receiver::Sapling([0; 43]), Receiver::P2pkh([0; 20])]);
390        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
391        // ensure that decoding catches the error
392        assert_eq!(
393            Address::parse_internal(Address::MAINNET, &encoded[..]),
394            Err(ParseError::InvalidTypecodeOrder)
395        );
396    }
397
398    #[test]
399    fn only_transparent() {
400        // Encoding of `Address(vec![Receiver::P2pkh([0; 20])])`.
401        let encoded = [
402            0xf0, 0x9e, 0x9d, 0x6e, 0xf5, 0xa6, 0xac, 0x16, 0x50, 0xf0, 0xdb, 0xe1, 0x2c, 0xa5,
403            0x36, 0x22, 0xa2, 0x04, 0x89, 0x86, 0xe9, 0x6a, 0x9b, 0xf3, 0xff, 0x6d, 0x2f, 0xe6,
404            0xea, 0xdb, 0xc5, 0x20, 0x62, 0xf9, 0x6f, 0xa9, 0x86, 0xcc,
405        ];
406
407        // We can't actually exercise this error, because at present the only transparent
408        // receivers we can use are P2PKH and P2SH (which cannot be used together), and
409        // with only one of them we don't have sufficient data for F4Jumble (so we hit a
410        // different error).
411        assert_matches!(
412            Address::parse_internal(Address::MAINNET, &encoded[..]),
413            Err(ParseError::InvalidEncoding(_))
414        );
415    }
416
417    #[test]
418    fn receivers_are_sorted() {
419        // Construct a UA with receivers in an unsorted order.
420        let ua = Address(vec![
421            Receiver::P2pkh([0; 20]),
422            Receiver::Orchard([0; 43]),
423            Receiver::Unknown {
424                typecode: 0xff,
425                data: vec![],
426            },
427            Receiver::Sapling([0; 43]),
428        ]);
429
430        // `Address::receivers` sorts the receivers in priority order.
431        assert_eq!(
432            ua.items(),
433            vec![
434                Receiver::Orchard([0; 43]),
435                Receiver::Sapling([0; 43]),
436                Receiver::P2pkh([0; 20]),
437                Receiver::Unknown {
438                    typecode: 0xff,
439                    data: vec![],
440                },
441            ]
442        )
443    }
444
445    #[test]
446    fn address_receiver_queries() {
447        use zcash_protocol::PoolType;
448
449        // A UA with all receiver types
450        let ua = Address(vec![
451            Receiver::Orchard([0; 43]),
452            Receiver::Sapling([1; 43]),
453            Receiver::P2pkh([2; 20]),
454        ]);
455
456        // has_receiver_of_type
457        assert!(ua.has_receiver_of_type(PoolType::ORCHARD));
458        assert!(ua.has_receiver_of_type(PoolType::SAPLING));
459        assert!(ua.has_receiver_of_type(PoolType::TRANSPARENT));
460
461        // can_receive_memo: true only when Sapling or Orchard is present
462        assert!(ua.can_receive_memo());
463
464        // contains_receiver: exact data match required
465        assert!(ua.contains_receiver(&Receiver::Orchard([0; 43])));
466        assert!(!ua.contains_receiver(&Receiver::Orchard([1; 43]))); // same type, different data
467
468        // Transparent-only address cannot receive memos
469        let transparent_only = Address(vec![Receiver::P2pkh([0; 20])]);
470        assert!(!transparent_only.can_receive_memo());
471        assert!(!transparent_only.has_receiver_of_type(PoolType::ORCHARD));
472        assert!(!transparent_only.has_receiver_of_type(PoolType::SAPLING));
473        assert!(transparent_only.has_receiver_of_type(PoolType::TRANSPARENT));
474
475        // Unknown receiver does not count for any pool type
476        let with_unknown = Address(vec![
477            Receiver::Orchard([0; 43]),
478            Receiver::Unknown {
479                typecode: 0xAA,
480                data: vec![0; 32],
481            },
482        ]);
483        assert!(!with_unknown.has_receiver_of_type(PoolType::SAPLING));
484        // Unknown receiver is NOT counted as transparent
485        assert!(!with_unknown.has_receiver_of_type(PoolType::TRANSPARENT));
486    }
487}