snarkvm_console_types_address/
parse.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18static ADDRESS_PREFIX: &str = "aleo";
19
20impl<E: Environment> Parser for Address<E> {
21    /// Parses a string into an address.
22    #[inline]
23    fn parse(string: &str) -> ParserResult<Self> {
24        // Prepare a parser for the Aleo address.
25        let parse_address = recognize(pair(
26            tag("aleo1"),
27            many1(terminated(one_of("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), many0(char('_')))),
28        ));
29
30        // Parse the address from the string.
31        map_res(parse_address, |address: &str| -> Result<_, Error> { Self::from_str(&address.replace('_', "")) })(
32            string,
33        )
34    }
35}
36
37impl<E: Environment> FromStr for Address<E> {
38    type Err = Error;
39
40    /// Reads in an account address string.
41    fn from_str(address: &str) -> Result<Self, Self::Err> {
42        // Ensure the address string length is 63 characters.
43        if address.len() != 63 {
44            bail!("Invalid account address length: found {}, expected 63", address.len())
45        }
46        // Decode the address string from bech32m.
47        let (hrp, data, variant) = bech32::decode(address)?;
48        if hrp != ADDRESS_PREFIX {
49            bail!("Failed to decode address: '{hrp}' is an invalid prefix")
50        } else if data.is_empty() {
51            bail!("Failed to decode address: data field is empty")
52        } else if variant != bech32::Variant::Bech32m {
53            bail!("Found an address that is not bech32m encoded: {address}");
54        }
55        // Decode the address data from u5 to u8, and into an account address.
56        Ok(Self::read_le(&Vec::from_base32(&data)?[..])?)
57    }
58}
59
60impl<E: Environment> Debug for Address<E> {
61    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
62        Display::fmt(self, f)
63    }
64}
65
66impl<E: Environment> Display for Address<E> {
67    /// Writes an account address as a bech32m string.
68    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
69        // Convert the address to bytes.
70        let bytes = self.to_bytes_le().map_err(|_| fmt::Error)?;
71        // Encode the bytes into bech32m.
72        let string =
73            bech32::encode(ADDRESS_PREFIX, bytes.to_base32(), bech32::Variant::Bech32m).map_err(|_| fmt::Error)?;
74        // Output the string.
75        Display::fmt(&string, f)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use snarkvm_console_network_environment::Console;
83
84    type CurrentEnvironment = Console;
85
86    const ITERATIONS: u64 = 1_000;
87
88    #[test]
89    fn test_parse() -> Result<()> {
90        // Ensure type and empty value fails.
91        assert!(Address::<CurrentEnvironment>::parse(Address::<CurrentEnvironment>::type_name()).is_err());
92        assert!(Address::<CurrentEnvironment>::parse("").is_err());
93
94        let mut rng = TestRng::default();
95
96        for _ in 0..ITERATIONS {
97            // Sample a new address.
98            let address = Address::<CurrentEnvironment>::rand(&mut rng);
99
100            let expected = format!("{address}");
101            let (remainder, candidate) = Address::<CurrentEnvironment>::parse(&expected).unwrap();
102            assert_eq!(format!("{expected}"), candidate.to_string());
103            assert_eq!(ADDRESS_PREFIX, candidate.to_string().split('1').next().unwrap());
104            assert_eq!("", remainder);
105        }
106        Ok(())
107    }
108
109    #[test]
110    fn test_string() -> Result<()> {
111        let mut rng = TestRng::default();
112
113        for _ in 0..ITERATIONS {
114            // Sample a new address.
115            let expected = Address::<CurrentEnvironment>::rand(&mut rng);
116
117            // Check the string representation.
118            let candidate = format!("{expected}");
119            assert_eq!(expected, Address::from_str(&candidate)?);
120            assert_eq!(ADDRESS_PREFIX, candidate.split('1').next().unwrap());
121        }
122        Ok(())
123    }
124
125    #[test]
126    fn test_display() -> Result<()> {
127        let mut rng = TestRng::default();
128
129        for _ in 0..ITERATIONS {
130            // Sample a new address.
131            let expected = Address::<CurrentEnvironment>::rand(&mut rng);
132
133            let candidate = expected.to_string();
134            assert_eq!(format!("{expected}"), candidate);
135            assert_eq!(ADDRESS_PREFIX, candidate.split('1').next().unwrap());
136
137            let candidate_recovered = Address::<CurrentEnvironment>::from_str(&candidate.to_string())?;
138            assert_eq!(expected, candidate_recovered);
139        }
140        Ok(())
141    }
142}