1use zcash_protocol::{PoolType, constants};
2
3use super::{ParseError, Typecode, private::SealedItem};
4
5use alloc::vec::Vec;
6use core::convert::{TryFrom, TryInto};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12pub enum Receiver {
13 Orchard([u8; 43]),
14 Sapling([u8; 43]),
15 P2pkh([u8; 20]),
16 P2sh([u8; 20]),
17 Unknown { typecode: u32, data: Vec<u8> },
18}
19
20impl TryFrom<(u32, &[u8])> for Receiver {
21 type Error = ParseError;
22
23 fn try_from((typecode, addr): (u32, &[u8])) -> Result<Self, Self::Error> {
24 match typecode.try_into()? {
25 Typecode::P2pkh => addr.try_into().map(Receiver::P2pkh),
26 Typecode::P2sh => addr.try_into().map(Receiver::P2sh),
27 Typecode::Sapling => addr.try_into().map(Receiver::Sapling),
28 Typecode::Orchard => addr.try_into().map(Receiver::Orchard),
29 Typecode::Unknown(_) => Ok(Receiver::Unknown {
31 typecode,
32 data: addr.to_vec(),
33 }),
34 }
35 .map_err(|e| {
36 ParseError::InvalidEncoding(format!("Invalid address for typecode {}: {}", typecode, e))
37 })
38 }
39}
40
41impl SealedItem for Receiver {
42 fn typecode(&self) -> Typecode {
43 match self {
44 Receiver::P2pkh(_) => Typecode::P2pkh,
45 Receiver::P2sh(_) => Typecode::P2sh,
46 Receiver::Sapling(_) => Typecode::Sapling,
47 Receiver::Orchard(_) => Typecode::Orchard,
48 Receiver::Unknown { typecode, .. } => Typecode::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)]
114pub struct Address(pub(crate) Vec<Receiver>);
115
116impl Address {
117 pub fn has_receiver_of_type(&self, pool_type: PoolType) -> bool {
119 self.0.iter().any(|r| match r {
120 Receiver::Orchard(_) => pool_type == PoolType::ORCHARD,
121 Receiver::Sapling(_) => pool_type == PoolType::SAPLING,
122 Receiver::P2pkh(_) | Receiver::P2sh(_) => pool_type == PoolType::TRANSPARENT,
123 Receiver::Unknown { .. } => false,
124 })
125 }
126
127 pub fn contains_receiver(&self, receiver: &Receiver) -> bool {
129 self.0.contains(receiver)
130 }
131
132 pub fn can_receive_memo(&self) -> bool {
134 self.0
135 .iter()
136 .any(|r| matches!(r, Receiver::Sapling(_) | Receiver::Orchard(_)))
137 }
138}
139
140impl super::private::SealedContainer for Address {
141 const MAINNET: &'static str = constants::mainnet::HRP_UNIFIED_ADDRESS;
147
148 const TESTNET: &'static str = constants::testnet::HRP_UNIFIED_ADDRESS;
154
155 const REGTEST: &'static str = constants::regtest::HRP_UNIFIED_ADDRESS;
157
158 fn from_inner(receivers: Vec<Self::Item>) -> Self {
159 Self(receivers)
160 }
161}
162
163impl super::Encoding for Address {}
164impl super::Container for Address {
165 type Item = Receiver;
166
167 fn items_as_parsed(&self) -> &[Receiver] {
168 &self.0
169 }
170}
171
172#[cfg(any(test, feature = "test-dependencies"))]
173pub mod testing {
174 use alloc::vec::Vec;
175
176 use proptest::{
177 array::{uniform11, uniform20, uniform32},
178 collection::vec,
179 prelude::*,
180 sample::select,
181 strategy::Strategy,
182 };
183 use zcash_encoding::MAX_COMPACT_SIZE;
184
185 use super::{Address, Receiver};
186 use crate::unified::Typecode;
187
188 prop_compose! {
189 fn uniform43()(a in uniform11(0u8..), b in uniform32(0u8..)) -> [u8; 43] {
190 let mut c = [0; 43];
191 c[..11].copy_from_slice(&a);
192 c[11..].copy_from_slice(&b);
193 c
194 }
195 }
196
197 pub fn arb_transparent_typecode() -> impl Strategy<Value = Typecode> {
199 select(vec![Typecode::P2pkh, Typecode::P2sh])
200 }
201
202 pub fn arb_shielded_typecode() -> impl Strategy<Value = Typecode> {
204 prop_oneof![
205 Just(Typecode::Sapling),
206 Just(Typecode::Orchard),
207 ((<u32>::from(Typecode::Orchard) + 1)..MAX_COMPACT_SIZE).prop_map(Typecode::Unknown)
208 ]
209 }
210
211 pub fn arb_typecodes() -> impl Strategy<Value = Vec<Typecode>> {
215 prop::option::of(arb_transparent_typecode()).prop_flat_map(|transparent| {
216 prop::collection::hash_set(arb_shielded_typecode(), 1..4).prop_map(move |xs| {
217 let mut typecodes: Vec<_> = xs.into_iter().chain(transparent).collect();
218 typecodes.sort_unstable_by(Typecode::encoding_order);
219 typecodes
220 })
221 })
222 }
223
224 pub fn arb_unified_address_for_typecodes(
229 typecodes: Vec<Typecode>,
230 ) -> impl Strategy<Value = Vec<Receiver>> {
231 typecodes
232 .into_iter()
233 .map(|tc| match tc {
234 Typecode::P2pkh => uniform20(0u8..).prop_map(Receiver::P2pkh).boxed(),
235 Typecode::P2sh => uniform20(0u8..).prop_map(Receiver::P2sh).boxed(),
236 Typecode::Sapling => uniform43().prop_map(Receiver::Sapling).boxed(),
237 Typecode::Orchard => uniform43().prop_map(Receiver::Orchard).boxed(),
238 Typecode::Unknown(typecode) => vec(any::<u8>(), 32..256)
239 .prop_map(move |data| Receiver::Unknown { typecode, data })
240 .boxed(),
241 })
242 .collect::<Vec<_>>()
243 }
244
245 pub fn arb_unified_address() -> impl Strategy<Value = Address> {
250 arb_typecodes()
251 .prop_flat_map(arb_unified_address_for_typecodes)
252 .prop_map(Address)
253 }
254}
255
256#[cfg(any(test, feature = "test-dependencies"))]
257pub mod test_vectors;
258
259#[cfg(test)]
260mod tests {
261 use alloc::borrow::ToOwned;
262
263 use assert_matches::assert_matches;
264 use zcash_protocol::consensus::NetworkType;
265
266 use crate::{
267 kind::unified::{Container, Encoding, private::SealedContainer},
268 unified::address::testing::arb_unified_address,
269 };
270
271 use proptest::{prelude::*, sample::select};
272
273 use super::{Address, ParseError, Receiver, Typecode};
274
275 proptest! {
276 #[test]
277 fn ua_roundtrip(
278 network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
279 ua in arb_unified_address(),
280 ) {
281 let encoded = ua.encode(&network);
282 let decoded = Address::decode(&encoded);
283 prop_assert_eq!(&decoded, &Ok((network, ua)));
284 let reencoded = decoded.unwrap().1.encode(&network);
285 prop_assert_eq!(reencoded, encoded);
286 }
287 }
288
289 #[test]
290 fn padding() {
291 let invalid_padding = [
295 0xe6, 0x59, 0xd1, 0xed, 0xf7, 0x4b, 0xe3, 0x5e, 0x5a, 0x54, 0x0e, 0x41, 0x5d, 0x2f,
296 0x0c, 0x0d, 0x33, 0x42, 0xbd, 0xbe, 0x9f, 0x82, 0x62, 0x01, 0xc1, 0x1b, 0xd4, 0x1e,
297 0x42, 0x47, 0x86, 0x23, 0x05, 0x4b, 0x98, 0xd7, 0x76, 0x86, 0xa5, 0xe3, 0x1b, 0xd3,
298 0x03, 0xca, 0x24, 0x44, 0x8e, 0x72, 0xc1, 0x4a, 0xc6, 0xbf, 0x3f, 0x2b, 0xce, 0xa7,
299 0x7b, 0x28, 0x69, 0xc9, 0x84,
300 ];
301 assert_eq!(
302 Address::parse_internal(Address::MAINNET, &invalid_padding[..]),
303 Err(ParseError::InvalidEncoding(
304 "Invalid padding bytes".to_owned()
305 ))
306 );
307
308 let truncated_padding = [
310 0x9a, 0x56, 0x12, 0xa3, 0x43, 0x45, 0xe0, 0x82, 0x6c, 0xac, 0x24, 0x8b, 0x3b, 0x45,
311 0x72, 0x9a, 0x53, 0xd5, 0xf8, 0xda, 0xec, 0x07, 0x7c, 0xba, 0x9f, 0xa8, 0xd2, 0x97,
312 0x5b, 0xda, 0x73, 0x1b, 0xd2, 0xd1, 0x32, 0x6b, 0x7b, 0x36, 0xdd, 0x57, 0x84, 0x2a,
313 0xa0, 0x21, 0x23, 0x89, 0x73, 0x85, 0xe1, 0x4b, 0x3e, 0x95, 0xb7, 0xd4, 0x67, 0xbc,
314 0x4b, 0x31, 0xee, 0x5a,
315 ];
316 assert_eq!(
317 Address::parse_internal(Address::MAINNET, &truncated_padding[..]),
318 Err(ParseError::InvalidEncoding(
319 "Invalid padding bytes".to_owned()
320 ))
321 );
322 }
323
324 #[test]
325 fn truncated() {
326 let truncated_sapling_data = [
332 0xaa, 0xb0, 0x6e, 0x7b, 0x26, 0x7a, 0x22, 0x17, 0x39, 0xfa, 0x07, 0x69, 0xe9, 0x32,
333 0x2b, 0xac, 0x8c, 0x9e, 0x5e, 0x8a, 0xd9, 0x24, 0x06, 0x5a, 0x13, 0x79, 0x3a, 0x8d,
334 0xb4, 0x52, 0xfa, 0x18, 0x4e, 0x33, 0x4d, 0x8c, 0x17, 0x77, 0x4d, 0x63, 0x69, 0x34,
335 0x22, 0x70, 0x3a, 0xea, 0x30, 0x82, 0x5a, 0x6b, 0x37, 0xd1, 0x0d, 0xbe, 0x20, 0xab,
336 0x82, 0x86, 0x98, 0x34, 0x6a, 0xd8, 0x45, 0x40, 0xd0, 0x25, 0x60, 0xbf, 0x1e, 0xb6,
337 0xeb, 0x06, 0x85, 0x70, 0x4c, 0x42, 0xbc, 0x19, 0x14, 0xef, 0x7a, 0x05, 0xa0, 0x71,
338 0xb2, 0x63, 0x80, 0xbb, 0xdc, 0x12, 0x08, 0x48, 0x28, 0x8f, 0x1c, 0x9e, 0xc3, 0x42,
339 0xc6, 0x5e, 0x68, 0xa2, 0x78, 0x6c, 0x9e,
340 ];
341 assert_matches!(
342 Address::parse_internal(Address::MAINNET, &truncated_sapling_data[..]),
343 Err(ParseError::InvalidEncoding(_))
344 );
345
346 let truncated_after_sapling_typecode = [
348 0x87, 0x7a, 0xdf, 0x79, 0x6b, 0xe3, 0xb3, 0x40, 0xef, 0xe4, 0x5d, 0xc2, 0x91, 0xa2,
349 0x81, 0xfc, 0x7d, 0x76, 0xbb, 0xb0, 0x58, 0x98, 0x53, 0x59, 0xd3, 0x3f, 0xbc, 0x4b,
350 0x86, 0x59, 0x66, 0x62, 0x75, 0x92, 0xba, 0xcc, 0x31, 0x1e, 0x60, 0x02, 0x3b, 0xd8,
351 0x4c, 0xdf, 0x36, 0xa1, 0xac, 0x82, 0x57, 0xed, 0x0c, 0x98, 0x49, 0x8f, 0x49, 0x7e,
352 0xe6, 0x70, 0x36, 0x5b, 0x7b, 0x9e,
353 ];
354 assert_matches!(
355 Address::parse_internal(Address::MAINNET, &truncated_after_sapling_typecode[..]),
356 Err(ParseError::InvalidEncoding(_))
357 );
358 }
359
360 #[test]
361 fn duplicate_typecode() {
362 let ua = Address(vec![Receiver::Sapling([1; 43]), Receiver::Sapling([2; 43])]);
365 let encoded = ua.to_jumbled_bytes(Address::MAINNET);
366 assert_eq!(
367 Address::parse_internal(Address::MAINNET, &encoded[..]),
368 Err(ParseError::DuplicateTypecode(Typecode::Sapling))
369 );
370 }
371
372 #[test]
373 fn p2pkh_and_p2sh() {
374 let ua = Address(vec![Receiver::P2pkh([0; 20]), Receiver::P2sh([0; 20])]);
377 let encoded = ua.to_jumbled_bytes(Address::MAINNET);
378 assert_eq!(
380 Address::parse_internal(Address::MAINNET, &encoded[..]),
381 Err(ParseError::BothP2phkAndP2sh)
382 );
383 }
384
385 #[test]
386 fn addresses_out_of_order() {
387 let ua = Address(vec![Receiver::Sapling([0; 43]), Receiver::P2pkh([0; 20])]);
390 let encoded = ua.to_jumbled_bytes(Address::MAINNET);
391 assert_eq!(
393 Address::parse_internal(Address::MAINNET, &encoded[..]),
394 Err(ParseError::InvalidTypecodeOrder)
395 );
396 }
397
398 #[test]
399 fn only_transparent() {
400 let encoded = [
402 0xf0, 0x9e, 0x9d, 0x6e, 0xf5, 0xa6, 0xac, 0x16, 0x50, 0xf0, 0xdb, 0xe1, 0x2c, 0xa5,
403 0x36, 0x22, 0xa2, 0x04, 0x89, 0x86, 0xe9, 0x6a, 0x9b, 0xf3, 0xff, 0x6d, 0x2f, 0xe6,
404 0xea, 0xdb, 0xc5, 0x20, 0x62, 0xf9, 0x6f, 0xa9, 0x86, 0xcc,
405 ];
406
407 assert_matches!(
412 Address::parse_internal(Address::MAINNET, &encoded[..]),
413 Err(ParseError::InvalidEncoding(_))
414 );
415 }
416
417 #[test]
418 fn receivers_are_sorted() {
419 let ua = Address(vec![
421 Receiver::P2pkh([0; 20]),
422 Receiver::Orchard([0; 43]),
423 Receiver::Unknown {
424 typecode: 0xff,
425 data: vec![],
426 },
427 Receiver::Sapling([0; 43]),
428 ]);
429
430 assert_eq!(
432 ua.items(),
433 vec![
434 Receiver::Orchard([0; 43]),
435 Receiver::Sapling([0; 43]),
436 Receiver::P2pkh([0; 20]),
437 Receiver::Unknown {
438 typecode: 0xff,
439 data: vec![],
440 },
441 ]
442 )
443 }
444
445 #[test]
446 fn address_receiver_queries() {
447 use zcash_protocol::PoolType;
448
449 let ua = Address(vec![
451 Receiver::Orchard([0; 43]),
452 Receiver::Sapling([1; 43]),
453 Receiver::P2pkh([2; 20]),
454 ]);
455
456 assert!(ua.has_receiver_of_type(PoolType::ORCHARD));
458 assert!(ua.has_receiver_of_type(PoolType::SAPLING));
459 assert!(ua.has_receiver_of_type(PoolType::TRANSPARENT));
460
461 assert!(ua.can_receive_memo());
463
464 assert!(ua.contains_receiver(&Receiver::Orchard([0; 43])));
466 assert!(!ua.contains_receiver(&Receiver::Orchard([1; 43]))); let transparent_only = Address(vec![Receiver::P2pkh([0; 20])]);
470 assert!(!transparent_only.can_receive_memo());
471 assert!(!transparent_only.has_receiver_of_type(PoolType::ORCHARD));
472 assert!(!transparent_only.has_receiver_of_type(PoolType::SAPLING));
473 assert!(transparent_only.has_receiver_of_type(PoolType::TRANSPARENT));
474
475 let with_unknown = Address(vec![
477 Receiver::Orchard([0; 43]),
478 Receiver::Unknown {
479 typecode: 0xAA,
480 data: vec![0; 32],
481 },
482 ]);
483 assert!(!with_unknown.has_receiver_of_type(PoolType::SAPLING));
484 assert!(!with_unknown.has_receiver_of_type(PoolType::TRANSPARENT));
486 }
487}