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#[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 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#[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 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 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 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 pub fn arb_transparent_typecode() -> impl Strategy<Value = DataTypecode> {
216 select(vec![DataTypecode::P2pkh, DataTypecode::P2sh])
217 }
218
219 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 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 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 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 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 pub fn arb_known_shielded_typecode() -> impl Strategy<Value = DataTypecode> {
288 select(vec![DataTypecode::Sapling, DataTypecode::Orchard])
289 }
290
291 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 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 let _ua = Address {
402 revision: Revision::R0,
403 items: vec![Uitem::Data(Receiver::Orchard([1; 43]))],
404 };
405
406 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 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 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 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 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 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 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 assert!(ua.can_receive_memo());
588
589 assert!(ua.contains_receiver(&Receiver::Orchard([0; 43])));
591 assert!(!ua.contains_receiver(&Receiver::Orchard([1; 43]))); 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 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 assert!(!with_unknown.has_receiver_of_type(PoolType::TRANSPARENT));
617 }
618
619 #[test]
620 fn r2_address_with_expiry() {
621 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 let encoded = ua.encode(&NetworkType::Main);
631 assert!(encoded.starts_with("zu1")); 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 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 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")); 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 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 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")); 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 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 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 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 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 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 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}