chia_sdk_utils/
address.rs

1use bech32::{u5, Variant};
2use chia_protocol::Bytes32;
3use thiserror::Error;
4
5#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
6pub enum AddressError {
7    #[error("encoding is not bech32m")]
8    InvalidFormat,
9
10    #[error("wrong length, expected 32 bytes but found {0}")]
11    WrongLength(usize),
12
13    #[error("error when decoding address: {0}")]
14    Decode(#[from] bech32::Error),
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Address {
19    pub puzzle_hash: Bytes32,
20    pub prefix: String,
21}
22
23impl Address {
24    pub fn new(puzzle_hash: Bytes32, prefix: String) -> Self {
25        Self {
26            puzzle_hash,
27            prefix,
28        }
29    }
30
31    pub fn decode(address: &str) -> Result<Self, AddressError> {
32        let (hrp, data, variant) = bech32::decode(address)?;
33
34        if variant != Variant::Bech32m {
35            return Err(AddressError::InvalidFormat);
36        }
37
38        let data = bech32::convert_bits(&data, 5, 8, false)?;
39        let length = data.len();
40        let puzzle_hash = data
41            .try_into()
42            .map_err(|_| AddressError::WrongLength(length))?;
43
44        Ok(Self {
45            puzzle_hash,
46            prefix: hrp,
47        })
48    }
49
50    pub fn encode(&self) -> Result<String, AddressError> {
51        let data = bech32::convert_bits(&self.puzzle_hash, 8, 5, true)
52            .unwrap()
53            .into_iter()
54            .map(u5::try_from_u8)
55            .collect::<Result<Vec<_>, bech32::Error>>()?;
56        Ok(bech32::encode(&self.prefix, data, Variant::Bech32m)?)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    fn check_addr(expected: &str) {
65        let info = Address::decode(expected).unwrap();
66        let actual = info.encode().unwrap();
67        assert_eq!(actual, expected);
68    }
69
70    #[test]
71    fn test_addresses() {
72        check_addr("xch1a0t57qn6uhe7tzjlxlhwy2qgmuxvvft8gnfzmg5detg0q9f3yc3s2apz0h");
73        check_addr("xch1ftxk2v033kv94ueucp0a34sgt9398vle7l7g3q9k4leedjmmdysqvv6q96");
74        check_addr("xch1ay273ctc9c6nxmzmzsup28scrce8ney84j4nlewdlaxqs22v53ksxgf38f");
75        check_addr("xch1avnwmy2fuesq7h2jnxehlrs9msrad9uuvrhms35k2pqwmjv56y5qk7zm6v");
76    }
77
78    #[test]
79    fn test_invalid_addresses() {
80        assert_eq!(
81            Address::decode("hello there!"),
82            Err(AddressError::Decode(bech32::Error::MissingSeparator))
83        );
84        assert_eq!(
85            Address::decode("bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"),
86            Err(AddressError::InvalidFormat)
87        );
88    }
89}