/ ledger / puzzle / src / solution_id / string.rs
string.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use super::*;
 20  
 21  pub static SOLUTION_ID_PREFIX: &str = "solution";
 22  
 23  impl<N: Network> FromStr for SolutionID<N> {
 24      type Err = Error;
 25  
 26      /// Reads in the solution ID string.
 27      fn from_str(solution_id: &str) -> Result<Self, Self::Err> {
 28          // Decode the solution ID string from bech32m.
 29          let (hrp, data, variant) = bech32::decode(solution_id)?;
 30          if hrp != SOLUTION_ID_PREFIX {
 31              bail!("Failed to decode solution ID: '{hrp}' is an invalid prefix")
 32          } else if data.is_empty() {
 33              bail!("Failed to decode solution ID: data field is empty")
 34          } else if variant != bech32::Variant::Bech32m {
 35              bail!("Found a solution ID that is not bech32m encoded: {solution_id}");
 36          }
 37          // Decode the solution ID data from u5 to u8, and into the solution ID.
 38          Ok(Self::read_le(&Vec::from_base32(&data)?[..])?)
 39      }
 40  }
 41  
 42  impl<N: Network> Debug for SolutionID<N> {
 43      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
 44          Display::fmt(self, f)
 45      }
 46  }
 47  
 48  impl<N: Network> Display for SolutionID<N> {
 49      /// Writes the solution ID as a bech32m string.
 50      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
 51          // Convert the solution ID to bytes.
 52          let bytes = self.to_bytes_le().map_err(|_| fmt::Error)?;
 53          // Encode the bytes into bech32m.
 54          let string =
 55              bech32::encode(SOLUTION_ID_PREFIX, bytes.to_base32(), bech32::Variant::Bech32m).map_err(|_| fmt::Error)?;
 56          // Output the string.
 57          Display::fmt(&string, f)
 58      }
 59  }
 60  
 61  #[cfg(test)]
 62  mod tests {
 63      use super::*;
 64      use console::network::MainnetV0;
 65  
 66      type CurrentNetwork = MainnetV0;
 67  
 68      const ITERATIONS: u64 = 1_000;
 69  
 70      #[test]
 71      fn test_string() -> Result<()> {
 72          // Ensure type and empty value fails.
 73          assert!(SolutionID::<CurrentNetwork>::from_str(&format!("{SOLUTION_ID_PREFIX}1")).is_err());
 74          assert!(SolutionID::<CurrentNetwork>::from_str("").is_err());
 75  
 76          let mut rng = TestRng::default();
 77  
 78          for _ in 0..ITERATIONS {
 79              // Sample a new solution ID.
 80              let expected = SolutionID::<CurrentNetwork>::from(rng.r#gen::<u64>());
 81  
 82              // Check the string representation.
 83              let candidate = format!("{expected}");
 84              assert_eq!(expected, SolutionID::from_str(&candidate)?);
 85              assert_eq!(SOLUTION_ID_PREFIX, candidate.split('1').next().unwrap());
 86          }
 87          Ok(())
 88      }
 89  
 90      #[test]
 91      fn test_display() -> Result<()> {
 92          let mut rng = TestRng::default();
 93  
 94          for _ in 0..ITERATIONS {
 95              // Sample a new solution ID.
 96              let expected = SolutionID::<CurrentNetwork>::from(rng.r#gen::<u64>());
 97  
 98              let candidate = expected.to_string();
 99              assert_eq!(format!("{expected}"), candidate);
100              assert_eq!(SOLUTION_ID_PREFIX, candidate.split('1').next().unwrap());
101  
102              let candidate_recovered = SolutionID::<CurrentNetwork>::from_str(&candidate.to_string())?;
103              assert_eq!(expected, candidate_recovered);
104          }
105          Ok(())
106      }
107  }