1use alloc::string::{String, ToString};
4use alloc::vec::Vec;
5use core::cmp;
6use core::convert::{TryFrom, TryInto};
7use core::fmt;
8use core::num::TryFromIntError;
9
10#[cfg(feature = "std")]
11use std::error::Error;
12
13use bech32::{primitives::decode::CheckedHrpstring, Bech32m, Checksum, Hrp};
14
15use zcash_protocol::consensus::NetworkType;
16
17pub(crate) mod address;
18pub(crate) mod fvk;
19pub(crate) mod ivk;
20
21pub use address::{Address, Receiver};
22pub use fvk::{Fvk, Ufvk};
23pub use ivk::{Ivk, Uivk};
24
25const PADDING_LEN: usize = 16;
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32pub enum Typecode {
33 P2pkh,
35 P2sh,
39 Sapling,
41 Orchard,
43 Unknown(u32),
45}
46
47impl Typecode {
48 pub fn preference_order(a: &Self, b: &Self) -> cmp::Ordering {
49 match (a, b) {
50 (Self::Orchard, Self::Orchard)
52 | (Self::Sapling, Self::Sapling)
53 | (Self::P2sh, Self::P2sh)
54 | (Self::P2pkh, Self::P2pkh) => cmp::Ordering::Equal,
55
56 (Self::Unknown(a), Self::Unknown(b)) => b.cmp(a),
61
62 (Self::Orchard, _) => cmp::Ordering::Less,
65 (_, Self::Orchard) => cmp::Ordering::Greater,
66
67 (Self::Sapling, _) => cmp::Ordering::Less,
68 (_, Self::Sapling) => cmp::Ordering::Greater,
69
70 (Self::P2sh, _) => cmp::Ordering::Less,
71 (_, Self::P2sh) => cmp::Ordering::Greater,
72
73 (Self::P2pkh, _) => cmp::Ordering::Less,
74 (_, Self::P2pkh) => cmp::Ordering::Greater,
75 }
76 }
77
78 pub fn encoding_order(a: &Self, b: &Self) -> cmp::Ordering {
79 u32::from(*a).cmp(&u32::from(*b))
80 }
81}
82
83impl TryFrom<u32> for Typecode {
84 type Error = ParseError;
85
86 fn try_from(typecode: u32) -> Result<Self, Self::Error> {
87 match typecode {
88 0x00 => Ok(Typecode::P2pkh),
89 0x01 => Ok(Typecode::P2sh),
90 0x02 => Ok(Typecode::Sapling),
91 0x03 => Ok(Typecode::Orchard),
92 0x04..=0x02000000 => Ok(Typecode::Unknown(typecode)),
93 0x02000001..=u32::MAX => Err(ParseError::InvalidTypecodeValue(typecode as u64)),
94 }
95 }
96}
97
98impl From<Typecode> for u32 {
99 fn from(t: Typecode) -> Self {
100 match t {
101 Typecode::P2pkh => 0x00,
102 Typecode::P2sh => 0x01,
103 Typecode::Sapling => 0x02,
104 Typecode::Orchard => 0x03,
105 Typecode::Unknown(typecode) => typecode,
106 }
107 }
108}
109
110impl TryFrom<Typecode> for usize {
111 type Error = TryFromIntError;
112 fn try_from(t: Typecode) -> Result<Self, Self::Error> {
113 u32::from(t).try_into()
114 }
115}
116
117impl Typecode {
118 fn is_transparent(&self) -> bool {
119 matches!(self, Typecode::P2pkh | Typecode::P2sh)
122 }
123}
124
125#[derive(Debug, PartialEq, Eq)]
127pub enum ParseError {
128 BothP2phkAndP2sh,
130 DuplicateTypecode(Typecode),
132 InvalidTypecodeValue(u64),
134 InvalidEncoding(String),
136 InvalidTypecodeOrder,
138 OnlyTransparent,
140 NotUnified,
142 UnknownPrefix(String),
144}
145
146impl fmt::Display for ParseError {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 match self {
149 ParseError::BothP2phkAndP2sh => write!(f, "UA contains both P2PKH and P2SH items"),
150 ParseError::DuplicateTypecode(c) => write!(f, "Duplicate typecode {}", u32::from(*c)),
151 ParseError::InvalidTypecodeValue(v) => write!(f, "Typecode value out of range {}", v),
152 ParseError::InvalidEncoding(msg) => write!(f, "Invalid encoding: {}", msg),
153 ParseError::InvalidTypecodeOrder => write!(f, "Items are out of order."),
154 ParseError::OnlyTransparent => write!(f, "UA only contains transparent items"),
155 ParseError::NotUnified => write!(f, "Address is not Bech32m encoded"),
156 ParseError::UnknownPrefix(s) => {
157 write!(f, "Unrecognized Bech32m human-readable prefix: {}", s)
158 }
159 }
160 }
161}
162
163#[cfg(feature = "std")]
164impl Error for ParseError {}
165
166pub(crate) mod private {
167 use alloc::borrow::ToOwned;
168 use alloc::vec::Vec;
169 use core::cmp;
170 use core::convert::{TryFrom, TryInto};
171 use core2::io::Write;
172
173 use super::{ParseError, Typecode, PADDING_LEN};
174 use zcash_encoding::CompactSize;
175 use zcash_protocol::consensus::NetworkType;
176
177 pub trait SealedItem: for<'a> TryFrom<(u32, &'a [u8]), Error = ParseError> + Clone {
179 fn typecode(&self) -> Typecode;
180 fn data(&self) -> &[u8];
181
182 fn preference_order(a: &Self, b: &Self) -> cmp::Ordering {
183 match Typecode::preference_order(&a.typecode(), &b.typecode()) {
184 cmp::Ordering::Equal => a.data().cmp(b.data()),
185 res => res,
186 }
187 }
188
189 fn encoding_order(a: &Self, b: &Self) -> cmp::Ordering {
190 match Typecode::encoding_order(&a.typecode(), &b.typecode()) {
191 cmp::Ordering::Equal => a.data().cmp(b.data()),
192 res => res,
193 }
194 }
195
196 fn write_raw_encoding<W: Write>(&self, mut writer: W) {
197 let data = self.data();
198 CompactSize::write(
199 &mut writer,
200 <u32>::from(self.typecode()).try_into().unwrap(),
201 )
202 .unwrap();
203 CompactSize::write(&mut writer, data.len()).unwrap();
204 writer.write_all(data).unwrap();
205 }
206 }
207
208 pub trait SealedContainer: super::Container + core::marker::Sized {
210 const MAINNET: &'static str;
211 const TESTNET: &'static str;
212 const REGTEST: &'static str;
213
214 fn from_inner(items: Vec<Self::Item>) -> Self;
218
219 fn network_hrp(network: &NetworkType) -> &'static str {
220 match network {
221 NetworkType::Main => Self::MAINNET,
222 NetworkType::Test => Self::TESTNET,
223 NetworkType::Regtest => Self::REGTEST,
224 }
225 }
226
227 fn hrp_network(hrp: &str) -> Option<NetworkType> {
228 if hrp == Self::MAINNET {
229 Some(NetworkType::Main)
230 } else if hrp == Self::TESTNET {
231 Some(NetworkType::Test)
232 } else if hrp == Self::REGTEST {
233 Some(NetworkType::Regtest)
234 } else {
235 None
236 }
237 }
238
239 fn write_raw_encoding<W: Write>(&self, mut writer: W) {
240 for item in self.items_as_parsed() {
241 item.write_raw_encoding(&mut writer);
242 }
243 }
244
245 fn to_jumbled_bytes(&self, hrp: &str) -> Vec<u8> {
247 assert!(hrp.len() <= PADDING_LEN);
248
249 let mut padded = Vec::new();
250 self.write_raw_encoding(&mut padded);
251
252 let mut padding = [0u8; PADDING_LEN];
253 padding[0..hrp.len()].copy_from_slice(hrp.as_bytes());
254 padded.write_all(&padding).unwrap();
255
256 f4jumble::f4jumble(&padded)
257 .unwrap_or_else(|e| panic!("f4jumble failed on {:?}: {}", padded, e))
258 }
259
260 fn parse_items<T: Into<Vec<u8>>>(hrp: &str, buf: T) -> Result<Vec<Self::Item>, ParseError> {
262 fn read_receiver<R: SealedItem>(
263 mut cursor: &mut core2::io::Cursor<&[u8]>,
264 ) -> Result<R, ParseError> {
265 let typecode = CompactSize::read(&mut cursor)
266 .map(|v| u32::try_from(v).expect("CompactSize::read enforces MAX_SIZE limit"))
267 .map_err(|e| {
268 ParseError::InvalidEncoding(format!(
269 "Failed to deserialize CompactSize-encoded typecode {}",
270 e
271 ))
272 })?;
273 let length = CompactSize::read(&mut cursor).map_err(|e| {
274 ParseError::InvalidEncoding(format!(
275 "Failed to deserialize CompactSize-encoded length {}",
276 e
277 ))
278 })?;
279 let addr_end = cursor.position().checked_add(length).ok_or_else(|| {
280 ParseError::InvalidEncoding(format!(
281 "Length value {} caused an overflow error",
282 length
283 ))
284 })?;
285 let buf = cursor.get_ref();
286 if (buf.len() as u64) < addr_end {
287 return Err(ParseError::InvalidEncoding(format!(
288 "Truncated: unable to read {} bytes of item data",
289 length
290 )));
291 }
292 let result = R::try_from((
293 typecode,
294 &buf[cursor.position() as usize..addr_end as usize],
295 ));
296 cursor.set_position(addr_end);
297 result
298 }
299
300 let mut encoded = buf.into();
302 f4jumble::f4jumble_inv_mut(&mut encoded[..]).map_err(|e| {
303 ParseError::InvalidEncoding(format!("F4Jumble decoding failed: {}", e))
304 })?;
305
306 if hrp.len() > 16 {
308 return Err(ParseError::InvalidEncoding(
309 "Invalid human-readable part".to_owned(),
310 ));
311 }
312 let mut expected_padding = [0; PADDING_LEN];
313 expected_padding[0..hrp.len()].copy_from_slice(hrp.as_bytes());
314 let encoded = match encoded.split_at(encoded.len() - PADDING_LEN) {
315 (encoded, tail) if tail == expected_padding => Ok(encoded),
316 _ => Err(ParseError::InvalidEncoding(
317 "Invalid padding bytes".to_owned(),
318 )),
319 }?;
320
321 let mut cursor = core2::io::Cursor::new(encoded);
322 let mut result = vec![];
323 while cursor.position() < encoded.len().try_into().unwrap() {
324 result.push(read_receiver(&mut cursor)?);
325 }
326 assert_eq!(cursor.position(), encoded.len().try_into().unwrap());
327
328 Ok(result)
329 }
330
331 fn try_from_items_internal(items: Vec<Self::Item>) -> Result<Self, ParseError> {
334 assert!(u32::from(Typecode::P2sh) == u32::from(Typecode::P2pkh) + 1);
335
336 let mut only_transparent = true;
337 let mut prev_code = None; for item in &items {
339 let t = item.typecode();
340 let t_code = Some(u32::from(t));
341 if t_code < prev_code {
342 return Err(ParseError::InvalidTypecodeOrder);
343 } else if t_code == prev_code {
344 return Err(ParseError::DuplicateTypecode(t));
345 } else if t == Typecode::P2sh && prev_code == Some(u32::from(Typecode::P2pkh)) {
346 return Err(ParseError::BothP2phkAndP2sh);
349 } else {
350 prev_code = t_code;
351 only_transparent = only_transparent && t.is_transparent();
352 }
353 }
354
355 if only_transparent {
356 Err(ParseError::OnlyTransparent)
357 } else {
358 Ok(Self::from_inner(items))
360 }
361 }
362
363 fn parse_internal<T: Into<Vec<u8>>>(hrp: &str, buf: T) -> Result<Self, ParseError> {
364 Self::parse_items(hrp, buf).and_then(Self::try_from_items_internal)
365 }
366 }
367}
368
369use private::SealedItem;
370
371#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
377pub enum Bech32mZip316 {}
378impl Checksum for Bech32mZip316 {
379 type MidstateRepr = <Bech32m as Checksum>::MidstateRepr;
380 const CODE_LENGTH: usize = 4194368;
382 const CHECKSUM_LENGTH: usize = Bech32m::CHECKSUM_LENGTH;
383 const GENERATOR_SH: [u32; 5] = Bech32m::GENERATOR_SH;
384 const TARGET_RESIDUE: u32 = Bech32m::TARGET_RESIDUE;
385}
386
387pub trait Encoding: private::SealedContainer {
389 fn try_from_items(mut items: Vec<Self::Item>) -> Result<Self, ParseError> {
400 items.sort_unstable_by(Self::Item::encoding_order);
401 Self::try_from_items_internal(items)
402 }
403
404 fn decode(s: &str) -> Result<(NetworkType, Self), ParseError> {
408 if let Ok(parsed) = CheckedHrpstring::new::<Bech32mZip316>(s) {
409 let hrp = parsed.hrp();
410 let hrp = hrp.as_str();
411 let net =
413 Self::hrp_network(hrp).ok_or_else(|| ParseError::UnknownPrefix(hrp.to_string()))?;
414
415 let data = parsed.byte_iter().collect::<Vec<_>>();
416
417 Self::parse_internal(hrp, data).map(|value| (net, value))
418 } else {
419 Err(ParseError::NotUnified)
420 }
421 }
422
423 fn encode(&self, network: &NetworkType) -> String {
428 let hrp = Self::network_hrp(network);
429 bech32::encode::<Bech32mZip316>(Hrp::parse_unchecked(hrp), &self.to_jumbled_bytes(hrp))
430 .expect("F4Jumble ensures length is short enough by construction")
431 }
432}
433
434pub trait Container {
436 type Item: Item;
438
439 fn items(&self) -> Vec<Self::Item> {
441 let mut items = self.items_as_parsed().to_vec();
442 items.sort_unstable_by(Self::Item::preference_order);
445 items
446 }
447
448 fn items_as_parsed(&self) -> &[Self::Item];
452}
453
454pub trait Item: SealedItem {
456 fn typed_encoding(&self) -> Vec<u8> {
462 let mut ret = vec![];
463 self.write_raw_encoding(&mut ret);
464 ret
465 }
466}
467
468impl<T: SealedItem> Item for T {}