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)]
107pub struct Address(pub(crate) Vec<Receiver>);
108
109impl Address {
110 pub fn has_receiver_of_type(&self, pool_type: PoolType) -> bool {
112 self.0.iter().any(|r| match r {
113 Receiver::Orchard(_) => pool_type == PoolType::ORCHARD,
114 Receiver::Sapling(_) => pool_type == PoolType::SAPLING,
115 Receiver::P2pkh(_) | Receiver::P2sh(_) => pool_type == PoolType::TRANSPARENT,
116 Receiver::Unknown { .. } => false,
117 })
118 }
119
120 pub fn contains_receiver(&self, receiver: &Receiver) -> bool {
122 self.0.contains(receiver)
123 }
124
125 pub fn can_receive_memo(&self) -> bool {
127 self.0
128 .iter()
129 .any(|r| matches!(r, Receiver::Sapling(_) | Receiver::Orchard(_)))
130 }
131}
132
133impl super::private::SealedContainer for Address {
134 const MAINNET: &'static str = constants::mainnet::HRP_UNIFIED_ADDRESS;
140
141 const TESTNET: &'static str = constants::testnet::HRP_UNIFIED_ADDRESS;
147
148 const REGTEST: &'static str = constants::regtest::HRP_UNIFIED_ADDRESS;
150
151 fn from_inner(receivers: Vec<Self::Item>) -> Self {
152 Self(receivers)
153 }
154}
155
156impl super::Encoding for Address {}
157impl super::Container for Address {
158 type Item = Receiver;
159
160 fn items_as_parsed(&self) -> &[Receiver] {
161 &self.0
162 }
163}
164
165#[cfg(any(test, feature = "test-dependencies"))]
166pub mod testing {
167 use alloc::vec::Vec;
168
169 use proptest::{
170 array::{uniform11, uniform20, uniform32},
171 collection::vec,
172 prelude::*,
173 sample::select,
174 strategy::Strategy,
175 };
176 use zcash_encoding::MAX_COMPACT_SIZE;
177
178 use super::{Address, Receiver};
179 use crate::unified::Typecode;
180
181 prop_compose! {
182 fn uniform43()(a in uniform11(0u8..), b in uniform32(0u8..)) -> [u8; 43] {
183 let mut c = [0; 43];
184 c[..11].copy_from_slice(&a);
185 c[11..].copy_from_slice(&b);
186 c
187 }
188 }
189
190 pub fn arb_transparent_typecode() -> impl Strategy<Value = Typecode> {
192 select(vec![Typecode::P2pkh, Typecode::P2sh])
193 }
194
195 pub fn arb_shielded_typecode() -> impl Strategy<Value = Typecode> {
197 prop_oneof![
198 Just(Typecode::Sapling),
199 Just(Typecode::Orchard),
200 ((<u32>::from(Typecode::Orchard) + 1)..MAX_COMPACT_SIZE).prop_map(Typecode::Unknown)
201 ]
202 }
203
204 pub fn arb_typecodes() -> impl Strategy<Value = Vec<Typecode>> {
208 prop::option::of(arb_transparent_typecode()).prop_flat_map(|transparent| {
209 prop::collection::hash_set(arb_shielded_typecode(), 1..4).prop_map(move |xs| {
210 let mut typecodes: Vec<_> = xs.into_iter().chain(transparent).collect();
211 typecodes.sort_unstable_by(Typecode::encoding_order);
212 typecodes
213 })
214 })
215 }
216
217 pub fn arb_unified_address_for_typecodes(
222 typecodes: Vec<Typecode>,
223 ) -> impl Strategy<Value = Vec<Receiver>> {
224 typecodes
225 .into_iter()
226 .map(|tc| match tc {
227 Typecode::P2pkh => uniform20(0u8..).prop_map(Receiver::P2pkh).boxed(),
228 Typecode::P2sh => uniform20(0u8..).prop_map(Receiver::P2sh).boxed(),
229 Typecode::Sapling => uniform43().prop_map(Receiver::Sapling).boxed(),
230 Typecode::Orchard => uniform43().prop_map(Receiver::Orchard).boxed(),
231 Typecode::Unknown(typecode) => vec(any::<u8>(), 32..256)
232 .prop_map(move |data| Receiver::Unknown { typecode, data })
233 .boxed(),
234 })
235 .collect::<Vec<_>>()
236 }
237
238 pub fn arb_unified_address() -> impl Strategy<Value = Address> {
243 arb_typecodes()
244 .prop_flat_map(arb_unified_address_for_typecodes)
245 .prop_map(Address)
246 }
247}
248
249#[cfg(any(test, feature = "test-dependencies"))]
250pub mod test_vectors;
251
252#[cfg(test)]
253mod tests {
254 use alloc::borrow::ToOwned;
255
256 use assert_matches::assert_matches;
257 use zcash_protocol::consensus::NetworkType;
258
259 use crate::{
260 kind::unified::{private::SealedContainer, Container, Encoding},
261 unified::address::testing::arb_unified_address,
262 };
263
264 use proptest::{prelude::*, sample::select};
265
266 use super::{Address, ParseError, Receiver, Typecode};
267
268 proptest! {
269 #[test]
270 fn ua_roundtrip(
271 network in select(vec![NetworkType::Main, NetworkType::Test, NetworkType::Regtest]),
272 ua in arb_unified_address(),
273 ) {
274 let encoded = ua.encode(&network);
275 let decoded = Address::decode(&encoded);
276 prop_assert_eq!(&decoded, &Ok((network, ua)));
277 let reencoded = decoded.unwrap().1.encode(&network);
278 prop_assert_eq!(reencoded, encoded);
279 }
280 }
281
282 #[test]
283 fn padding() {
284 let invalid_padding = [
288 0xe6, 0x59, 0xd1, 0xed, 0xf7, 0x4b, 0xe3, 0x5e, 0x5a, 0x54, 0x0e, 0x41, 0x5d, 0x2f,
289 0x0c, 0x0d, 0x33, 0x42, 0xbd, 0xbe, 0x9f, 0x82, 0x62, 0x01, 0xc1, 0x1b, 0xd4, 0x1e,
290 0x42, 0x47, 0x86, 0x23, 0x05, 0x4b, 0x98, 0xd7, 0x76, 0x86, 0xa5, 0xe3, 0x1b, 0xd3,
291 0x03, 0xca, 0x24, 0x44, 0x8e, 0x72, 0xc1, 0x4a, 0xc6, 0xbf, 0x3f, 0x2b, 0xce, 0xa7,
292 0x7b, 0x28, 0x69, 0xc9, 0x84,
293 ];
294 assert_eq!(
295 Address::parse_internal(Address::MAINNET, &invalid_padding[..]),
296 Err(ParseError::InvalidEncoding(
297 "Invalid padding bytes".to_owned()
298 ))
299 );
300
301 let truncated_padding = [
303 0x9a, 0x56, 0x12, 0xa3, 0x43, 0x45, 0xe0, 0x82, 0x6c, 0xac, 0x24, 0x8b, 0x3b, 0x45,
304 0x72, 0x9a, 0x53, 0xd5, 0xf8, 0xda, 0xec, 0x07, 0x7c, 0xba, 0x9f, 0xa8, 0xd2, 0x97,
305 0x5b, 0xda, 0x73, 0x1b, 0xd2, 0xd1, 0x32, 0x6b, 0x7b, 0x36, 0xdd, 0x57, 0x84, 0x2a,
306 0xa0, 0x21, 0x23, 0x89, 0x73, 0x85, 0xe1, 0x4b, 0x3e, 0x95, 0xb7, 0xd4, 0x67, 0xbc,
307 0x4b, 0x31, 0xee, 0x5a,
308 ];
309 assert_eq!(
310 Address::parse_internal(Address::MAINNET, &truncated_padding[..]),
311 Err(ParseError::InvalidEncoding(
312 "Invalid padding bytes".to_owned()
313 ))
314 );
315 }
316
317 #[test]
318 fn truncated() {
319 let truncated_sapling_data = [
325 0xaa, 0xb0, 0x6e, 0x7b, 0x26, 0x7a, 0x22, 0x17, 0x39, 0xfa, 0x07, 0x69, 0xe9, 0x32,
326 0x2b, 0xac, 0x8c, 0x9e, 0x5e, 0x8a, 0xd9, 0x24, 0x06, 0x5a, 0x13, 0x79, 0x3a, 0x8d,
327 0xb4, 0x52, 0xfa, 0x18, 0x4e, 0x33, 0x4d, 0x8c, 0x17, 0x77, 0x4d, 0x63, 0x69, 0x34,
328 0x22, 0x70, 0x3a, 0xea, 0x30, 0x82, 0x5a, 0x6b, 0x37, 0xd1, 0x0d, 0xbe, 0x20, 0xab,
329 0x82, 0x86, 0x98, 0x34, 0x6a, 0xd8, 0x45, 0x40, 0xd0, 0x25, 0x60, 0xbf, 0x1e, 0xb6,
330 0xeb, 0x06, 0x85, 0x70, 0x4c, 0x42, 0xbc, 0x19, 0x14, 0xef, 0x7a, 0x05, 0xa0, 0x71,
331 0xb2, 0x63, 0x80, 0xbb, 0xdc, 0x12, 0x08, 0x48, 0x28, 0x8f, 0x1c, 0x9e, 0xc3, 0x42,
332 0xc6, 0x5e, 0x68, 0xa2, 0x78, 0x6c, 0x9e,
333 ];
334 assert_matches!(
335 Address::parse_internal(Address::MAINNET, &truncated_sapling_data[..]),
336 Err(ParseError::InvalidEncoding(_))
337 );
338
339 let truncated_after_sapling_typecode = [
341 0x87, 0x7a, 0xdf, 0x79, 0x6b, 0xe3, 0xb3, 0x40, 0xef, 0xe4, 0x5d, 0xc2, 0x91, 0xa2,
342 0x81, 0xfc, 0x7d, 0x76, 0xbb, 0xb0, 0x58, 0x98, 0x53, 0x59, 0xd3, 0x3f, 0xbc, 0x4b,
343 0x86, 0x59, 0x66, 0x62, 0x75, 0x92, 0xba, 0xcc, 0x31, 0x1e, 0x60, 0x02, 0x3b, 0xd8,
344 0x4c, 0xdf, 0x36, 0xa1, 0xac, 0x82, 0x57, 0xed, 0x0c, 0x98, 0x49, 0x8f, 0x49, 0x7e,
345 0xe6, 0x70, 0x36, 0x5b, 0x7b, 0x9e,
346 ];
347 assert_matches!(
348 Address::parse_internal(Address::MAINNET, &truncated_after_sapling_typecode[..]),
349 Err(ParseError::InvalidEncoding(_))
350 );
351 }
352
353 #[test]
354 fn duplicate_typecode() {
355 let ua = Address(vec![Receiver::Sapling([1; 43]), Receiver::Sapling([2; 43])]);
358 let encoded = ua.to_jumbled_bytes(Address::MAINNET);
359 assert_eq!(
360 Address::parse_internal(Address::MAINNET, &encoded[..]),
361 Err(ParseError::DuplicateTypecode(Typecode::Sapling))
362 );
363 }
364
365 #[test]
366 fn p2pkh_and_p2sh() {
367 let ua = Address(vec![Receiver::P2pkh([0; 20]), Receiver::P2sh([0; 20])]);
370 let encoded = ua.to_jumbled_bytes(Address::MAINNET);
371 assert_eq!(
373 Address::parse_internal(Address::MAINNET, &encoded[..]),
374 Err(ParseError::BothP2phkAndP2sh)
375 );
376 }
377
378 #[test]
379 fn addresses_out_of_order() {
380 let ua = Address(vec![Receiver::Sapling([0; 43]), Receiver::P2pkh([0; 20])]);
383 let encoded = ua.to_jumbled_bytes(Address::MAINNET);
384 assert_eq!(
386 Address::parse_internal(Address::MAINNET, &encoded[..]),
387 Err(ParseError::InvalidTypecodeOrder)
388 );
389 }
390
391 #[test]
392 fn only_transparent() {
393 let encoded = [
395 0xf0, 0x9e, 0x9d, 0x6e, 0xf5, 0xa6, 0xac, 0x16, 0x50, 0xf0, 0xdb, 0xe1, 0x2c, 0xa5,
396 0x36, 0x22, 0xa2, 0x04, 0x89, 0x86, 0xe9, 0x6a, 0x9b, 0xf3, 0xff, 0x6d, 0x2f, 0xe6,
397 0xea, 0xdb, 0xc5, 0x20, 0x62, 0xf9, 0x6f, 0xa9, 0x86, 0xcc,
398 ];
399
400 assert_matches!(
405 Address::parse_internal(Address::MAINNET, &encoded[..]),
406 Err(ParseError::InvalidEncoding(_))
407 );
408 }
409
410 #[test]
411 fn receivers_are_sorted() {
412 let ua = Address(vec![
414 Receiver::P2pkh([0; 20]),
415 Receiver::Orchard([0; 43]),
416 Receiver::Unknown {
417 typecode: 0xff,
418 data: vec![],
419 },
420 Receiver::Sapling([0; 43]),
421 ]);
422
423 assert_eq!(
425 ua.items(),
426 vec![
427 Receiver::Orchard([0; 43]),
428 Receiver::Sapling([0; 43]),
429 Receiver::P2pkh([0; 20]),
430 Receiver::Unknown {
431 typecode: 0xff,
432 data: vec![],
433 },
434 ]
435 )
436 }
437}