Skip to main content

zcash_address/kind/unified/
address.rs

1use zcash_protocol::address::Revision;
2use zcash_protocol::{PoolType, constants};
3
4use super::{DataTypecode, ParseError, Uitem, private::SealedItem};
5
6use alloc::vec::Vec;
7use core::convert::TryInto;
8
9/// The set of known Receivers for Unified Addresses.
10///
11/// Defined in [ZIP 316](https://zips.z.cash/zip-0316).
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub enum Receiver {
14    Orchard([u8; 43]),
15    Sapling([u8; 43]),
16    P2pkh([u8; 20]),
17    P2sh([u8; 20]),
18    Unknown { typecode: u32, data: Vec<u8> },
19}
20
21impl SealedItem for Receiver {
22    fn parse(typecode: DataTypecode, addr: &[u8]) -> Result<Self, ParseError> {
23        match typecode {
24            DataTypecode::P2pkh => addr.try_into().map(Receiver::P2pkh),
25            DataTypecode::P2sh => addr.try_into().map(Receiver::P2sh),
26            DataTypecode::Sapling => addr.try_into().map(Receiver::Sapling),
27            DataTypecode::Orchard => addr.try_into().map(Receiver::Orchard),
28            // Preserve unknown typecodes for forward compatibility.
29            DataTypecode::Unknown(tc) => Ok(Receiver::Unknown {
30                typecode: tc,
31                data: addr.to_vec(),
32            }),
33        }
34        .map_err(|e| {
35            ParseError::InvalidEncoding(format!(
36                "Invalid address for typecode {}: {e}",
37                u32::from(typecode)
38            ))
39        })
40    }
41
42    fn typecode(&self) -> DataTypecode {
43        match self {
44            Receiver::P2pkh(_) => DataTypecode::P2pkh,
45            Receiver::P2sh(_) => DataTypecode::P2sh,
46            Receiver::Sapling(_) => DataTypecode::Sapling,
47            Receiver::Orchard(_) => DataTypecode::Orchard,
48            Receiver::Unknown { typecode, .. } => DataTypecode::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, _revision, 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(
109///     unified::Revision::R0,
110///     receivers.into_iter().map(unified::Uitem::Data).collect(),
111/// )?;
112/// assert_eq!(new_ua, ua);
113/// # Ok(())
114/// # }
115/// ```
116#[derive(Clone, Debug, PartialEq, Eq, Hash)]
117pub struct Address {
118    pub(crate) revision: Revision,
119    pub(crate) items: Vec<Uitem<Receiver>>,
120}
121
122impl Address {
123    /// Returns whether this address has the ability to receive transfers of the given pool type.
124    pub fn has_receiver_of_type(&self, pool_type: PoolType) -> bool {
125        self.items.iter().any(|item| match item {
126            Uitem::Data(Receiver::Orchard(_)) => pool_type == PoolType::ORCHARD,
127            Uitem::Data(Receiver::Sapling(_)) => pool_type == PoolType::SAPLING,
128            Uitem::Data(Receiver::P2pkh(_) | Receiver::P2sh(_)) => {
129                pool_type == PoolType::TRANSPARENT
130            }
131            Uitem::Data(Receiver::Unknown { .. }) => false,
132            Uitem::Metadata(_) => false,
133        })
134    }
135
136    /// Returns whether this address contains the given receiver.
137    pub fn contains_receiver(&self, receiver: &Receiver) -> bool {
138        self.items.iter().any(|item| match item {
139            Uitem::Data(r) => r == receiver,
140            Uitem::Metadata(_) => false,
141        })
142    }
143
144    /// Returns whether this address can receive a memo.
145    pub fn can_receive_memo(&self) -> bool {
146        self.items.iter().any(|item| {
147            matches!(
148                item,
149                Uitem::Data(Receiver::Sapling(_)) | Uitem::Data(Receiver::Orchard(_))
150            )
151        })
152    }
153}
154
155impl super::private::SealedContainer for Address {
156    const MAINNET: &'static str = constants::mainnet::HRP_UNIFIED_ADDRESS;
157    const TESTNET: &'static str = constants::testnet::HRP_UNIFIED_ADDRESS;
158    const REGTEST: &'static str = constants::regtest::HRP_UNIFIED_ADDRESS;
159
160    const MAINNET_R2: &'static str = constants::mainnet::HRP_UNIFIED_ADDRESS_R2;
161    const TESTNET_R2: &'static str = constants::testnet::HRP_UNIFIED_ADDRESS_R2;
162    const REGTEST_R2: &'static str = constants::regtest::HRP_UNIFIED_ADDRESS_R2;
163
164    const MAINNET_R2_TI: &'static str = constants::mainnet::HRP_UNIFIED_ADDRESS_R2_TI;
165    const TESTNET_R2_TI: &'static str = constants::testnet::HRP_UNIFIED_ADDRESS_R2_TI;
166    const REGTEST_R2_TI: &'static str = constants::regtest::HRP_UNIFIED_ADDRESS_R2_TI;
167
168    const IS_ADDRESS: bool = true;
169
170    fn from_inner(revision: Revision, items: Vec<Uitem<Receiver>>) -> Self {
171        Self { revision, items }
172    }
173}
174
175impl super::Encoding for Address {}
176impl super::Container for Address {
177    type Item = Receiver;
178
179    fn revision(&self) -> Revision {
180        self.revision
181    }
182
183    fn items_as_parsed(&self) -> &[Uitem<Receiver>] {
184        &self.items
185    }
186}
187
188#[cfg(any(test, feature = "test-dependencies"))]
189pub mod testing {
190    use alloc::collections::BTreeSet;
191    use alloc::vec::Vec;
192
193    use proptest::{
194        array::{uniform11, uniform20, uniform32},
195        collection::vec,
196        prelude::*,
197        sample::select,
198        strategy::Strategy,
199    };
200    use zcash_protocol::address::Revision;
201
202    use super::{Address, Receiver};
203    use crate::unified::{DataTypecode, MetadataItem, Uitem};
204
205    prop_compose! {
206        fn uniform43()(a in uniform11(0u8..), b in uniform32(0u8..)) -> [u8; 43] {
207            let mut c = [0; 43];
208            c[..11].copy_from_slice(&a);
209            c[11..].copy_from_slice(&b);
210            c
211        }
212    }
213
214    /// A strategy to generate an arbitrary transparent data typecode.
215    pub fn arb_transparent_typecode() -> impl Strategy<Value = DataTypecode> {
216        select(vec![DataTypecode::P2pkh, DataTypecode::P2sh])
217    }
218
219    /// A strategy to generate an arbitrary shielded (Sapling, Orchard, or unknown) data
220    /// typecode.
221    pub fn arb_shielded_typecode() -> impl Strategy<Value = DataTypecode> {
222        prop_oneof![
223            Just(DataTypecode::Sapling),
224            Just(DataTypecode::Orchard),
225            ((<u32>::from(DataTypecode::Orchard) + 1)..0xC0u32).prop_map(DataTypecode::Unknown)
226        ]
227    }
228
229    /// A strategy to generate an arbitrary valid set of data typecodes containing at
230    /// most one of the P2SH and P2PKH transparent typecodes.
231    pub fn arb_typecodes() -> impl Strategy<Value = BTreeSet<DataTypecode>> {
232        prop::option::of(arb_transparent_typecode()).prop_flat_map(|transparent| {
233            prop::collection::hash_set(arb_shielded_typecode(), 1..4)
234                .prop_map(move |xs| xs.into_iter().chain(transparent).collect())
235        })
236    }
237
238    /// Generates an arbitrary sequence of Unified address items containing receivers
239    /// corresponding to the provided set of typecodes, in canonical encoding order. The
240    /// receivers of this address are likely to not represent valid protocol receivers,
241    /// and should only be used for testing parsing and/or encoding functions that do not
242    /// concern themselves with the validity of the underlying receivers.
243    pub fn arb_unified_address_for_typecodes(
244        typecodes: BTreeSet<DataTypecode>,
245    ) -> impl Strategy<Value = Vec<Uitem<Receiver>>> {
246        typecodes
247            .into_iter()
248            .map(|tc| match tc {
249                DataTypecode::P2pkh => uniform20(0u8..).prop_map(Receiver::P2pkh).boxed(),
250                DataTypecode::P2sh => uniform20(0u8..).prop_map(Receiver::P2sh).boxed(),
251                DataTypecode::Sapling => uniform43().prop_map(Receiver::Sapling).boxed(),
252                DataTypecode::Orchard => uniform43().prop_map(Receiver::Orchard).boxed(),
253                DataTypecode::Unknown(typecode) => vec(any::<u8>(), 32..256)
254                    .prop_map(move |data| Receiver::Unknown { typecode, data })
255                    .boxed(),
256            })
257            .map(|s| s.prop_map(Uitem::Data))
258            .collect::<Vec<_>>()
259    }
260
261    /// Generates an arbitrary R0 Unified address (shielded with optional transparent).
262    pub fn arb_unified_address() -> impl Strategy<Value = Address> {
263        arb_typecodes()
264            .prop_flat_map(arb_unified_address_for_typecodes)
265            .prop_map(|items| Address {
266                revision: Revision::R0,
267                items,
268            })
269    }
270
271    /// Generates an arbitrary set of metadata items.
272    pub fn arb_metadata_items() -> impl Strategy<Value = Vec<Uitem<Receiver>>> {
273        (
274            prop::option::of(
275                any::<u32>().prop_map(|h| Uitem::Metadata(MetadataItem::ExpiryHeight(h))),
276            ),
277            prop::option::of(
278                any::<u64>().prop_map(|t| Uitem::Metadata(MetadataItem::ExpiryTime(t))),
279            ),
280        )
281            .prop_map(|(h, t)| h.into_iter().chain(t).collect())
282    }
283
284    /// A strategy to generate a known shielded typecode (Sapling or Orchard only).
285    /// Unlike `arb_shielded_typecode`, this excludes unknown typecodes which are not
286    /// recognized as shielded for `zu` address validation.
287    pub fn arb_known_shielded_typecode() -> impl Strategy<Value = DataTypecode> {
288        select(vec![DataTypecode::Sapling, DataTypecode::Orchard])
289    }
290
291    /// Generates an arbitrary R2 shielded-only (`zu`) Unified address: at least one known
292    /// shielded receiver (Sapling or Orchard), no transparent receivers, with optional metadata.
293    pub fn arb_r2_shielded_address() -> impl Strategy<Value = Address> {
294        (
295            prop::collection::btree_set(arb_known_shielded_typecode(), 1..2),
296            arb_metadata_items(),
297        )
298            .prop_flat_map(|(shielded_tcs, metadata)| {
299                arb_unified_address_for_typecodes(shielded_tcs).prop_map(move |mut items| {
300                    items.extend(metadata.clone());
301                    Address {
302                        revision: Revision::R2,
303                        items,
304                    }
305                })
306            })
307    }
308
309    /// Generates an arbitrary R2 transparent-including (`tu`) Unified address: at least one
310    /// shielded receiver plus a transparent receiver, with optional metadata.
311    pub fn arb_r2_transparent_including_address() -> impl Strategy<Value = Address> {
312        (
313            prop::collection::hash_set(arb_shielded_typecode(), 1..3),
314            arb_transparent_typecode(),
315            arb_metadata_items(),
316        )
317            .prop_flat_map(|(shielded_tcs, transparent_tc, metadata)| {
318                let typecodes: BTreeSet<DataTypecode> = shielded_tcs
319                    .into_iter()
320                    .chain(Some(transparent_tc))
321                    .collect();
322                arb_unified_address_for_typecodes(typecodes).prop_map(move |mut items| {
323                    items.extend(metadata.clone());
324                    Address {
325                        revision: Revision::R2,
326                        items,
327                    }
328                })
329            })
330    }
331}
332
333#[cfg(feature = "test-dependencies")]
334pub mod test_vectors;
335
336#[cfg(test)]
337mod tests {
338    use alloc::borrow::ToOwned;
339    use alloc::vec::Vec;
340
341    use assert_matches::assert_matches;
342    use zcash_protocol::address::Revision;
343    use zcash_protocol::consensus::NetworkType;
344
345    use crate::{
346        kind::unified::{Container, Encoding, MetadataItem, Uitem, private::SealedContainer},
347        unified::address::testing::{
348            arb_r2_shielded_address, arb_r2_transparent_including_address, arb_unified_address,
349        },
350    };
351
352    use proptest::{prelude::*, sample::select};
353
354    use super::{Address, ParseError, Receiver};
355    use crate::unified::Typecode;
356
357    proptest! {
358        #[test]
359        fn ua_roundtrip(
360            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
361            ua in arb_unified_address(),
362        ) {
363            let encoded = ua.encode(&network);
364            let decoded = Address::decode(&encoded);
365            let decoded = decoded.map(|(net, _rev, addr)| (net, addr));
366            prop_assert_eq!(&decoded, &Ok((network, ua)));
367            let reencoded = decoded.unwrap().1.encode(&network);
368            prop_assert_eq!(reencoded, encoded);
369        }
370
371        #[test]
372        fn r2_shielded_ua_roundtrip(
373            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
374            ua in arb_r2_shielded_address(),
375        ) {
376            let encoded = ua.encode(&network);
377            let decoded = Address::decode(&encoded);
378            let decoded = decoded.map(|(net, _rev, addr)| (net, addr));
379            prop_assert_eq!(&decoded, &Ok((network, ua)));
380            let reencoded = decoded.unwrap().1.encode(&network);
381            prop_assert_eq!(reencoded, encoded);
382        }
383
384        #[test]
385        fn r2_transparent_including_ua_roundtrip(
386            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
387            ua in arb_r2_transparent_including_address(),
388        ) {
389            let encoded = ua.encode(&network);
390            let decoded = Address::decode(&encoded);
391            let decoded = decoded.map(|(net, _rev, addr)| (net, addr));
392            prop_assert_eq!(&decoded, &Ok((network, ua)));
393            let reencoded = decoded.unwrap().1.encode(&network);
394            prop_assert_eq!(reencoded, encoded);
395        }
396    }
397
398    #[test]
399    fn padding() {
400        // The test cases below use `Address { revision: R0, items: vec![Uitem::Data(Receiver::Orchard([1; 43]))] }` as base.
401        let _ua = Address {
402            revision: Revision::R0,
403            items: vec![Uitem::Data(Receiver::Orchard([1; 43]))],
404        };
405
406        // Invalid padding ([0xff; 16] instead of [0x75, 0x00, 0x00, 0x00...])
407        let invalid_padding = [
408            0xe6, 0x59, 0xd1, 0xed, 0xf7, 0x4b, 0xe3, 0x5e, 0x5a, 0x54, 0x0e, 0x41, 0x5d, 0x2f,
409            0x0c, 0x0d, 0x33, 0x42, 0xbd, 0xbe, 0x9f, 0x82, 0x62, 0x01, 0xc1, 0x1b, 0xd4, 0x1e,
410            0x42, 0x47, 0x86, 0x23, 0x05, 0x4b, 0x98, 0xd7, 0x76, 0x86, 0xa5, 0xe3, 0x1b, 0xd3,
411            0x03, 0xca, 0x24, 0x44, 0x8e, 0x72, 0xc1, 0x4a, 0xc6, 0xbf, 0x3f, 0x2b, 0xce, 0xa7,
412            0x7b, 0x28, 0x69, 0xc9, 0x84,
413        ];
414        assert_eq!(
415            Address::parse_internal(Address::MAINNET, &invalid_padding[..], Revision::R0),
416            Err(ParseError::InvalidEncoding(
417                "Invalid padding bytes".to_owned()
418            ))
419        );
420
421        // Short padding (padded to 15 bytes instead of 16)
422        let truncated_padding = [
423            0x9a, 0x56, 0x12, 0xa3, 0x43, 0x45, 0xe0, 0x82, 0x6c, 0xac, 0x24, 0x8b, 0x3b, 0x45,
424            0x72, 0x9a, 0x53, 0xd5, 0xf8, 0xda, 0xec, 0x07, 0x7c, 0xba, 0x9f, 0xa8, 0xd2, 0x97,
425            0x5b, 0xda, 0x73, 0x1b, 0xd2, 0xd1, 0x32, 0x6b, 0x7b, 0x36, 0xdd, 0x57, 0x84, 0x2a,
426            0xa0, 0x21, 0x23, 0x89, 0x73, 0x85, 0xe1, 0x4b, 0x3e, 0x95, 0xb7, 0xd4, 0x67, 0xbc,
427            0x4b, 0x31, 0xee, 0x5a,
428        ];
429        assert_eq!(
430            Address::parse_internal(Address::MAINNET, &truncated_padding[..], Revision::R0),
431            Err(ParseError::InvalidEncoding(
432                "Invalid padding bytes".to_owned()
433            ))
434        );
435    }
436
437    #[test]
438    fn truncated() {
439        // - Missing the last data byte of the Sapling receiver.
440        let truncated_sapling_data = [
441            0xaa, 0xb0, 0x6e, 0x7b, 0x26, 0x7a, 0x22, 0x17, 0x39, 0xfa, 0x07, 0x69, 0xe9, 0x32,
442            0x2b, 0xac, 0x8c, 0x9e, 0x5e, 0x8a, 0xd9, 0x24, 0x06, 0x5a, 0x13, 0x79, 0x3a, 0x8d,
443            0xb4, 0x52, 0xfa, 0x18, 0x4e, 0x33, 0x4d, 0x8c, 0x17, 0x77, 0x4d, 0x63, 0x69, 0x34,
444            0x22, 0x70, 0x3a, 0xea, 0x30, 0x82, 0x5a, 0x6b, 0x37, 0xd1, 0x0d, 0xbe, 0x20, 0xab,
445            0x82, 0x86, 0x98, 0x34, 0x6a, 0xd8, 0x45, 0x40, 0xd0, 0x25, 0x60, 0xbf, 0x1e, 0xb6,
446            0xeb, 0x06, 0x85, 0x70, 0x4c, 0x42, 0xbc, 0x19, 0x14, 0xef, 0x7a, 0x05, 0xa0, 0x71,
447            0xb2, 0x63, 0x80, 0xbb, 0xdc, 0x12, 0x08, 0x48, 0x28, 0x8f, 0x1c, 0x9e, 0xc3, 0x42,
448            0xc6, 0x5e, 0x68, 0xa2, 0x78, 0x6c, 0x9e,
449        ];
450        assert_matches!(
451            Address::parse_internal(Address::MAINNET, &truncated_sapling_data[..], Revision::R0),
452            Err(ParseError::InvalidEncoding(_))
453        );
454
455        // - Truncated after the typecode of the Sapling receiver.
456        let truncated_after_sapling_typecode = [
457            0x87, 0x7a, 0xdf, 0x79, 0x6b, 0xe3, 0xb3, 0x40, 0xef, 0xe4, 0x5d, 0xc2, 0x91, 0xa2,
458            0x81, 0xfc, 0x7d, 0x76, 0xbb, 0xb0, 0x58, 0x98, 0x53, 0x59, 0xd3, 0x3f, 0xbc, 0x4b,
459            0x86, 0x59, 0x66, 0x62, 0x75, 0x92, 0xba, 0xcc, 0x31, 0x1e, 0x60, 0x02, 0x3b, 0xd8,
460            0x4c, 0xdf, 0x36, 0xa1, 0xac, 0x82, 0x57, 0xed, 0x0c, 0x98, 0x49, 0x8f, 0x49, 0x7e,
461            0xe6, 0x70, 0x36, 0x5b, 0x7b, 0x9e,
462        ];
463        assert_matches!(
464            Address::parse_internal(
465                Address::MAINNET,
466                &truncated_after_sapling_typecode[..],
467                Revision::R0
468            ),
469            Err(ParseError::InvalidEncoding(_))
470        );
471    }
472
473    #[test]
474    fn duplicate_typecode() {
475        let ua = Address {
476            revision: Revision::R0,
477            items: vec![
478                Uitem::Data(Receiver::Sapling([1; 43])),
479                Uitem::Data(Receiver::Sapling([2; 43])),
480            ],
481        };
482        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
483        assert_eq!(
484            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
485            Err(ParseError::DuplicateTypecode(Typecode::Data(
486                super::super::DataTypecode::Sapling
487            )))
488        );
489    }
490
491    #[test]
492    fn p2pkh_and_p2sh() {
493        let ua = Address {
494            revision: Revision::R0,
495            items: vec![
496                Uitem::Data(Receiver::P2pkh([0; 20])),
497                Uitem::Data(Receiver::P2sh([0; 20])),
498            ],
499        };
500        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
501        assert_eq!(
502            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
503            Err(ParseError::BothP2phkAndP2sh)
504        );
505    }
506
507    #[test]
508    fn addresses_out_of_order() {
509        let ua = Address {
510            revision: Revision::R0,
511            items: vec![
512                Uitem::Data(Receiver::Sapling([0; 43])),
513                Uitem::Data(Receiver::P2pkh([0; 20])),
514            ],
515        };
516        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
517        assert_eq!(
518            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
519            Err(ParseError::InvalidTypecodeOrder)
520        );
521    }
522
523    #[test]
524    fn only_transparent() {
525        // Encoding of `Address { items: vec![Uitem::Data(Receiver::P2pkh([0; 20]))] }`.
526        let encoded = [
527            0xf0, 0x9e, 0x9d, 0x6e, 0xf5, 0xa6, 0xac, 0x16, 0x50, 0xf0, 0xdb, 0xe1, 0x2c, 0xa5,
528            0x36, 0x22, 0xa2, 0x04, 0x89, 0x86, 0xe9, 0x6a, 0x9b, 0xf3, 0xff, 0x6d, 0x2f, 0xe6,
529            0xea, 0xdb, 0xc5, 0x20, 0x62, 0xf9, 0x6f, 0xa9, 0x86, 0xcc,
530        ];
531
532        assert_matches!(
533            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
534            Err(ParseError::InvalidEncoding(_))
535        );
536    }
537
538    #[test]
539    fn receivers_are_sorted() {
540        let ua = Address {
541            revision: Revision::R0,
542            items: vec![
543                Uitem::Data(Receiver::P2pkh([0; 20])),
544                Uitem::Data(Receiver::Orchard([0; 43])),
545                Uitem::Data(Receiver::Unknown {
546                    typecode: 0xff,
547                    data: vec![],
548                }),
549                Uitem::Data(Receiver::Sapling([0; 43])),
550            ],
551        };
552
553        assert_eq!(
554            ua.items(),
555            vec![
556                Receiver::Orchard([0; 43]),
557                Receiver::Sapling([0; 43]),
558                Receiver::P2pkh([0; 20]),
559                Receiver::Unknown {
560                    typecode: 0xff,
561                    data: vec![],
562                },
563            ]
564        )
565    }
566
567    #[test]
568    fn address_receiver_queries() {
569        use zcash_protocol::PoolType;
570
571        // A UA with all receiver types
572        let ua = Address {
573            revision: Revision::R0,
574            items: vec![
575                Uitem::Data(Receiver::Orchard([0; 43])),
576                Uitem::Data(Receiver::Sapling([1; 43])),
577                Uitem::Data(Receiver::P2pkh([2; 20])),
578            ],
579        };
580
581        // has_receiver_of_type
582        assert!(ua.has_receiver_of_type(PoolType::ORCHARD));
583        assert!(ua.has_receiver_of_type(PoolType::SAPLING));
584        assert!(ua.has_receiver_of_type(PoolType::TRANSPARENT));
585
586        // can_receive_memo: true only when Sapling or Orchard is present
587        assert!(ua.can_receive_memo());
588
589        // contains_receiver: exact data match required
590        assert!(ua.contains_receiver(&Receiver::Orchard([0; 43])));
591        assert!(!ua.contains_receiver(&Receiver::Orchard([1; 43]))); // same type, different data
592
593        // Transparent-only address cannot receive memos
594        let transparent_only = Address {
595            revision: Revision::R0,
596            items: vec![Uitem::Data(Receiver::P2pkh([0; 20]))],
597        };
598        assert!(!transparent_only.can_receive_memo());
599        assert!(!transparent_only.has_receiver_of_type(PoolType::ORCHARD));
600        assert!(!transparent_only.has_receiver_of_type(PoolType::SAPLING));
601        assert!(transparent_only.has_receiver_of_type(PoolType::TRANSPARENT));
602
603        // Unknown receiver does not count for any pool type
604        let with_unknown = Address {
605            revision: Revision::R0,
606            items: vec![
607                Uitem::Data(Receiver::Orchard([0; 43])),
608                Uitem::Data(Receiver::Unknown {
609                    typecode: 0xAA,
610                    data: vec![0; 32],
611                }),
612            ],
613        };
614        assert!(!with_unknown.has_receiver_of_type(PoolType::SAPLING));
615        // Unknown receiver is NOT counted as transparent
616        assert!(!with_unknown.has_receiver_of_type(PoolType::TRANSPARENT));
617    }
618
619    #[test]
620    fn r2_address_with_expiry() {
621        // Construct an R2 address with Orchard + ExpiryHeight (no transparent = zu).
622        let items = vec![
623            Uitem::Data(Receiver::Orchard([7; 43])),
624            Uitem::Metadata(MetadataItem::ExpiryHeight(1_000_000)),
625        ];
626        let ua = Address::try_from_items(Revision::R2, items).unwrap();
627        assert_eq!(ua.revision(), Revision::R2);
628
629        // Round-trip through encoding.
630        let encoded = ua.encode(&NetworkType::Main);
631        assert!(encoded.starts_with("zu1")); // shielded-only R2 mainnet HRP
632        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
633        assert_eq!(net, NetworkType::Main);
634        assert_eq!(rev, Revision::R2);
635        assert_eq!(decoded, ua);
636
637        // Check that metadata is preserved.
638        let meta: Vec<_> = decoded.metadata_items();
639        assert_eq!(meta.len(), 1);
640        assert_eq!(*meta[0], MetadataItem::ExpiryHeight(1_000_000));
641    }
642
643    #[test]
644    fn r2_tu_address_with_transparent() {
645        // R2 addresses with transparent receivers encode as tu.
646        let items = vec![
647            Uitem::Data(Receiver::P2pkh([0; 20])),
648            Uitem::Data(Receiver::Orchard([0; 43])),
649            Uitem::Metadata(MetadataItem::ExpiryHeight(1_000_000)),
650        ];
651        let ua = Address::try_from_items(Revision::R2, items).unwrap();
652
653        let encoded = ua.encode(&NetworkType::Main);
654        assert!(encoded.starts_with("tu1")); // transparent-including R2 mainnet HRP
655
656        // Round-trip decode.
657        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
658        assert_eq!(net, NetworkType::Main);
659        assert_eq!(rev, Revision::R2);
660        assert_eq!(decoded, ua);
661    }
662
663    #[test]
664    fn zu_address_rejects_transparent() {
665        // Manually construct a zu-encoded payload containing transparent data.
666        // The parse_internal with zu HRP should reject it.
667        use crate::kind::unified::private::SealedContainer;
668
669        let ua = Address {
670            revision: Revision::R2,
671            items: vec![
672                Uitem::Data(Receiver::P2pkh([0; 20])),
673                Uitem::Data(Receiver::Orchard([0; 43])),
674                Uitem::Metadata(MetadataItem::ExpiryHeight(1_000_000)),
675            ],
676        };
677        let encoded = ua.to_jumbled_bytes(Address::MAINNET_R2);
678        assert_eq!(
679            Address::parse_internal(Address::MAINNET_R2, &encoded[..], Revision::R2),
680            Err(ParseError::TransparentReceiverInR2Address)
681        );
682    }
683
684    #[test]
685    fn tu_transparent_only_roundtrip() {
686        // A tu address with only P2pkh + ExpiryHeight (no shielded) is valid.
687        let items = vec![
688            Uitem::Data(Receiver::P2pkh([1; 20])),
689            Uitem::Metadata(MetadataItem::ExpiryHeight(500_000)),
690        ];
691        let ua = Address::try_from_items(Revision::R2, items).unwrap();
692
693        let encoded = ua.encode(&NetworkType::Main);
694        assert!(encoded.starts_with("tu1")); // transparent-including HRP
695
696        // Round-trip.
697        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
698        assert_eq!(net, NetworkType::Main);
699        assert_eq!(rev, Revision::R2);
700        assert_eq!(decoded, ua);
701    }
702
703    #[test]
704    fn r2_rejects_metadata_only_container() {
705        // An R2 address with only metadata (no data items) should fail.
706        let items = vec![Uitem::Metadata(MetadataItem::ExpiryHeight(100))];
707        assert_eq!(
708            Address::try_from_items(Revision::R2, items),
709            Err(ParseError::NoDataItems)
710        );
711    }
712
713    #[test]
714    fn r0_rejects_metadata_only_container() {
715        // A container holding only metadata items has no data item whatever its
716        // revision, which is distinct from holding only transparent items.
717        // A metadata typecode below the MUST-understand range (which starts at
718        // `0xE0`), so that a Revision 0 container may carry it without being
719        // rejected as not understood.
720        const SHOULD_UNDERSTAND_METADATA_TYPECODE: u32 = 0xC0;
721
722        let items = vec![Uitem::Metadata(MetadataItem::Unknown {
723            typecode: SHOULD_UNDERSTAND_METADATA_TYPECODE,
724            data: vec![],
725        })];
726        assert_eq!(
727            Address::try_from_items(Revision::R0, items),
728            Err(ParseError::NoDataItems)
729        );
730    }
731
732    #[test]
733    fn zu_address_allows_unrecognised_only_receiver() {
734        // ZIP 316 requires only that a `zu` address contain no transparent receiver;
735        // one whose sole data item is of an unrecognised type is well-formed, and must
736        // round-trip through the `zu` encoding the encoder selects for it.
737        // A data typecode from the range ZIP 316 reserves for experiments
738        // (`0xFFFA..=0xFFFF`), which no revision assigns a meaning to.
739        const EXPERIMENTAL_TYPECODE: u32 = 0xFFFA;
740
741        let items = vec![Uitem::Data(Receiver::Unknown {
742            typecode: EXPERIMENTAL_TYPECODE,
743            data: vec![0; 32],
744        })];
745        let ua = Address::try_from_items(Revision::R2, items).unwrap();
746
747        let encoded = ua.encode(&NetworkType::Main);
748        assert!(
749            encoded.starts_with("zu1"),
750            "expected a zu address: {encoded}"
751        );
752
753        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
754        assert_eq!(net, NetworkType::Main);
755        assert_eq!(rev, Revision::R2);
756        assert_eq!(decoded, ua);
757    }
758
759    #[test]
760    fn r0_rejects_must_understand_metadata() {
761        // Construct an R0 address encoding that contains a MUST-understand metadata item.
762        // We build the raw bytes manually: Orchard receiver + ExpiryHeight metadata.
763        use crate::kind::unified::private::SealedContainer;
764
765        let ua = Address {
766            revision: Revision::R0,
767            items: vec![
768                Uitem::Data(Receiver::Orchard([1; 43])),
769                Uitem::Metadata(MetadataItem::ExpiryHeight(100)),
770            ],
771        };
772        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
773        // Parsing as R0 should fail with NotUnderstood.
774        assert_matches!(
775            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
776            Err(ParseError::NotUnderstood(0xE0))
777        );
778    }
779
780    #[test]
781    fn r2_rejects_unknown_must_understand_metadata() {
782        // An unknown MUST-understand metadata (e.g. 0xE5) should fail in R2 too.
783        use crate::kind::unified::private::SealedContainer;
784
785        let ua = Address {
786            revision: Revision::R2,
787            items: vec![
788                Uitem::Data(Receiver::Orchard([1; 43])),
789                Uitem::Metadata(MetadataItem::Unknown {
790                    typecode: 0xE5,
791                    data: vec![0; 4],
792                }),
793            ],
794        };
795        let encoded = ua.to_jumbled_bytes(Address::MAINNET_R2);
796        assert_matches!(
797            Address::parse_internal(Address::MAINNET_R2, &encoded[..], Revision::R2),
798            Err(ParseError::NotUnderstood(0xE5))
799        );
800    }
801}