/ compiler / ast / src / constructor / mod.rs
mod.rs
  1  // Copyright (C) 2019-2025 ADnet Contributors
  2  // This file is part of the ADL library.
  3  
  4  // The ADL library is free software: you can redistribute it and/or modify
  5  // it under the terms of the GNU General Public License as published by
  6  // the Free Software Foundation, either version 3 of the License, or
  7  // (at your option) any later version.
  8  
  9  // The ADL library is distributed in the hope that it will be useful,
 10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12  // GNU General Public License for more details.
 13  
 14  // You should have received a copy of the GNU General Public License
 15  // along with the ADL library. If not, see <https://www.gnu.org/licenses/>.
 16  
 17  use crate::{Annotation, Block, Indent, IntegerType, Location, NetworkName, Node, NodeID, Type};
 18  use adl_span::{Span, sym};
 19  
 20  use anyhow::{anyhow, bail};
 21  use serde::{Deserialize, Serialize};
 22  use snarkvm::prelude::{Address, Literal, Locator, Network};
 23  use std::{fmt, str::FromStr};
 24  
 25  /// A constructor definition.
 26  #[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
 27  pub struct Constructor {
 28      /// Annotations on the constructor.
 29      pub annotations: Vec<Annotation>,
 30      /// The body of the constructor.
 31      pub block: Block,
 32      /// The entire span of the constructor definition.
 33      pub span: Span,
 34      /// The ID of the node.
 35      pub id: NodeID,
 36  }
 37  
 38  /// The upgrade variant.
 39  #[derive(Clone, Debug, Eq, PartialEq)]
 40  pub enum UpgradeVariant {
 41      Admin { address: String },
 42      Custom,
 43      Checksum { mapping: Location, key: String, key_type: Type },
 44      NoUpgrade,
 45  }
 46  
 47  impl Constructor {
 48      #[allow(deprecated)]
 49      pub fn get_upgrade_variant_with_network(&self, network: NetworkName) -> anyhow::Result<UpgradeVariant> {
 50          match network {
 51              // ALPHA networks
 52              NetworkName::AlphaMainnetV0 => self.get_upgrade_variant::<snarkvm::prelude::MainnetV0>(),
 53              NetworkName::AlphaTestnetV0 => self.get_upgrade_variant::<snarkvm::prelude::TestnetV0>(),
 54              NetworkName::AlphaCanaryV0 => self.get_upgrade_variant::<snarkvm::prelude::CanaryV0>(),
 55              // DELTA networks
 56              NetworkName::DeltaMainnetV0 => self.get_upgrade_variant::<snarkvm::prelude::MainnetV0>(),
 57              NetworkName::DeltaTestnetV0 => self.get_upgrade_variant::<snarkvm::prelude::TestnetV0>(),
 58              NetworkName::DeltaCanaryV0 => self.get_upgrade_variant::<snarkvm::prelude::CanaryV0>(),
 59              // Legacy Aleo networks (deprecated)
 60              NetworkName::MainnetV0 => self.get_upgrade_variant::<snarkvm::prelude::MainnetV0>(),
 61              NetworkName::TestnetV0 => self.get_upgrade_variant::<snarkvm::prelude::TestnetV0>(),
 62              NetworkName::CanaryV0 => self.get_upgrade_variant::<snarkvm::prelude::CanaryV0>(),
 63          }
 64      }
 65  
 66      /// Checks that the constructor's annotations are valid and returns the upgrade variant.
 67      pub fn get_upgrade_variant<N: Network>(&self) -> anyhow::Result<UpgradeVariant> {
 68          // Check that there is exactly one annotation.
 69          if self.annotations.len() != 1 {
 70              bail!(
 71                  "A constructor must have exactly one of the following annotations: `@admin`, `@checksum`, `@custom`, or `@noupgrade`."
 72              );
 73          }
 74          // Get the annotation.
 75          let annotation = &self.annotations[0];
 76          match annotation.identifier.name {
 77              sym::admin => {
 78                  // Parse the address string from the annotation.
 79                  let Some(address_string) = annotation.map.get(&sym::address) else {
 80                      bail!("An `@admin` annotation must have an 'address' key.")
 81                  };
 82                  // Parse the address.
 83                  let address = Address::<N>::from_str(address_string)
 84                      .map_err(|e| anyhow!("Invalid address in `@admin` annotation: `{e}`."))?;
 85                  Ok(UpgradeVariant::Admin { address: address.to_string() })
 86              }
 87              sym::checksum => {
 88                  // Parse the mapping string from the annotation.
 89                  let Some(mapping_string) = annotation.map.get(&sym::mapping) else {
 90                      bail!("A `@checksum` annotation must have a 'mapping' key.")
 91                  };
 92                  // Parse the mapping string as a locator.
 93                  let mapping = Locator::<N>::from_str(mapping_string)
 94                      .map_err(|e| anyhow!("Invalid mapping in `@checksum` annotation: `{e}`."))?;
 95  
 96                  // Parse the key string from the annotation.
 97                  let Some(key_string) = annotation.map.get(&sym::key) else {
 98                      bail!("A `@checksum` annotation must have a 'key' key.")
 99                  };
100                  // Parse the key as a plaintext value.
101                  let key = Literal::<N>::from_str(key_string)
102                      .map_err(|e| anyhow!("Invalid key in `@checksum` annotation: `{e}`."))?;
103                  // Get the literal type.
104                  let key_type = get_type_from_snarkvm_literal(&key);
105                  Ok(UpgradeVariant::Checksum { mapping: mapping.into(), key: key.to_string(), key_type })
106              }
107              sym::custom => Ok(UpgradeVariant::Custom),
108              sym::noupgrade => Ok(UpgradeVariant::NoUpgrade),
109              _ => bail!(
110                  "Invalid annotation on constructor: `{}`. Expected one of `@admin`, `@checksum`, `@custom`, or `@noupgrade`.",
111                  annotation.identifier.name
112              ),
113          }
114      }
115  }
116  
117  impl fmt::Display for Constructor {
118      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119          for annotation in &self.annotations {
120              writeln!(f, "{annotation}")?;
121          }
122  
123          writeln!(f, "async constructor() {{")?;
124          for stmt in self.block.statements.iter() {
125              writeln!(f, "{}{}", Indent(stmt), stmt.semicolon())?;
126          }
127          write!(f, "}}")
128      }
129  }
130  
131  impl fmt::Debug for Constructor {
132      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133          write!(f, "{self}")
134      }
135  }
136  
137  crate::simple_node_impl!(Constructor);
138  
139  // A helper function to get the type from a snarkVM literal.
140  fn get_type_from_snarkvm_literal<N: Network>(literal: &Literal<N>) -> Type {
141      match literal {
142          Literal::Field(_) => Type::Field,
143          Literal::Group(_) => Type::Group,
144          Literal::Address(_) => Type::Address,
145          Literal::Scalar(_) => Type::Scalar,
146          Literal::Boolean(_) => Type::Boolean,
147          Literal::String(_) => Type::String,
148          Literal::I8(_) => Type::Integer(IntegerType::I8),
149          Literal::I16(_) => Type::Integer(IntegerType::I16),
150          Literal::I32(_) => Type::Integer(IntegerType::I32),
151          Literal::I64(_) => Type::Integer(IntegerType::I64),
152          Literal::I128(_) => Type::Integer(IntegerType::I128),
153          Literal::U8(_) => Type::Integer(IntegerType::U8),
154          Literal::U16(_) => Type::Integer(IntegerType::U16),
155          Literal::U32(_) => Type::Integer(IntegerType::U32),
156          Literal::U64(_) => Type::Integer(IntegerType::U64),
157          Literal::U128(_) => Type::Integer(IntegerType::U128),
158          Literal::Signature(_) => Type::Signature,
159      }
160  }