/ cli / src / helpers / bech32m.rs
bech32m.rs
 1  // Copyright (c) 2025 ADnet Contributors
 2  // This file is part of the AlphaOS 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  
16  pub const BECH32M_CHARSET: &str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l1";
17  
18  /// Check if a string is a valid bech32m character set.
19  ///
20  /// A bech32m character set is considered valid if it consists of the following characters:
21  /// ```ignore
22  ///     qpzry9x8gf2tvdw0s3jn54khce6mua7l1
23  /// ```
24  /// The function returns `true` if the string is a valid bech32m character set, and `false` otherwise.
25  pub fn is_in_bech32m_charset(s: &str) -> bool {
26      s.as_bytes().iter().all(|b| BECH32M_CHARSET.as_bytes().contains(b))
27  }
28  
29  /// Check if a given vanity string exists at the start or end of the data part of a bech32m string.
30  ///
31  /// The bech32m string must have the following format:
32  /// ```ignore
33  ///     <HRP>1<data>[<vanity string>]
34  /// ```
35  /// where:
36  ///
37  /// - `<HRP>` is the human-readable part of the bech32m string.
38  /// - `1` is the separator between the HRP and the data part.
39  /// - `<data>` is the data part of the bech32m string.
40  /// - `<vanity string>` is the vanity string to search for. This string may or may not be present at
41  ///   the start or end of the data part.
42  ///
43  /// The function returns `true` if the vanity string exists at the start or end of the data part, and
44  /// `false` otherwise.
45  pub fn has_vanity_string(s: &str, vanity: &str) -> bool {
46      // Split the bech32m string into the HRP and data parts.
47      let (hrp, data) = match s.split_once('1') {
48          Some((hrp, data)) => (hrp, data),
49          // The bech32m string is invalid.
50          None => return false,
51      };
52      // Ensure neither the HRP nor the data part are empty.
53      if hrp.is_empty() || data.is_empty() {
54          return false;
55      }
56      // Check if the vanity string exists at the start or end of the data part.
57      data.starts_with(vanity) || data.ends_with(vanity)
58  }
59  
60  #[test]
61  fn test_is_in_bech32m_charset() {
62      assert!(is_in_bech32m_charset("qpzry9x8gf2tvdw0s3jn54khce6mua7l1qpzry9x8gf2tvdw0s3jn54khce6mua7l1"));
63      assert!(!is_in_bech32m_charset("qpzry9x8gf2tvdw0s3jn54khce6mua7l1qpzry9x8gf2tvdw0s3jn54khce6mua7lo"));
64  }
65  
66  #[test]
67  fn test_has_vanity_string() {
68      assert!(has_vanity_string("myhrp1myvanitystring", "myvanitystring"));
69      assert!(!has_vanity_string("myhrp1myvanitystring", "anotherstring"));
70      assert!(has_vanity_string("myhrp1myvanitystring1234", "myvanitystring"));
71      assert!(has_vanity_string("myhrp11234myvanitystring", "myvanitystring"));
72      assert!(!has_vanity_string("myhrp1anotherstring1234", "myvanitystring"));
73  }