zcash_address/kind/unified/
address.rs

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