Skip to main content

zcash_address/kind/unified/
address.rs

1use alloc::vec::Vec;
2use core::convert::TryInto;
3
4use zcash_protocol::PoolType;
5
6use super::{DataTypecode, ParseError, Uitem, private::SealedItem};
7use crate::{constants, unified::Revision};
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 crate::unified::Revision;
194    use proptest::{
195        array::{uniform11, uniform20, uniform32},
196        collection::vec,
197        prelude::*,
198        sample::select,
199        strategy::Strategy,
200    };
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 crate::unified::Revision;
342    use assert_matches::assert_matches;
343    use zcash_encoding::MAX_COMPACT_SIZE;
344    use zcash_protocol::consensus::NetworkType;
345
346    use crate::{
347        kind::unified::{Container, Encoding, MetadataItem, Uitem, private::SealedContainer},
348        unified::address::testing::{
349            arb_r2_shielded_address, arb_r2_transparent_including_address, arb_unified_address,
350        },
351    };
352
353    use proptest::{prelude::*, sample::select};
354
355    use super::{Address, ParseError, Receiver};
356    use crate::unified::Typecode;
357
358    proptest! {
359        #[test]
360        fn ua_roundtrip(
361            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
362            ua in arb_unified_address(),
363        ) {
364            let encoded = ua.encode(&network);
365            let decoded = Address::decode(&encoded);
366            let decoded = decoded.map(|(net, _rev, addr)| (net, addr));
367            prop_assert_eq!(&decoded, &Ok((network, ua)));
368            let reencoded = decoded.unwrap().1.encode(&network);
369            prop_assert_eq!(reencoded, encoded);
370        }
371
372        #[test]
373        fn r2_shielded_ua_roundtrip(
374            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
375            ua in arb_r2_shielded_address(),
376        ) {
377            let encoded = ua.encode(&network);
378            let decoded = Address::decode(&encoded);
379            let decoded = decoded.map(|(net, _rev, addr)| (net, addr));
380            prop_assert_eq!(&decoded, &Ok((network, ua)));
381            let reencoded = decoded.unwrap().1.encode(&network);
382            prop_assert_eq!(reencoded, encoded);
383        }
384
385        #[test]
386        fn r2_transparent_including_ua_roundtrip(
387            network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
388            ua in arb_r2_transparent_including_address(),
389        ) {
390            let encoded = ua.encode(&network);
391            let decoded = Address::decode(&encoded);
392            let decoded = decoded.map(|(net, _rev, addr)| (net, addr));
393            prop_assert_eq!(&decoded, &Ok((network, ua)));
394            let reencoded = decoded.unwrap().1.encode(&network);
395            prop_assert_eq!(reencoded, encoded);
396        }
397    }
398
399    #[test]
400    fn padding() {
401        // The test cases below use `Address { revision: R0, items: vec![Uitem::Data(Receiver::Orchard([1; 43]))] }` as base.
402        let _ua = Address {
403            revision: Revision::R0,
404            items: vec![Uitem::Data(Receiver::Orchard([1; 43]))],
405        };
406
407        // Invalid padding ([0xff; 16] instead of [0x75, 0x00, 0x00, 0x00...])
408        let invalid_padding = [
409            0xe6, 0x59, 0xd1, 0xed, 0xf7, 0x4b, 0xe3, 0x5e, 0x5a, 0x54, 0x0e, 0x41, 0x5d, 0x2f,
410            0x0c, 0x0d, 0x33, 0x42, 0xbd, 0xbe, 0x9f, 0x82, 0x62, 0x01, 0xc1, 0x1b, 0xd4, 0x1e,
411            0x42, 0x47, 0x86, 0x23, 0x05, 0x4b, 0x98, 0xd7, 0x76, 0x86, 0xa5, 0xe3, 0x1b, 0xd3,
412            0x03, 0xca, 0x24, 0x44, 0x8e, 0x72, 0xc1, 0x4a, 0xc6, 0xbf, 0x3f, 0x2b, 0xce, 0xa7,
413            0x7b, 0x28, 0x69, 0xc9, 0x84,
414        ];
415        assert_eq!(
416            Address::parse_internal(Address::MAINNET, &invalid_padding[..], Revision::R0),
417            Err(ParseError::InvalidEncoding(
418                "Invalid padding bytes".to_owned()
419            ))
420        );
421
422        // Short padding (padded to 15 bytes instead of 16)
423        let truncated_padding = [
424            0x9a, 0x56, 0x12, 0xa3, 0x43, 0x45, 0xe0, 0x82, 0x6c, 0xac, 0x24, 0x8b, 0x3b, 0x45,
425            0x72, 0x9a, 0x53, 0xd5, 0xf8, 0xda, 0xec, 0x07, 0x7c, 0xba, 0x9f, 0xa8, 0xd2, 0x97,
426            0x5b, 0xda, 0x73, 0x1b, 0xd2, 0xd1, 0x32, 0x6b, 0x7b, 0x36, 0xdd, 0x57, 0x84, 0x2a,
427            0xa0, 0x21, 0x23, 0x89, 0x73, 0x85, 0xe1, 0x4b, 0x3e, 0x95, 0xb7, 0xd4, 0x67, 0xbc,
428            0x4b, 0x31, 0xee, 0x5a,
429        ];
430        assert_eq!(
431            Address::parse_internal(Address::MAINNET, &truncated_padding[..], Revision::R0),
432            Err(ParseError::InvalidEncoding(
433                "Invalid padding bytes".to_owned()
434            ))
435        );
436    }
437
438    #[test]
439    fn truncated() {
440        // - Missing the last data byte of the Sapling receiver.
441        let truncated_sapling_data = [
442            0xaa, 0xb0, 0x6e, 0x7b, 0x26, 0x7a, 0x22, 0x17, 0x39, 0xfa, 0x07, 0x69, 0xe9, 0x32,
443            0x2b, 0xac, 0x8c, 0x9e, 0x5e, 0x8a, 0xd9, 0x24, 0x06, 0x5a, 0x13, 0x79, 0x3a, 0x8d,
444            0xb4, 0x52, 0xfa, 0x18, 0x4e, 0x33, 0x4d, 0x8c, 0x17, 0x77, 0x4d, 0x63, 0x69, 0x34,
445            0x22, 0x70, 0x3a, 0xea, 0x30, 0x82, 0x5a, 0x6b, 0x37, 0xd1, 0x0d, 0xbe, 0x20, 0xab,
446            0x82, 0x86, 0x98, 0x34, 0x6a, 0xd8, 0x45, 0x40, 0xd0, 0x25, 0x60, 0xbf, 0x1e, 0xb6,
447            0xeb, 0x06, 0x85, 0x70, 0x4c, 0x42, 0xbc, 0x19, 0x14, 0xef, 0x7a, 0x05, 0xa0, 0x71,
448            0xb2, 0x63, 0x80, 0xbb, 0xdc, 0x12, 0x08, 0x48, 0x28, 0x8f, 0x1c, 0x9e, 0xc3, 0x42,
449            0xc6, 0x5e, 0x68, 0xa2, 0x78, 0x6c, 0x9e,
450        ];
451        assert_matches!(
452            Address::parse_internal(Address::MAINNET, &truncated_sapling_data[..], Revision::R0),
453            Err(ParseError::InvalidEncoding(_))
454        );
455
456        // - Truncated after the typecode of the Sapling receiver.
457        let truncated_after_sapling_typecode = [
458            0x87, 0x7a, 0xdf, 0x79, 0x6b, 0xe3, 0xb3, 0x40, 0xef, 0xe4, 0x5d, 0xc2, 0x91, 0xa2,
459            0x81, 0xfc, 0x7d, 0x76, 0xbb, 0xb0, 0x58, 0x98, 0x53, 0x59, 0xd3, 0x3f, 0xbc, 0x4b,
460            0x86, 0x59, 0x66, 0x62, 0x75, 0x92, 0xba, 0xcc, 0x31, 0x1e, 0x60, 0x02, 0x3b, 0xd8,
461            0x4c, 0xdf, 0x36, 0xa1, 0xac, 0x82, 0x57, 0xed, 0x0c, 0x98, 0x49, 0x8f, 0x49, 0x7e,
462            0xe6, 0x70, 0x36, 0x5b, 0x7b, 0x9e,
463        ];
464        assert_matches!(
465            Address::parse_internal(
466                Address::MAINNET,
467                &truncated_after_sapling_typecode[..],
468                Revision::R0
469            ),
470            Err(ParseError::InvalidEncoding(_))
471        );
472    }
473
474    #[test]
475    fn duplicate_typecode() {
476        let ua = Address {
477            revision: Revision::R0,
478            items: vec![
479                Uitem::Data(Receiver::Sapling([1; 43])),
480                Uitem::Data(Receiver::Sapling([2; 43])),
481            ],
482        };
483        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
484        assert_eq!(
485            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
486            Err(ParseError::DuplicateTypecode(Typecode::Data(
487                super::super::DataTypecode::Sapling
488            )))
489        );
490    }
491
492    #[test]
493    fn p2pkh_and_p2sh() {
494        let ua = Address {
495            revision: Revision::R0,
496            items: vec![
497                Uitem::Data(Receiver::P2pkh([0; 20])),
498                Uitem::Data(Receiver::P2sh([0; 20])),
499            ],
500        };
501        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
502        assert_eq!(
503            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
504            Err(ParseError::BothP2phkAndP2sh)
505        );
506    }
507
508    #[test]
509    fn addresses_out_of_order() {
510        let ua = Address {
511            revision: Revision::R0,
512            items: vec![
513                Uitem::Data(Receiver::Sapling([0; 43])),
514                Uitem::Data(Receiver::P2pkh([0; 20])),
515            ],
516        };
517        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
518        assert_eq!(
519            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
520            Err(ParseError::InvalidTypecodeOrder)
521        );
522    }
523
524    #[test]
525    fn only_transparent() {
526        // Encoding of `Address { items: vec![Uitem::Data(Receiver::P2pkh([0; 20]))] }`.
527        let encoded = [
528            0xf0, 0x9e, 0x9d, 0x6e, 0xf5, 0xa6, 0xac, 0x16, 0x50, 0xf0, 0xdb, 0xe1, 0x2c, 0xa5,
529            0x36, 0x22, 0xa2, 0x04, 0x89, 0x86, 0xe9, 0x6a, 0x9b, 0xf3, 0xff, 0x6d, 0x2f, 0xe6,
530            0xea, 0xdb, 0xc5, 0x20, 0x62, 0xf9, 0x6f, 0xa9, 0x86, 0xcc,
531        ];
532
533        assert_matches!(
534            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
535            Err(ParseError::InvalidEncoding(_))
536        );
537    }
538
539    #[test]
540    fn receivers_are_sorted() {
541        let ua = Address {
542            revision: Revision::R0,
543            items: vec![
544                Uitem::Data(Receiver::P2pkh([0; 20])),
545                Uitem::Data(Receiver::Orchard([0; 43])),
546                Uitem::Data(Receiver::Unknown {
547                    typecode: 0xff,
548                    data: vec![],
549                }),
550                Uitem::Data(Receiver::Sapling([0; 43])),
551            ],
552        };
553
554        assert_eq!(
555            ua.items(),
556            vec![
557                Receiver::Orchard([0; 43]),
558                Receiver::Sapling([0; 43]),
559                Receiver::P2pkh([0; 20]),
560                Receiver::Unknown {
561                    typecode: 0xff,
562                    data: vec![],
563                },
564            ]
565        )
566    }
567
568    #[test]
569    fn address_receiver_queries() {
570        use zcash_protocol::PoolType;
571
572        // A UA with all receiver types
573        let ua = Address {
574            revision: Revision::R0,
575            items: vec![
576                Uitem::Data(Receiver::Orchard([0; 43])),
577                Uitem::Data(Receiver::Sapling([1; 43])),
578                Uitem::Data(Receiver::P2pkh([2; 20])),
579            ],
580        };
581
582        // has_receiver_of_type
583        assert!(ua.has_receiver_of_type(PoolType::ORCHARD));
584        assert!(ua.has_receiver_of_type(PoolType::SAPLING));
585        assert!(ua.has_receiver_of_type(PoolType::TRANSPARENT));
586
587        // can_receive_memo: true only when Sapling or Orchard is present
588        assert!(ua.can_receive_memo());
589
590        // contains_receiver: exact data match required
591        assert!(ua.contains_receiver(&Receiver::Orchard([0; 43])));
592        assert!(!ua.contains_receiver(&Receiver::Orchard([1; 43]))); // same type, different data
593
594        // Transparent-only address cannot receive memos
595        let transparent_only = Address {
596            revision: Revision::R0,
597            items: vec![Uitem::Data(Receiver::P2pkh([0; 20]))],
598        };
599        assert!(!transparent_only.can_receive_memo());
600        assert!(!transparent_only.has_receiver_of_type(PoolType::ORCHARD));
601        assert!(!transparent_only.has_receiver_of_type(PoolType::SAPLING));
602        assert!(transparent_only.has_receiver_of_type(PoolType::TRANSPARENT));
603
604        // Unknown receiver does not count for any pool type
605        let with_unknown = Address {
606            revision: Revision::R0,
607            items: vec![
608                Uitem::Data(Receiver::Orchard([0; 43])),
609                Uitem::Data(Receiver::Unknown {
610                    typecode: 0xAA,
611                    data: vec![0; 32],
612                }),
613            ],
614        };
615        assert!(!with_unknown.has_receiver_of_type(PoolType::SAPLING));
616        // Unknown receiver is NOT counted as transparent
617        assert!(!with_unknown.has_receiver_of_type(PoolType::TRANSPARENT));
618    }
619
620    #[test]
621    fn r2_address_with_expiry() {
622        // Construct an R2 address with Orchard + ExpiryHeight (no transparent = zu).
623        let items = vec![
624            Uitem::Data(Receiver::Orchard([7; 43])),
625            Uitem::Metadata(MetadataItem::ExpiryHeight(1_000_000)),
626        ];
627        let ua = Address::try_from_items(Revision::R2, items).unwrap();
628        assert_eq!(ua.revision(), Revision::R2);
629
630        // Round-trip through encoding.
631        let encoded = ua.encode(&NetworkType::Main);
632        assert!(encoded.starts_with("zu1")); // shielded-only R2 mainnet HRP
633        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
634        assert_eq!(net, NetworkType::Main);
635        assert_eq!(rev, Revision::R2);
636        assert_eq!(decoded, ua);
637
638        // Check that metadata is preserved.
639        let meta: Vec<_> = decoded.metadata_items();
640        assert_eq!(meta.len(), 1);
641        assert_eq!(*meta[0], MetadataItem::ExpiryHeight(1_000_000));
642    }
643
644    #[test]
645    fn r2_tu_address_with_transparent() {
646        // R2 addresses with transparent receivers encode as tu.
647        let items = vec![
648            Uitem::Data(Receiver::P2pkh([0; 20])),
649            Uitem::Data(Receiver::Orchard([0; 43])),
650            Uitem::Metadata(MetadataItem::ExpiryHeight(1_000_000)),
651        ];
652        let ua = Address::try_from_items(Revision::R2, items).unwrap();
653
654        let encoded = ua.encode(&NetworkType::Main);
655        assert!(encoded.starts_with("tu1")); // transparent-including R2 mainnet HRP
656
657        // Round-trip decode.
658        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
659        assert_eq!(net, NetworkType::Main);
660        assert_eq!(rev, Revision::R2);
661        assert_eq!(decoded, ua);
662    }
663
664    #[test]
665    fn zu_address_rejects_transparent() {
666        // Manually construct a zu-encoded payload containing transparent data.
667        // The parse_internal with zu HRP should reject it.
668        use crate::kind::unified::private::SealedContainer;
669
670        let ua = Address {
671            revision: Revision::R2,
672            items: vec![
673                Uitem::Data(Receiver::P2pkh([0; 20])),
674                Uitem::Data(Receiver::Orchard([0; 43])),
675                Uitem::Metadata(MetadataItem::ExpiryHeight(1_000_000)),
676            ],
677        };
678        let encoded = ua.to_jumbled_bytes(Address::MAINNET_R2);
679        assert_eq!(
680            Address::parse_internal(Address::MAINNET_R2, &encoded[..], Revision::R2),
681            Err(ParseError::TransparentReceiverInR2Address)
682        );
683    }
684
685    #[test]
686    fn tu_transparent_only_roundtrip() {
687        // A tu address with only P2pkh + ExpiryHeight (no shielded) is valid.
688        let items = vec![
689            Uitem::Data(Receiver::P2pkh([1; 20])),
690            Uitem::Metadata(MetadataItem::ExpiryHeight(500_000)),
691        ];
692        let ua = Address::try_from_items(Revision::R2, items).unwrap();
693
694        let encoded = ua.encode(&NetworkType::Main);
695        assert!(encoded.starts_with("tu1")); // transparent-including HRP
696
697        // Round-trip.
698        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
699        assert_eq!(net, NetworkType::Main);
700        assert_eq!(rev, Revision::R2);
701        assert_eq!(decoded, ua);
702    }
703
704    #[test]
705    fn r2_rejects_metadata_only_container() {
706        // An R2 address with only metadata (no data items) should fail.
707        let items = vec![Uitem::Metadata(MetadataItem::ExpiryHeight(100))];
708        assert_eq!(
709            Address::try_from_items(Revision::R2, items),
710            Err(ParseError::NoDataItems)
711        );
712    }
713
714    #[test]
715    fn r0_rejects_metadata_only_container() {
716        // A container holding only metadata items has no data item whatever its
717        // revision, which is distinct from holding only transparent items.
718        // A metadata typecode below the MUST-understand range (which starts at
719        // `0xE0`), so that a Revision 0 container may carry it without being
720        // rejected as not understood.
721        const SHOULD_UNDERSTAND_METADATA_TYPECODE: u32 = 0xC0;
722
723        let items = vec![Uitem::Metadata(MetadataItem::Unknown {
724            typecode: SHOULD_UNDERSTAND_METADATA_TYPECODE,
725            data: vec![],
726        })];
727        assert_eq!(
728            Address::try_from_items(Revision::R0, items),
729            Err(ParseError::NoDataItems)
730        );
731    }
732
733    #[test]
734    fn zu_address_allows_unrecognised_only_receiver() {
735        // ZIP 316 requires only that a `zu` address contain no transparent receiver;
736        // one whose sole data item is of an unrecognised type is well-formed, and must
737        // round-trip through the `zu` encoding the encoder selects for it.
738        // A data typecode from the range ZIP 316 reserves for experiments
739        // (`0xFFFA..=0xFFFF`), which no revision assigns a meaning to.
740        const EXPERIMENTAL_TYPECODE: u32 = 0xFFFA;
741
742        let items = vec![Uitem::Data(Receiver::Unknown {
743            typecode: EXPERIMENTAL_TYPECODE,
744            data: vec![0; 32],
745        })];
746        let ua = Address::try_from_items(Revision::R2, items).unwrap();
747
748        let encoded = ua.encode(&NetworkType::Main);
749        assert!(
750            encoded.starts_with("zu1"),
751            "expected a zu address: {encoded}"
752        );
753
754        let (net, rev, decoded) = Address::decode(&encoded).unwrap();
755        assert_eq!(net, NetworkType::Main);
756        assert_eq!(rev, Revision::R2);
757        assert_eq!(decoded, ua);
758    }
759
760    #[test]
761    fn rejects_typecode_above_compact_size_bound() {
762        // A typecode is encoded as a CompactSize, so a container holding a typecode
763        // above the CompactSize bound has no valid encoding.
764        let oversized_typecode = MAX_COMPACT_SIZE + 1;
765        let items = vec![Uitem::Data(Receiver::Unknown {
766            typecode: oversized_typecode,
767            data: vec![0; 32],
768        })];
769        assert_eq!(
770            Address::try_from_items(Revision::R2, items),
771            Err(ParseError::InvalidTypecodeValue(u64::from(
772                oversized_typecode
773            )))
774        );
775    }
776
777    #[test]
778    fn r0_rejects_must_understand_metadata() {
779        // Construct an R0 address encoding that contains a MUST-understand metadata item.
780        // We build the raw bytes manually: Orchard receiver + ExpiryHeight metadata.
781        use crate::kind::unified::private::SealedContainer;
782
783        let ua = Address {
784            revision: Revision::R0,
785            items: vec![
786                Uitem::Data(Receiver::Orchard([1; 43])),
787                Uitem::Metadata(MetadataItem::ExpiryHeight(100)),
788            ],
789        };
790        let encoded = ua.to_jumbled_bytes(Address::MAINNET);
791        // Parsing as R0 should fail with NotUnderstood.
792        assert_matches!(
793            Address::parse_internal(Address::MAINNET, &encoded[..], Revision::R0),
794            Err(ParseError::NotUnderstood(0xE0))
795        );
796    }
797
798    #[test]
799    fn r2_rejects_unknown_must_understand_metadata() {
800        // An unknown MUST-understand metadata (e.g. 0xE5) should fail in R2 too.
801        use crate::kind::unified::private::SealedContainer;
802
803        let ua = Address {
804            revision: Revision::R2,
805            items: vec![
806                Uitem::Data(Receiver::Orchard([1; 43])),
807                Uitem::Metadata(MetadataItem::Unknown {
808                    typecode: 0xE5,
809                    data: vec![0; 4],
810                }),
811            ],
812        };
813        let encoded = ua.to_jumbled_bytes(Address::MAINNET_R2);
814        assert_matches!(
815            Address::parse_internal(Address::MAINNET_R2, &encoded[..], Revision::R2),
816            Err(ParseError::NotUnderstood(0xE5))
817        );
818    }
819
820    #[test]
821    fn r0_construction_rejects_must_understand_metadata() {
822        // Revision 0 defines no MUST-understand metadata, so an R0 container holding an
823        // expiry item has no encoding that an R0 decoder accepts.
824        use crate::kind::unified::MetadataTypecode;
825
826        const EXPIRY_HEIGHT: u32 = 100;
827        const EXPIRY_TIME: u64 = 1_700_000_000;
828
829        for (meta, typecode) in [
830            (
831                MetadataItem::ExpiryHeight(EXPIRY_HEIGHT),
832                MetadataTypecode::ExpiryHeight,
833            ),
834            (
835                MetadataItem::ExpiryTime(EXPIRY_TIME),
836                MetadataTypecode::ExpiryTime,
837            ),
838        ] {
839            let items = vec![
840                Uitem::Data(Receiver::Orchard([1; 43])),
841                Uitem::Metadata(meta.clone()),
842            ];
843            assert_eq!(
844                Address::try_from_items(Revision::R0, items.clone()),
845                Err(ParseError::NotDefinedInRevision {
846                    typecode: Typecode::Metadata(typecode),
847                    revision: Revision::R0,
848                })
849            );
850            assert!(Address::try_from_items(Revision::R2, items).is_ok());
851        }
852    }
853
854    #[test]
855    fn construction_rejects_unknown_must_understand_metadata() {
856        // A MUST-understand metadata typecode that no revision assigns a meaning to.
857        const UNKNOWN_MUST_UNDERSTAND_TYPECODE: u32 = 0xE5;
858
859        for revision in [Revision::R0, Revision::R2] {
860            let items = vec![
861                Uitem::Data(Receiver::Orchard([1; 43])),
862                Uitem::Metadata(MetadataItem::Unknown {
863                    typecode: UNKNOWN_MUST_UNDERSTAND_TYPECODE,
864                    data: vec![0; 4],
865                }),
866            ];
867            assert_eq!(
868                Address::try_from_items(revision, items),
869                Err(ParseError::NotUnderstood(UNKNOWN_MUST_UNDERSTAND_TYPECODE))
870            );
871        }
872    }
873
874    #[test]
875    fn construction_rejects_encodings_outside_f4jumble_range() {
876        // A data typecode from the range ZIP 316 reserves for experiments
877        // (`0xFFFA..=0xFFFF`), which no revision assigns a meaning to.
878        const EXPERIMENTAL_TYPECODE: u32 = 0xFFFA;
879
880        // The 3-byte typecode, the 1-byte length, and the 16 bytes of padding are below
881        // F4Jumble's minimum input length.
882        let too_short = vec![Uitem::Data(Receiver::Unknown {
883            typecode: EXPERIMENTAL_TYPECODE,
884            data: vec![],
885        })];
886        assert_matches!(
887            Address::try_from_items(Revision::R2, too_short),
888            Err(ParseError::InvalidEncodedLength(length))
889                if length < *f4jumble::VALID_LENGTH.start()
890        );
891
892        let too_long = vec![Uitem::Data(Receiver::Unknown {
893            typecode: EXPERIMENTAL_TYPECODE,
894            data: vec![0; *f4jumble::VALID_LENGTH.end()],
895        })];
896        assert_matches!(
897            Address::try_from_items(Revision::R2, too_long),
898            Err(ParseError::InvalidEncodedLength(length))
899                if length > *f4jumble::VALID_LENGTH.end()
900        );
901    }
902
903    #[test]
904    fn transparent_only_r2_address_round_trips() {
905        // A lone P2PKH receiver has the shortest encoding that F4Jumble accepts.
906        let ua = Address::try_from_items(Revision::R2, vec![Uitem::Data(Receiver::P2pkh([1; 20]))])
907            .expect("a P2PKH receiver alone is a valid Revision 2 address");
908        let encoded = ua.encode(&NetworkType::Main);
909        assert!(
910            encoded.starts_with("tu1"),
911            "expected a tu address: {encoded}"
912        );
913        assert_eq!(
914            Address::decode(&encoded),
915            Ok((NetworkType::Main, Revision::R2, ua))
916        );
917    }
918}