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