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::{Bech32m, Checksum, Hrp, primitives::decode::CheckedHrpstring};
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(u64::from(typecode))),
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 corez::io::Write;
172
173 use super::{PADDING_LEN, ParseError, Typecode};
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 corez::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 {e}"
270 ))
271 })?;
272 let length = CompactSize::read(&mut cursor).map_err(|e| {
273 ParseError::InvalidEncoding(format!(
274 "Failed to deserialize CompactSize-encoded length {e}"
275 ))
276 })?;
277 let addr_end = cursor.position().checked_add(length).ok_or_else(|| {
278 ParseError::InvalidEncoding(format!(
279 "Length value {length} caused an overflow error"
280 ))
281 })?;
282 let buf = cursor.get_ref();
283 if (buf.len() as u64) < addr_end {
284 return Err(ParseError::InvalidEncoding(format!(
285 "Truncated: unable to read {length} bytes of item data"
286 )));
287 }
288 let result = R::try_from((
289 typecode,
290 &buf[cursor.position() as usize..addr_end as usize],
291 ));
292 cursor.set_position(addr_end);
293 result
294 }
295
296 let mut encoded = buf.into();
298 f4jumble::f4jumble_inv_mut(&mut encoded[..]).map_err(|e| {
299 ParseError::InvalidEncoding(format!("F4Jumble decoding failed: {e}"))
300 })?;
301
302 if hrp.len() > 16 {
304 return Err(ParseError::InvalidEncoding(
305 "Invalid human-readable part".to_owned(),
306 ));
307 }
308 let mut expected_padding = [0; PADDING_LEN];
309 expected_padding[0..hrp.len()].copy_from_slice(hrp.as_bytes());
310 let encoded = match encoded.split_at(encoded.len() - PADDING_LEN) {
311 (encoded, tail) if tail == expected_padding => Ok(encoded),
312 _ => Err(ParseError::InvalidEncoding(
313 "Invalid padding bytes".to_owned(),
314 )),
315 }?;
316
317 let mut cursor = corez::io::Cursor::new(encoded);
318 let mut result = vec![];
319 while cursor.position() < encoded.len().try_into().unwrap() {
320 result.push(read_receiver(&mut cursor)?);
321 }
322 assert_eq!(cursor.position(), encoded.len().try_into().unwrap());
323
324 Ok(result)
325 }
326
327 fn try_from_items_internal(items: Vec<Self::Item>) -> Result<Self, ParseError> {
330 assert!(u32::from(Typecode::P2sh) == u32::from(Typecode::P2pkh) + 1);
331
332 let mut only_transparent = true;
333 let mut prev_code = None; for item in &items {
335 let t = item.typecode();
336 let t_code = Some(u32::from(t));
337 if t_code < prev_code {
338 return Err(ParseError::InvalidTypecodeOrder);
339 } else if t_code == prev_code {
340 return Err(ParseError::DuplicateTypecode(t));
341 } else if t == Typecode::P2sh && prev_code == Some(u32::from(Typecode::P2pkh)) {
342 return Err(ParseError::BothP2phkAndP2sh);
345 } else {
346 prev_code = t_code;
347 only_transparent = only_transparent && t.is_transparent();
348 }
349 }
350
351 if only_transparent {
352 Err(ParseError::OnlyTransparent)
353 } else {
354 Ok(Self::from_inner(items))
356 }
357 }
358
359 fn parse_internal<T: Into<Vec<u8>>>(hrp: &str, buf: T) -> Result<Self, ParseError> {
360 Self::parse_items(hrp, buf).and_then(Self::try_from_items_internal)
361 }
362 }
363}
364
365use private::SealedItem;
366
367#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
373pub enum Bech32mZip316 {}
374impl Checksum for Bech32mZip316 {
375 type MidstateRepr = <Bech32m as Checksum>::MidstateRepr;
376 const CODE_LENGTH: usize = 4194368;
378 const CHECKSUM_LENGTH: usize = Bech32m::CHECKSUM_LENGTH;
379 const GENERATOR_SH: [u32; 5] = Bech32m::GENERATOR_SH;
380 const TARGET_RESIDUE: u32 = Bech32m::TARGET_RESIDUE;
381}
382
383pub trait Encoding: private::SealedContainer {
385 fn try_from_items(mut items: Vec<Self::Item>) -> Result<Self, ParseError> {
396 items.sort_unstable_by(Self::Item::encoding_order);
397 Self::try_from_items_internal(items)
398 }
399
400 fn decode(s: &str) -> Result<(NetworkType, Self), ParseError> {
404 if let Ok(parsed) = CheckedHrpstring::new::<Bech32mZip316>(s) {
405 let hrp = parsed.hrp();
406 let hrp = hrp.as_str();
407 let net =
409 Self::hrp_network(hrp).ok_or_else(|| ParseError::UnknownPrefix(hrp.to_string()))?;
410
411 let data = parsed.byte_iter().collect::<Vec<_>>();
412
413 Self::parse_internal(hrp, data).map(|value| (net, value))
414 } else {
415 Err(ParseError::NotUnified)
416 }
417 }
418
419 fn encode(&self, network: &NetworkType) -> String {
424 let hrp = Self::network_hrp(network);
425 bech32::encode::<Bech32mZip316>(Hrp::parse_unchecked(hrp), &self.to_jumbled_bytes(hrp))
426 .expect("F4Jumble ensures length is short enough by construction")
427 }
428}
429
430pub trait Container {
432 type Item: Item;
434
435 fn items(&self) -> Vec<Self::Item> {
437 let mut items = self.items_as_parsed().to_vec();
438 items.sort_unstable_by(Self::Item::preference_order);
441 items
442 }
443
444 fn items_as_parsed(&self) -> &[Self::Item];
448}
449
450pub trait Item: SealedItem {
452 fn typed_encoding(&self) -> Vec<u8> {
458 let mut ret = vec![];
459 self.write_raw_encoding(&mut ret);
460 ret
461 }
462}
463
464impl<T: SealedItem> Item for T {}