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