snarkvm_console_program/state_path/
parse.rs

1// Copyright 2024-2025 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 STATE_PATH_PREFIX: &str = "path";
19
20impl<N: Network> Parser for StatePath<N> {
21    /// Parses a string into the state path.
22    #[inline]
23    fn parse(string: &str) -> ParserResult<Self> {
24        // Prepare a parser for the Aleo state path.
25        let parse_state_path = recognize(pair(
26            pair(tag(STATE_PATH_PREFIX), tag("1")),
27            many1(terminated(one_of("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), many0(char('_')))),
28        ));
29
30        // Parse the state path from the string.
31        map_res(parse_state_path, |state_path: &str| -> Result<_, Error> {
32            Self::from_str(&state_path.replace('_', ""))
33        })(string)
34    }
35}
36
37impl<N: Network> FromStr for StatePath<N> {
38    type Err = Error;
39
40    /// Reads in the state path string.
41    fn from_str(state_path: &str) -> Result<Self, Self::Err> {
42        // Decode the state path string from bech32m.
43        let (hrp, data, variant) = bech32::decode(state_path)?;
44        if hrp != STATE_PATH_PREFIX {
45            bail!("Failed to decode state path: '{hrp}' is an invalid prefix")
46        } else if data.is_empty() {
47            bail!("Failed to decode state path: data field is empty")
48        } else if variant != bech32::Variant::Bech32m {
49            bail!("Found an state path that is not bech32m encoded: {state_path}");
50        }
51        // Decode the state path data from u5 to u8, and into the state path.
52        Ok(Self::read_le(&Vec::from_base32(&data)?[..])?)
53    }
54}
55
56impl<N: Network> Debug for StatePath<N> {
57    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
58        Display::fmt(self, f)
59    }
60}
61
62impl<N: Network> Display for StatePath<N> {
63    /// Writes the state path as a bech32m string.
64    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
65        // Convert the state path to bytes.
66        let bytes = self.to_bytes_le().map_err(|_| fmt::Error)?;
67        // Encode the bytes into bech32m.
68        let string =
69            bech32::encode(STATE_PATH_PREFIX, bytes.to_base32(), bech32::Variant::Bech32m).map_err(|_| fmt::Error)?;
70        // Output the string.
71        Display::fmt(&string, f)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use snarkvm_console_network::MainnetV0;
79
80    type CurrentNetwork = MainnetV0;
81
82    const ITERATIONS: usize = 100;
83
84    #[test]
85    fn test_parse() {
86        let mut rng = TestRng::default();
87
88        // Ensure type and empty value fails.
89        assert!(StatePath::<CurrentNetwork>::parse(&format!("{STATE_PATH_PREFIX}1")).is_err());
90        assert!(StatePath::<CurrentNetwork>::parse("").is_err());
91
92        for _ in 0..ITERATIONS {
93            // Sample the state path.
94            let expected =
95                crate::state_path::test_helpers::sample_global_state_path::<CurrentNetwork>(None, &mut rng).unwrap();
96
97            let expected = format!("{expected}");
98            let (remainder, candidate) = StatePath::<CurrentNetwork>::parse(&expected).unwrap();
99            assert_eq!(format!("{expected}"), candidate.to_string());
100            assert_eq!(STATE_PATH_PREFIX, candidate.to_string().split('1').next().unwrap());
101            assert_eq!("", remainder);
102        }
103    }
104
105    #[test]
106    fn test_string() {
107        let mut rng = TestRng::default();
108
109        for _ in 0..ITERATIONS {
110            // Sample the state path.
111            let expected =
112                crate::state_path::test_helpers::sample_global_state_path::<CurrentNetwork>(None, &mut rng).unwrap();
113
114            // Check the string representation.
115            let candidate = format!("{expected}");
116            assert_eq!(expected, StatePath::from_str(&candidate).unwrap());
117            assert_eq!(STATE_PATH_PREFIX, candidate.split('1').next().unwrap());
118        }
119    }
120
121    #[test]
122    fn test_display() {
123        let mut rng = TestRng::default();
124
125        for _ in 0..ITERATIONS {
126            // Sample the state path.
127            let expected =
128                crate::state_path::test_helpers::sample_global_state_path::<CurrentNetwork>(None, &mut rng).unwrap();
129
130            let candidate = expected.to_string();
131            assert_eq!(format!("{expected}"), candidate);
132            assert_eq!(STATE_PATH_PREFIX, candidate.split('1').next().unwrap());
133
134            let candidate_recovered = StatePath::<CurrentNetwork>::from_str(&candidate.to_string()).unwrap();
135            assert_eq!(expected, candidate_recovered);
136        }
137    }
138}