1use bech32::{Variant, u5};
2use chia_protocol::{Bytes, Bytes32};
3use thiserror::Error;
4
5#[derive(Error, Debug, Clone, PartialEq, Eq)]
6pub enum Bech32Error {
7 #[error("not bech32m encoded")]
8 InvalidFormat,
9
10 #[error("expected 32 bytes, found {0}")]
11 WrongLength(usize),
12
13 #[error("expected prefix {1}, found {0}")]
14 WrongPrefix(String, String),
15
16 #[error("bech32 error: {0}")]
17 Decode(#[from] bech32::Error),
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Bech32 {
22 pub data: Bytes,
23 pub prefix: String,
24}
25
26impl Bech32 {
27 pub fn new(data: Bytes, prefix: String) -> Self {
28 Self { data, prefix }
29 }
30
31 pub fn decode(address: &str) -> Result<Self, Bech32Error> {
32 let (hrp, data, variant) = bech32::decode(address)?;
33
34 if variant != Variant::Bech32m {
35 return Err(Bech32Error::InvalidFormat);
36 }
37
38 let data = bech32::convert_bits(&data, 5, 8, false)?;
39
40 Ok(Self {
41 data: data.into(),
42 prefix: hrp,
43 })
44 }
45
46 pub fn encode(&self) -> Result<String, Bech32Error> {
47 let data = bech32::convert_bits(&self.data, 8, 5, true)
48 .unwrap()
49 .into_iter()
50 .map(u5::try_from_u8)
51 .collect::<Result<Vec<_>, bech32::Error>>()?;
52 Ok(bech32::encode(&self.prefix, data, Variant::Bech32m)?)
53 }
54
55 pub fn expect_prefix(self, prefix: &str) -> Result<Bytes, Bech32Error> {
56 if self.prefix != prefix {
57 return Err(Bech32Error::WrongPrefix(self.prefix, prefix.to_string()));
58 }
59 Ok(self.data)
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct Address {
65 pub puzzle_hash: Bytes32,
66 pub prefix: String,
67}
68
69impl Address {
70 pub fn new(puzzle_hash: Bytes32, prefix: String) -> Self {
71 Self {
72 puzzle_hash,
73 prefix,
74 }
75 }
76
77 pub fn decode(address: &str) -> Result<Self, Bech32Error> {
78 Bech32::decode(address).and_then(TryInto::try_into)
79 }
80
81 pub fn encode(&self) -> Result<String, Bech32Error> {
82 Bech32::from(self.clone()).encode()
83 }
84
85 pub fn expect_prefix(self, prefix: &str) -> Result<Bytes32, Bech32Error> {
86 if self.prefix != prefix {
87 return Err(Bech32Error::WrongPrefix(self.prefix, prefix.to_string()));
88 }
89 Ok(self.puzzle_hash)
90 }
91}
92
93impl TryFrom<Bech32> for Address {
94 type Error = Bech32Error;
95
96 fn try_from(bech32: Bech32) -> Result<Self, Self::Error> {
97 let len = bech32.data.len();
98 Ok(Self::new(
99 bech32
100 .data
101 .try_into()
102 .map_err(|_| Bech32Error::WrongLength(len))?,
103 bech32.prefix,
104 ))
105 }
106}
107
108impl From<Address> for Bech32 {
109 fn from(address: Address) -> Self {
110 Bech32::new(address.puzzle_hash.into(), address.prefix)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 fn check_addr(expected: &str) {
119 let info = Address::decode(expected).unwrap();
120 let actual = info.encode().unwrap();
121 assert_eq!(actual, expected);
122 }
123
124 #[test]
125 fn test_addresses() {
126 check_addr("xch1a0t57qn6uhe7tzjlxlhwy2qgmuxvvft8gnfzmg5detg0q9f3yc3s2apz0h");
127 check_addr("xch1ftxk2v033kv94ueucp0a34sgt9398vle7l7g3q9k4leedjmmdysqvv6q96");
128 check_addr("xch1ay273ctc9c6nxmzmzsup28scrce8ney84j4nlewdlaxqs22v53ksxgf38f");
129 check_addr("xch1avnwmy2fuesq7h2jnxehlrs9msrad9uuvrhms35k2pqwmjv56y5qk7zm6v");
130 }
131
132 #[test]
133 fn test_invalid_addresses() {
134 assert_eq!(
135 Address::decode("hello there!"),
136 Err(Bech32Error::Decode(bech32::Error::MissingSeparator))
137 );
138 assert_eq!(
139 Address::decode("bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"),
140 Err(Bech32Error::InvalidFormat)
141 );
142 }
143}