/ console / program / src / id / mod.rs
mod.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  mod bytes;
 20  mod parse;
 21  mod serialize;
 22  mod to_address;
 23  mod to_bits;
 24  mod to_fields;
 25  
 26  use crate::Identifier;
 27  use alphavm_console_network::prelude::*;
 28  use alphavm_console_types::{Address, Boolean, Field};
 29  
 30  /// Returns `true` if the string consists of lowercase alphanumeric characters.
 31  fn is_lowercase_alphanumeric(s: &str) -> bool {
 32      s.chars().all(|c| matches!(c, '0'..='9' | 'a'..='z' | '_'))
 33  }
 34  
 35  /// A program ID is of the form `{name}.{network}`.
 36  #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 37  pub struct ProgramID<N: Network> {
 38      /// The program name.
 39      name: Identifier<N>,
 40      /// The network-level domain (NLD).
 41      network: Identifier<N>,
 42  }
 43  
 44  impl<N: Network> From<&ProgramID<N>> for ProgramID<N> {
 45      /// Returns a copy of the program ID.
 46      fn from(program_id: &ProgramID<N>) -> Self {
 47          *program_id
 48      }
 49  }
 50  
 51  impl<N: Network> TryFrom<(Identifier<N>, Identifier<N>)> for ProgramID<N> {
 52      type Error = Error;
 53  
 54      /// Initializes a program ID from a name and network-level domain identifier.
 55      fn try_from((name, network): (Identifier<N>, Identifier<N>)) -> Result<Self> {
 56          // Ensure the name is lowercase alphabets and numbers.
 57          ensure!(is_lowercase_alphanumeric(&name.to_string()), "Program name is invalid: {name}");
 58          // Construct the program ID.
 59          let id = Self { name, network };
 60          // Ensure the program network-level domain is `alpha`.
 61          ensure!(id.is_alpha(), "Program network is invalid: {network}");
 62          // Return the program ID.
 63          Ok(id)
 64      }
 65  }
 66  
 67  impl<N: Network> TryFrom<String> for ProgramID<N> {
 68      type Error = Error;
 69  
 70      /// Initializes a program ID from a name and network-level domain identifier.
 71      fn try_from(program_id: String) -> Result<Self> {
 72          Self::from_str(&program_id)
 73      }
 74  }
 75  
 76  impl<N: Network> TryFrom<&String> for ProgramID<N> {
 77      type Error = Error;
 78  
 79      /// Initializes a program ID from a name and network-level domain identifier.
 80      fn try_from(program_id: &String) -> Result<Self> {
 81          Self::from_str(program_id)
 82      }
 83  }
 84  
 85  impl<N: Network> TryFrom<&str> for ProgramID<N> {
 86      type Error = Error;
 87  
 88      /// Initializes a program ID from a name and network-level domain identifier.
 89      fn try_from(program_id: &str) -> Result<Self> {
 90          // Split the program ID into a name and network-level domain.
 91          let mut split = program_id.split('.');
 92          // Parse the name and network.
 93          if let (Some(name), Some(network), None) = (split.next(), split.next(), split.next()) {
 94              // Ensure the name is lowercase alphabets and numbers.
 95              ensure!(is_lowercase_alphanumeric(name), "Program name is invalid: {name}");
 96              // Construct the program ID.
 97              Self::try_from((Identifier::from_str(name)?, Identifier::from_str(network)?))
 98          } else {
 99              bail!("Invalid program ID '{program_id}'")
100          }
101      }
102  }
103  
104  impl<N: Network> ProgramID<N> {
105      /// Returns the program name.
106      #[inline]
107      pub const fn name(&self) -> &Identifier<N> {
108          &self.name
109      }
110  
111      /// Returns the network-level domain (NLD).
112      #[inline]
113      pub const fn network(&self) -> &Identifier<N> {
114          &self.network
115      }
116  
117      /// Returns `true` if the network-level domain is `alpha`.
118      #[inline]
119      pub fn is_alpha(&self) -> bool {
120          self.network() == &Identifier::from_str("alpha").expect("Failed to parse Alpha domain")
121      }
122  }
123  
124  impl<N: Network> Ord for ProgramID<N> {
125      /// Ordering is determined by the network first, then the program name second.
126      fn cmp(&self, other: &Self) -> Ordering {
127          match self.network == other.network {
128              true => self.name.to_string().cmp(&other.name.to_string()),
129              false => self.network.to_string().cmp(&other.network.to_string()),
130          }
131      }
132  }
133  
134  impl<N: Network> PartialOrd for ProgramID<N> {
135      /// Ordering is determined by the network first, then the program name second.
136      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
137          Some(self.cmp(other))
138      }
139  }
140  
141  impl<N: Network> Equal<Self> for ProgramID<N> {
142      type Output = Boolean<N>;
143  
144      /// Returns `true` if `self` and `other` are equal.
145      fn is_equal(&self, other: &Self) -> Self::Output {
146          Boolean::new(self == other)
147      }
148  
149      /// Returns `true` if `self` and `other` are **not** equal.
150      fn is_not_equal(&self, other: &Self) -> Self::Output {
151          Boolean::new(self != other)
152      }
153  }
154  
155  #[cfg(test)]
156  mod tests {
157      use super::*;
158      use alphavm_console_network::MainnetV0;
159  
160      type CurrentNetwork = MainnetV0;
161  
162      #[test]
163      fn test_partial_ord() -> Result<()> {
164          let import1 = ProgramID::<CurrentNetwork>::from_str("bar.alpha")?;
165          let import2 = ProgramID::<CurrentNetwork>::from_str("foo.alpha")?;
166  
167          let import3 = ProgramID::<CurrentNetwork>::from_str("bar.alpha")?;
168          let import4 = ProgramID::<CurrentNetwork>::from_str("foo.alpha")?;
169  
170          assert_eq!(import1.partial_cmp(&import1), Some(Ordering::Equal));
171          assert_eq!(import1.partial_cmp(&import2), Some(Ordering::Less));
172          assert_eq!(import1.partial_cmp(&import3), Some(Ordering::Equal));
173          assert_eq!(import1.partial_cmp(&import4), Some(Ordering::Less));
174  
175          assert_eq!(import2.partial_cmp(&import1), Some(Ordering::Greater));
176          assert_eq!(import2.partial_cmp(&import2), Some(Ordering::Equal));
177          assert_eq!(import2.partial_cmp(&import3), Some(Ordering::Greater));
178          assert_eq!(import2.partial_cmp(&import4), Some(Ordering::Equal));
179  
180          assert_eq!(import3.partial_cmp(&import1), Some(Ordering::Equal));
181          assert_eq!(import3.partial_cmp(&import2), Some(Ordering::Less));
182          assert_eq!(import3.partial_cmp(&import3), Some(Ordering::Equal));
183          assert_eq!(import3.partial_cmp(&import4), Some(Ordering::Less));
184  
185          assert_eq!(import4.partial_cmp(&import1), Some(Ordering::Greater));
186          assert_eq!(import4.partial_cmp(&import2), Some(Ordering::Equal));
187          assert_eq!(import4.partial_cmp(&import3), Some(Ordering::Greater));
188          assert_eq!(import4.partial_cmp(&import4), Some(Ordering::Equal));
189  
190          Ok(())
191      }
192  }