/ synthesizer / program / src / lib.rs
lib.rs
   1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
   2  // This file is part of the deltavm 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  #![forbid(unsafe_code)]
  17  #![allow(clippy::too_many_arguments)]
  18  #![warn(clippy::cast_possible_truncation)]
  19  
  20  extern crate deltavm_circuit as circuit;
  21  extern crate deltavm_console as console;
  22  
  23  pub type Program<N> = crate::ProgramCore<N>;
  24  pub type Function<N> = crate::FunctionCore<N>;
  25  pub type Finalize<N> = crate::FinalizeCore<N>;
  26  pub type Closure<N> = crate::ClosureCore<N>;
  27  pub type Constructor<N> = crate::ConstructorCore<N>;
  28  
  29  mod closure;
  30  pub use closure::*;
  31  
  32  mod constructor;
  33  pub use constructor::*;
  34  
  35  pub mod finalize;
  36  pub use finalize::*;
  37  
  38  mod function;
  39  pub use function::*;
  40  
  41  mod import;
  42  pub use import::*;
  43  
  44  pub mod logic;
  45  pub use logic::*;
  46  
  47  mod mapping;
  48  pub use mapping::*;
  49  
  50  mod traits;
  51  pub use traits::*;
  52  
  53  mod bytes;
  54  mod parse;
  55  mod serialize;
  56  mod to_checksum;
  57  
  58  use deltavm_utilities::cfg_iter;
  59  use console::{
  60      network::{
  61          ConsensusVersion,
  62          prelude::{
  63              Debug,
  64              Deserialize,
  65              Deserializer,
  66              Display,
  67              Err,
  68              Error,
  69              ErrorKind,
  70              Formatter,
  71              FromBytes,
  72              FromBytesDeserializer,
  73              FromStr,
  74              IoResult,
  75              Itertools,
  76              Network,
  77              Parser,
  78              ParserResult,
  79              Read,
  80              Result,
  81              Sanitizer,
  82              Serialize,
  83              Serializer,
  84              ToBytes,
  85              ToBytesSerializer,
  86              TypeName,
  87              Write,
  88              anyhow,
  89              bail,
  90              de,
  91              ensure,
  92              error,
  93              fmt,
  94              make_error,
  95              many0,
  96              many1,
  97              map,
  98              map_res,
  99              tag,
 100              take,
 101          },
 102      },
 103      program::{Identifier, PlaintextType, ProgramID, RecordType, StructType},
 104      types::U8,
 105  };
 106  
 107  use indexmap::{IndexMap, IndexSet};
 108  use std::collections::BTreeSet;
 109  use tiny_keccak::{Hasher, Sha3 as TinySha3};
 110  
 111  #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 112  enum ProgramLabel<N: Network> {
 113      /// A program constructor.
 114      Constructor,
 115      /// A named component.
 116      Identifier(Identifier<N>),
 117  }
 118  
 119  #[cfg(not(feature = "serial"))]
 120  use rayon::prelude::*;
 121  
 122  #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 123  enum ProgramDefinition {
 124      /// A program constructor.
 125      Constructor,
 126      /// A program mapping.
 127      Mapping,
 128      /// A program struct.
 129      Struct,
 130      /// A program record.
 131      Record,
 132      /// A program closure.
 133      Closure,
 134      /// A program function.
 135      Function,
 136  }
 137  
 138  #[derive(Clone)]
 139  pub struct ProgramCore<N: Network> {
 140      /// The ID of the program.
 141      id: ProgramID<N>,
 142      /// A map of the declared imports for the program.
 143      imports: IndexMap<ProgramID<N>, Import<N>>,
 144      /// A map of program labels to their program definitions.
 145      components: IndexMap<ProgramLabel<N>, ProgramDefinition>,
 146      /// An optional constructor for the program.
 147      constructor: Option<ConstructorCore<N>>,
 148      /// A map of the declared mappings for the program.
 149      mappings: IndexMap<Identifier<N>, Mapping<N>>,
 150      /// A map of the declared structs for the program.
 151      structs: IndexMap<Identifier<N>, StructType<N>>,
 152      /// A map of the declared record types for the program.
 153      records: IndexMap<Identifier<N>, RecordType<N>>,
 154      /// A map of the declared closures for the program.
 155      closures: IndexMap<Identifier<N>, ClosureCore<N>>,
 156      /// A map of the declared functions for the program.
 157      functions: IndexMap<Identifier<N>, FunctionCore<N>>,
 158  }
 159  
 160  impl<N: Network> PartialEq for ProgramCore<N> {
 161      /// Compares two programs for equality, verifying that the components are in the same order.
 162      /// The order of the components must match to ensure that deployment tree is well-formed.
 163      fn eq(&self, other: &Self) -> bool {
 164          // Check that the number of components is the same.
 165          if self.components.len() != other.components.len() {
 166              return false;
 167          }
 168          // Check that the components match in order.
 169          for (left, right) in self.components.iter().zip_eq(other.components.iter()) {
 170              if left != right {
 171                  return false;
 172              }
 173          }
 174          // Check that the remaining fields match.
 175          self.id == other.id
 176              && self.imports == other.imports
 177              && self.mappings == other.mappings
 178              && self.structs == other.structs
 179              && self.records == other.records
 180              && self.closures == other.closures
 181              && self.functions == other.functions
 182      }
 183  }
 184  
 185  impl<N: Network> Eq for ProgramCore<N> {}
 186  
 187  impl<N: Network> ProgramCore<N> {
 188      /// A list of reserved keywords for Alpha programs, enforced at the parser level.
 189      // New keywords should be enforced through `RESTRICTED_KEYWORDS` instead, if possible.
 190      // Adding keywords to this list will require a backwards-compatible versioning for programs.
 191      #[rustfmt::skip]
 192      pub const KEYWORDS: &'static [&'static str] = &[
 193          // Mode
 194          "const",
 195          "constant",
 196          "public",
 197          "private",
 198          // Literals
 199          "address",
 200          "boolean",
 201          "field",
 202          "group",
 203          "i8",
 204          "i16",
 205          "i32",
 206          "i64",
 207          "i128",
 208          "u8",
 209          "u16",
 210          "u32",
 211          "u64",
 212          "u128",
 213          "scalar",
 214          "signature",
 215          "string",
 216          // Boolean
 217          "true",
 218          "false",
 219          // Statements
 220          "input",
 221          "output",
 222          "as",
 223          "into",
 224          // Record
 225          "record",
 226          "owner",
 227          // Program
 228          "transition",
 229          "import",
 230          "function",
 231          "struct",
 232          "closure",
 233          "program",
 234          "alpha",
 235          "self",
 236          "storage",
 237          "mapping",
 238          "key",
 239          "value",
 240          "async",
 241          "finalize",
 242          // Reserved (catch all)
 243          "global",
 244          "block",
 245          "return",
 246          "break",
 247          "assert",
 248          "continue",
 249          "let",
 250          "if",
 251          "else",
 252          "while",
 253          "for",
 254          "switch",
 255          "case",
 256          "default",
 257          "match",
 258          "enum",
 259          "struct",
 260          "union",
 261          "trait",
 262          "impl",
 263          "type",
 264          "future",
 265      ];
 266      /// A list of restricted keywords for Alpha programs, enforced at the VM-level for program hygiene.
 267      /// Each entry is a tuple of the consensus version and a list of keywords.
 268      /// If the current consensus version is greater than or equal to the specified version,
 269      /// the keywords in the list should be restricted.
 270      #[rustfmt::skip]
 271      pub const RESTRICTED_KEYWORDS: &'static [(ConsensusVersion, &'static [&'static str])] = &[
 272          (ConsensusVersion::V6, &["constructor"])
 273      ];
 274  
 275      /// Initializes an empty program.
 276      #[inline]
 277      pub fn new(id: ProgramID<N>) -> Result<Self> {
 278          // Ensure the program name is valid.
 279          ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
 280  
 281          Ok(Self {
 282              id,
 283              imports: IndexMap::new(),
 284              constructor: None,
 285              components: IndexMap::new(),
 286              mappings: IndexMap::new(),
 287              structs: IndexMap::new(),
 288              records: IndexMap::new(),
 289              closures: IndexMap::new(),
 290              functions: IndexMap::new(),
 291          })
 292      }
 293  
 294      /// Initializes the credits program.
 295      #[inline]
 296      pub fn credits() -> Result<Self> {
 297          Self::from_str(include_str!("./resources/credits.delta"))
 298      }
 299  
 300      /// Returns the ID of the program.
 301      pub const fn id(&self) -> &ProgramID<N> {
 302          &self.id
 303      }
 304  
 305      /// Returns the imports in the program.
 306      pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
 307          &self.imports
 308      }
 309  
 310      /// Returns the constructor for the program.
 311      pub const fn constructor(&self) -> Option<&ConstructorCore<N>> {
 312          self.constructor.as_ref()
 313      }
 314  
 315      /// Returns the mappings in the program.
 316      pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
 317          &self.mappings
 318      }
 319  
 320      /// Returns the structs in the program.
 321      pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
 322          &self.structs
 323      }
 324  
 325      /// Returns the records in the program.
 326      pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
 327          &self.records
 328      }
 329  
 330      /// Returns the closures in the program.
 331      pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N>> {
 332          &self.closures
 333      }
 334  
 335      /// Returns the functions in the program.
 336      pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N>> {
 337          &self.functions
 338      }
 339  
 340      /// Returns `true` if the program contains an import with the given program ID.
 341      pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
 342          self.imports.contains_key(id)
 343      }
 344  
 345      /// Returns `true` if the program contains a constructor.
 346      pub const fn contains_constructor(&self) -> bool {
 347          self.constructor.is_some()
 348      }
 349  
 350      /// Returns `true` if the program contains a mapping with the given name.
 351      pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
 352          self.mappings.contains_key(name)
 353      }
 354  
 355      /// Returns `true` if the program contains a struct with the given name.
 356      pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
 357          self.structs.contains_key(name)
 358      }
 359  
 360      /// Returns `true` if the program contains a record with the given name.
 361      pub fn contains_record(&self, name: &Identifier<N>) -> bool {
 362          self.records.contains_key(name)
 363      }
 364  
 365      /// Returns `true` if the program contains a closure with the given name.
 366      pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
 367          self.closures.contains_key(name)
 368      }
 369  
 370      /// Returns `true` if the program contains a function with the given name.
 371      pub fn contains_function(&self, name: &Identifier<N>) -> bool {
 372          self.functions.contains_key(name)
 373      }
 374  
 375      /// Returns the mapping with the given name.
 376      pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
 377          // Attempt to retrieve the mapping.
 378          let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
 379          // Ensure the mapping name matches.
 380          ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
 381          // Return the mapping.
 382          Ok(mapping)
 383      }
 384  
 385      /// Returns the struct with the given name.
 386      pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
 387          // Attempt to retrieve the struct.
 388          let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
 389          // Ensure the struct name matches.
 390          ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
 391          // Ensure the struct contains members.
 392          ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
 393          // Return the struct.
 394          Ok(struct_)
 395      }
 396  
 397      /// Returns the record with the given name.
 398      pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
 399          // Attempt to retrieve the record.
 400          let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
 401          // Ensure the record name matches.
 402          ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
 403          // Return the record.
 404          Ok(record)
 405      }
 406  
 407      /// Returns the closure with the given name.
 408      pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N>> {
 409          // Attempt to retrieve the closure.
 410          let closure = self.closures.get(name).cloned().ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
 411          // Ensure the closure name matches.
 412          ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
 413          // Ensure there are input statements in the closure.
 414          ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
 415          // Ensure the number of inputs is within the allowed range.
 416          ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
 417          // Ensure there are instructions in the closure.
 418          ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
 419          // Ensure the number of outputs is within the allowed range.
 420          ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
 421          // Return the closure.
 422          Ok(closure)
 423      }
 424  
 425      /// Returns the function with the given name.
 426      pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N>> {
 427          self.get_function_ref(name).cloned()
 428      }
 429  
 430      /// Returns a reference to the function with the given name.
 431      pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N>> {
 432          // Attempt to retrieve the function.
 433          let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
 434          // Ensure the function name matches.
 435          ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
 436          // Ensure the number of inputs is within the allowed range.
 437          ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
 438          // Ensure the number of instructions is within the allowed range.
 439          ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
 440          // Ensure the number of outputs is within the allowed range.
 441          ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
 442          // Return the function.
 443          Ok(function)
 444      }
 445  
 446      /// Adds a new import statement to the program.
 447      ///
 448      /// # Errors
 449      /// This method will halt if the imported program was previously added.
 450      #[inline]
 451      fn add_import(&mut self, import: Import<N>) -> Result<()> {
 452          // Retrieve the imported program name.
 453          let import_name = *import.name();
 454  
 455          // Ensure that the number of imports is within the allowed range.
 456          ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
 457  
 458          // Ensure the import name is new.
 459          ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
 460          // Ensure the import name is not a reserved opcode.
 461          ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
 462          // Ensure the import name is not a reserved keyword.
 463          ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
 464  
 465          // Ensure the import is new.
 466          ensure!(
 467              !self.imports.contains_key(import.program_id()),
 468              "Import '{}' is already defined.",
 469              import.program_id()
 470          );
 471  
 472          // Add the import statement to the program.
 473          if self.imports.insert(*import.program_id(), import.clone()).is_some() {
 474              bail!("'{}' already exists in the program.", import.program_id())
 475          }
 476          Ok(())
 477      }
 478  
 479      /// Adds a constructor to the program.
 480      ///
 481      /// # Errors
 482      /// This method will halt if a constructor was previously added.
 483      /// This method will halt if a constructor exceeds the maximum number of commands.
 484      fn add_constructor(&mut self, constructor: ConstructorCore<N>) -> Result<()> {
 485          // Ensure the program does not already have a constructor.
 486          ensure!(self.constructor.is_none(), "Program already has a constructor.");
 487          // Ensure the number of commands is within the allowed range.
 488          ensure!(!constructor.commands().is_empty(), "Constructor must have at least one command");
 489          ensure!(constructor.commands().len() <= N::MAX_COMMANDS, "Constructor exceeds maximum number of commands");
 490          // Add the constructor to the components.
 491          if self.components.insert(ProgramLabel::Constructor, ProgramDefinition::Constructor).is_some() {
 492              bail!("Constructor already exists in the program.")
 493          }
 494          // Add the constructor to the program.
 495          self.constructor = Some(constructor);
 496          Ok(())
 497      }
 498  
 499      /// Adds a new mapping to the program.
 500      ///
 501      /// # Errors
 502      /// This method will halt if the mapping name is already in use.
 503      /// This method will halt if the mapping name is a reserved opcode or keyword.
 504      #[inline]
 505      fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
 506          // Retrieve the mapping name.
 507          let mapping_name = *mapping.name();
 508  
 509          // Ensure the program has not exceeded the maximum number of mappings.
 510          ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
 511  
 512          // Ensure the mapping name is new.
 513          ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
 514          // Ensure the mapping name is not a reserved keyword.
 515          ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
 516          // Ensure the mapping name is not a reserved opcode.
 517          ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
 518  
 519          // Add the mapping name to the identifiers.
 520          if self.components.insert(ProgramLabel::Identifier(mapping_name), ProgramDefinition::Mapping).is_some() {
 521              bail!("'{mapping_name}' already exists in the program.")
 522          }
 523          // Add the mapping to the program.
 524          if self.mappings.insert(mapping_name, mapping).is_some() {
 525              bail!("'{mapping_name}' already exists in the program.")
 526          }
 527          Ok(())
 528      }
 529  
 530      /// Adds a new struct to the program.
 531      ///
 532      /// # Errors
 533      /// This method will halt if the struct was previously added.
 534      /// This method will halt if the struct name is already in use in the program.
 535      /// This method will halt if the struct name is a reserved opcode or keyword.
 536      /// This method will halt if any structs in the struct's members are not already defined.
 537      #[inline]
 538      fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
 539          // Retrieve the struct name.
 540          let struct_name = *struct_.name();
 541  
 542          // Ensure the program has not exceeded the maximum number of structs.
 543          ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
 544  
 545          // Ensure the struct name is new.
 546          ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
 547          // Ensure the struct name is not a reserved opcode.
 548          ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
 549          // Ensure the struct name is not a reserved keyword.
 550          ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
 551  
 552          // Ensure the struct contains members.
 553          ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
 554  
 555          // Ensure all struct members are well-formed.
 556          // Note: This design ensures cyclic references are not possible.
 557          for (identifier, plaintext_type) in struct_.members() {
 558              // Ensure the member name is not a reserved keyword.
 559              ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
 560              // Ensure the member type is already defined in the program.
 561              match plaintext_type {
 562                  PlaintextType::Literal(_) => continue,
 563                  PlaintextType::Struct(member_identifier) => {
 564                      // Ensure the member struct name exists in the program.
 565                      if !self.structs.contains_key(member_identifier) {
 566                          bail!("'{member_identifier}' in struct '{struct_name}' is not defined.")
 567                      }
 568                  }
 569                  PlaintextType::Array(array_type) => {
 570                      if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
 571                          // Ensure the member struct name exists in the program.
 572                          if !self.structs.contains_key(struct_name) {
 573                              bail!("'{struct_name}' in array '{array_type}' is not defined.")
 574                          }
 575                      }
 576                  }
 577              }
 578          }
 579  
 580          // Add the struct name to the identifiers.
 581          if self.components.insert(ProgramLabel::Identifier(struct_name), ProgramDefinition::Struct).is_some() {
 582              bail!("'{struct_name}' already exists in the program.")
 583          }
 584          // Add the struct to the program.
 585          if self.structs.insert(struct_name, struct_).is_some() {
 586              bail!("'{struct_name}' already exists in the program.")
 587          }
 588          Ok(())
 589      }
 590  
 591      /// Adds a new record to the program.
 592      ///
 593      /// # Errors
 594      /// This method will halt if the record was previously added.
 595      /// This method will halt if the record name is already in use in the program.
 596      /// This method will halt if the record name is a reserved opcode or keyword.
 597      /// This method will halt if any records in the record's members are not already defined.
 598      #[inline]
 599      fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
 600          // Retrieve the record name.
 601          let record_name = *record.name();
 602  
 603          // Ensure the program has not exceeded the maximum number of records.
 604          ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
 605  
 606          // Ensure the record name is new.
 607          ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
 608          // Ensure the record name is not a reserved opcode.
 609          ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
 610          // Ensure the record name is not a reserved keyword.
 611          ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
 612  
 613          // Ensure all record entries are well-formed.
 614          // Note: This design ensures cyclic references are not possible.
 615          for (identifier, entry_type) in record.entries() {
 616              // Ensure the member name is not a reserved keyword.
 617              ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
 618              // Ensure the member type is already defined in the program.
 619              match entry_type.plaintext_type() {
 620                  PlaintextType::Literal(_) => continue,
 621                  PlaintextType::Struct(identifier) => {
 622                      if !self.structs.contains_key(identifier) {
 623                          bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
 624                      }
 625                  }
 626                  PlaintextType::Array(array_type) => {
 627                      if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
 628                          // Ensure the member struct name exists in the program.
 629                          if !self.structs.contains_key(struct_name) {
 630                              bail!("'{struct_name}' in array '{array_type}' is not defined.")
 631                          }
 632                      }
 633                  }
 634              }
 635          }
 636  
 637          // Add the record name to the identifiers.
 638          if self.components.insert(ProgramLabel::Identifier(record_name), ProgramDefinition::Record).is_some() {
 639              bail!("'{record_name}' already exists in the program.")
 640          }
 641          // Add the record to the program.
 642          if self.records.insert(record_name, record).is_some() {
 643              bail!("'{record_name}' already exists in the program.")
 644          }
 645          Ok(())
 646      }
 647  
 648      /// Adds a new closure to the program.
 649      ///
 650      /// # Errors
 651      /// This method will halt if the closure was previously added.
 652      /// This method will halt if the closure name is already in use in the program.
 653      /// This method will halt if the closure name is a reserved opcode or keyword.
 654      /// This method will halt if any registers are assigned more than once.
 655      /// This method will halt if the registers are not incrementing monotonically.
 656      /// This method will halt if an input type references a non-existent definition.
 657      /// This method will halt if an operand register does not already exist in memory.
 658      /// This method will halt if a destination register already exists in memory.
 659      /// This method will halt if an output register does not already exist.
 660      /// This method will halt if an output type references a non-existent definition.
 661      #[inline]
 662      fn add_closure(&mut self, closure: ClosureCore<N>) -> Result<()> {
 663          // Retrieve the closure name.
 664          let closure_name = *closure.name();
 665  
 666          // Ensure the program has not exceeded the maximum number of closures.
 667          ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
 668  
 669          // Ensure the closure name is new.
 670          ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
 671          // Ensure the closure name is not a reserved opcode.
 672          ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
 673          // Ensure the closure name is not a reserved keyword.
 674          ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
 675  
 676          // Ensure there are input statements in the closure.
 677          ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
 678          // Ensure the number of inputs is within the allowed range.
 679          ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
 680          // Ensure there are instructions in the closure.
 681          ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
 682          // Ensure the number of outputs is within the allowed range.
 683          ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
 684  
 685          // Add the function name to the identifiers.
 686          if self.components.insert(ProgramLabel::Identifier(closure_name), ProgramDefinition::Closure).is_some() {
 687              bail!("'{closure_name}' already exists in the program.")
 688          }
 689          // Add the closure to the program.
 690          if self.closures.insert(closure_name, closure).is_some() {
 691              bail!("'{closure_name}' already exists in the program.")
 692          }
 693          Ok(())
 694      }
 695  
 696      /// Adds a new function to the program.
 697      ///
 698      /// # Errors
 699      /// This method will halt if the function was previously added.
 700      /// This method will halt if the function name is already in use in the program.
 701      /// This method will halt if the function name is a reserved opcode or keyword.
 702      /// This method will halt if any registers are assigned more than once.
 703      /// This method will halt if the registers are not incrementing monotonically.
 704      /// This method will halt if an input type references a non-existent definition.
 705      /// This method will halt if an operand register does not already exist in memory.
 706      /// This method will halt if a destination register already exists in memory.
 707      /// This method will halt if an output register does not already exist.
 708      /// This method will halt if an output type references a non-existent definition.
 709      #[inline]
 710      fn add_function(&mut self, function: FunctionCore<N>) -> Result<()> {
 711          // Retrieve the function name.
 712          let function_name = *function.name();
 713  
 714          // Ensure the program has not exceeded the maximum number of functions.
 715          ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
 716  
 717          // Ensure the function name is new.
 718          ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
 719          // Ensure the function name is not a reserved opcode.
 720          ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
 721          // Ensure the function name is not a reserved keyword.
 722          ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
 723  
 724          // Ensure the number of inputs is within the allowed range.
 725          ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
 726          // Ensure the number of instructions is within the allowed range.
 727          ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
 728          // Ensure the number of outputs is within the allowed range.
 729          ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
 730  
 731          // Add the function name to the identifiers.
 732          if self.components.insert(ProgramLabel::Identifier(function_name), ProgramDefinition::Function).is_some() {
 733              bail!("'{function_name}' already exists in the program.")
 734          }
 735          // Add the function to the program.
 736          if self.functions.insert(function_name, function).is_some() {
 737              bail!("'{function_name}' already exists in the program.")
 738          }
 739          Ok(())
 740      }
 741  
 742      /// Returns `true` if the given name does not already exist in the program.
 743      fn is_unique_name(&self, name: &Identifier<N>) -> bool {
 744          !self.components.contains_key(&ProgramLabel::Identifier(*name))
 745      }
 746  
 747      /// Returns `true` if the given name is a reserved opcode.
 748      pub fn is_reserved_opcode(name: &str) -> bool {
 749          Instruction::<N>::is_reserved_opcode(name)
 750      }
 751  
 752      /// Returns `true` if the given name uses a reserved keyword.
 753      pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
 754          // Convert the given name to a string.
 755          let name = name.to_string();
 756          // Check if the name is a keyword.
 757          Self::KEYWORDS.iter().any(|keyword| *keyword == name)
 758      }
 759  
 760      /// Returns an iterator over the restricted keywords for the given consensus version.
 761      pub fn restricted_keywords_for_consensus_version(
 762          consensus_version: ConsensusVersion,
 763      ) -> impl Iterator<Item = &'static str> {
 764          Self::RESTRICTED_KEYWORDS
 765              .iter()
 766              .filter(move |(version, _)| *version <= consensus_version)
 767              .flat_map(|(_, keywords)| *keywords)
 768              .copied()
 769      }
 770  
 771      /// Checks a program for restricted keywords for the given consensus version.
 772      /// Returns an error if any restricted keywords are found.
 773      /// Note: Restrictions are not enforced on the import names in case they were deployed before the restrictions were added.
 774      pub fn check_restricted_keywords_for_consensus_version(&self, consensus_version: ConsensusVersion) -> Result<()> {
 775          // Get all keywords that are restricted for the consensus version.
 776          let keywords =
 777              Program::<N>::restricted_keywords_for_consensus_version(consensus_version).collect::<IndexSet<_>>();
 778          // Check if the program name is a restricted keywords.
 779          let program_name = self.id().name().to_string();
 780          if keywords.contains(&program_name.as_str()) {
 781              bail!("Program name '{program_name}' is a restricted keyword for the current consensus version")
 782          }
 783          // Check that all top-level program components are not restricted keywords.
 784          for component in self.components.keys() {
 785              match component {
 786                  ProgramLabel::Identifier(identifier) => {
 787                      if keywords.contains(identifier.to_string().as_str()) {
 788                          bail!(
 789                              "Program component '{identifier}' is a restricted keyword for the current consensus version"
 790                          )
 791                      }
 792                  }
 793                  ProgramLabel::Constructor => continue,
 794              }
 795          }
 796          // Check that all record entry names are not restricted keywords.
 797          for record_type in self.records().values() {
 798              for entry_name in record_type.entries().keys() {
 799                  if keywords.contains(entry_name.to_string().as_str()) {
 800                      bail!("Record entry '{entry_name}' is a restricted keyword for the current consensus version")
 801                  }
 802              }
 803          }
 804          // Check that all struct member names are not restricted keywords.
 805          for struct_type in self.structs().values() {
 806              for member_name in struct_type.members().keys() {
 807                  if keywords.contains(member_name.to_string().as_str()) {
 808                      bail!("Struct member '{member_name}' is a restricted keyword for the current consensus version")
 809                  }
 810              }
 811          }
 812          // Check that all `finalize` positions.
 813          // Note: It is sufficient to only check the positions in `FinalizeCore` since `FinalizeTypes::initialize` checks that every
 814          // `Branch` instruction targets a valid position.
 815          for function in self.functions().values() {
 816              if let Some(finalize_logic) = function.finalize_logic() {
 817                  for position in finalize_logic.positions().keys() {
 818                      if keywords.contains(position.to_string().as_str()) {
 819                          bail!(
 820                              "Finalize position '{position}' is a restricted keyword for the current consensus version"
 821                          )
 822                      }
 823                  }
 824              }
 825          }
 826          Ok(())
 827      }
 828  
 829      /// Checks that the program structure is well-formed under the following rules:
 830      ///  1. The program ID must not contain the keyword "alpha" in the program name.
 831      ///  2. The record name must not contain the keyword "alpha".
 832      ///  3. Record names must not be prefixes of other record names.
 833      ///  4. Record entry names must not contain the keyword "alpha".
 834      pub fn check_program_naming_structure(&self) -> Result<()> {
 835          // 1. Check if the program ID contains the "alpha" substring
 836          let program_id = self.id().name().to_string();
 837          if program_id.contains("alpha") {
 838              bail!("Program ID '{program_id}' can't contain the reserved keyword 'alpha'.");
 839          }
 840  
 841          // Fetch the record names in a sorted BTreeSet.
 842          let record_names: BTreeSet<String> = self.records.keys().map(|name| name.to_string()).collect();
 843  
 844          // 2. Check if any record name contains the "alpha" substring.
 845          for record_name in &record_names {
 846              if record_name.contains("alpha") {
 847                  bail!("Record name '{record_name}' can't contain the reserved keyword 'alpha'.");
 848              }
 849          }
 850  
 851          // 3. Check if any of the record names are a prefix of another.
 852          let mut record_names_iter = record_names.iter();
 853          let mut previous_record_name = record_names_iter.next();
 854          for record_name in record_names_iter {
 855              if let Some(previous) = previous_record_name {
 856                  if record_name.starts_with(previous) {
 857                      bail!("Record name '{previous}' can't be a prefix of record name '{record_name}'.");
 858                  }
 859              }
 860              previous_record_name = Some(record_name);
 861          }
 862  
 863          // 4. Check if any record entry names contain the "alpha" substring.
 864          for record_entry_name in self.records.values().flat_map(|record_type| record_type.entries().keys()) {
 865              if record_entry_name.to_string().contains("alpha") {
 866                  bail!("Record entry name '{record_entry_name}' can't contain the reserved keyword 'alpha'.");
 867              }
 868          }
 869  
 870          Ok(())
 871      }
 872  
 873      /// Checks that the program does not make external calls to `credits.delta/upgrade`.
 874      pub fn check_external_calls_to_credits_upgrade(&self) -> Result<()> {
 875          // Check if the program makes external calls to `credits.delta/upgrade`.
 876          cfg_iter!(self.functions()).flat_map(|(_, function)| function.instructions()).try_for_each(|instruction| {
 877              if let Some(CallOperator::Locator(locator)) = instruction.call_operator() {
 878                  // Check if the locator is restricted.
 879                  if locator.to_string() == "credits.delta/upgrade" {
 880                      bail!("External call to restricted locator '{locator}'")
 881                  }
 882              }
 883              Ok(())
 884          })?;
 885          Ok(())
 886      }
 887  
 888      /// Returns `true` if a program contains any V9 syntax.
 889      /// This includes `constructor`, `Operand::Edition`, `Operand::Checksum`, and `Operand::ProgramOwner`.
 890      /// This is enforced to be `false` for programs before `ConsensusVersion::V9`.
 891      #[inline]
 892      pub fn contains_v9_syntax(&self) -> bool {
 893          // Check if the program contains a constructor.
 894          if self.contains_constructor() {
 895              return true;
 896          }
 897          // Check each instruction and output in each function's finalize scope for the use of
 898          // `Operand::Checksum`, `Operand::Edition` or `Operand::ProgramOwner`.
 899          for function in self.functions().values() {
 900              // Check the finalize scope if it exists.
 901              if let Some(finalize_logic) = function.finalize_logic() {
 902                  // Check the command operands.
 903                  for command in finalize_logic.commands() {
 904                      for operand in command.operands() {
 905                          if matches!(operand, Operand::Checksum(_) | Operand::Edition(_) | Operand::ProgramOwner(_)) {
 906                              return true;
 907                          }
 908                      }
 909                  }
 910              }
 911          }
 912          // Return `false` since no V9 syntax was found.
 913          false
 914      }
 915  
 916      /// Returns `true` if the program contains an array type with a size that exceeds the given maximum.
 917      pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
 918          self.mappings.values().any(|mapping| mapping.exceeds_max_array_size(max_array_size))
 919              || self.structs.values().any(|struct_type| struct_type.exceeds_max_array_size(max_array_size))
 920              || self.records.values().any(|record_type| record_type.exceeds_max_array_size(max_array_size))
 921              || self.closures.values().any(|closure| closure.exceeds_max_array_size(max_array_size))
 922              || self.functions.values().any(|function| function.exceeds_max_array_size(max_array_size))
 923              || self.constructor.iter().any(|constructor| constructor.exceeds_max_array_size(max_array_size))
 924      }
 925  
 926      /// Returns `true` if a program contains any V11 syntax.
 927      /// This includes:
 928      /// 1. `.raw` hash or signature verification variants
 929      /// 2. `ecdsa.verify.*` opcodes
 930      /// 3. arrays that exceed the previous maximum length of 32.
 931      #[inline]
 932      pub fn contains_v11_syntax(&self) -> bool {
 933          // The previous maximum array size before V11.
 934          const V10_MAX_ARRAY_ELEMENTS: u32 = 32;
 935  
 936          // Helper to check if any of the opcodes:
 937          // - start with `ecdsa.verify`, `serialize`, or `deserialize`
 938          // - end with `.raw` or `.native`
 939          let has_op = |opcode: &str| {
 940              opcode.starts_with("ecdsa.verify")
 941                  || opcode.starts_with("serialize")
 942                  || opcode.starts_with("deserialize")
 943                  || opcode.ends_with(".raw")
 944                  || opcode.ends_with(".native")
 945          };
 946  
 947          // Determine if any function instructions contain the new syntax.
 948          let function_contains = cfg_iter!(self.functions())
 949              .flat_map(|(_, function)| function.instructions())
 950              .any(|instruction| has_op(*instruction.opcode()));
 951  
 952          // Determine if any closure instructions contain the new syntax.
 953          let closure_contains = cfg_iter!(self.closures())
 954              .flat_map(|(_, closure)| closure.instructions())
 955              .any(|instruction| has_op(*instruction.opcode()));
 956  
 957          // Determine if any finalize commands or constructor commands contain the new syntax.
 958          let command_contains = cfg_iter!(self.functions())
 959              .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
 960              .flatten()
 961              .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
 962              .any(|command| matches!(command, Command::Instruction(instruction) if has_op(*instruction.opcode())));
 963  
 964          // Determine if any of the array types exceed the previous maximum length of 32.
 965          let array_size_exceeds = self.exceeds_max_array_size(V10_MAX_ARRAY_ELEMENTS);
 966  
 967          function_contains || closure_contains || command_contains || array_size_exceeds
 968      }
 969  
 970      /// Returns `true` if a program contains any V12 syntax.
 971      /// This includes `Operand::BlockTimestamp`.
 972      /// This is enforced to be `false` for programs before `ConsensusVersion::V12`.
 973      #[inline]
 974      pub fn contains_v12_syntax(&self) -> bool {
 975          // Check each instruction and output in each function's finalize scope for the use of
 976          // `Operand::BlockTimestamp`.
 977          cfg_iter!(self.functions()).any(|(_, function)| {
 978              function.finalize_logic().is_some_and(|finalize_logic| {
 979                  cfg_iter!(finalize_logic.commands()).any(|command| {
 980                      cfg_iter!(command.operands()).any(|operand| matches!(operand, Operand::BlockTimestamp))
 981                  })
 982              })
 983          })
 984      }
 985  
 986      /// Returns `true` if a program contains any string type.
 987      /// Before ConsensusVersion::V12, variable-length string sampling when using them as inputs caused deployment synthesis to be inconsistent and abort with probability 63/64.
 988      /// After ConsensusVersion::V12, string types are disallowed.
 989      #[inline]
 990      pub fn contains_string_type(&self) -> bool {
 991          self.mappings.values().any(|mapping| mapping.contains_string_type())
 992              || self.structs.values().any(|struct_type| struct_type.contains_string_type())
 993              || self.records.values().any(|record_type| record_type.contains_string_type())
 994              || self.closures.values().any(|closure| closure.contains_string_type())
 995              || self.functions.values().any(|function| function.contains_string_type())
 996              || self.constructor.iter().any(|constructor| constructor.contains_string_type())
 997      }
 998  }
 999  
1000  impl<N: Network> TypeName for ProgramCore<N> {
1001      /// Returns the type name as a string.
1002      #[inline]
1003      fn type_name() -> &'static str {
1004          "program"
1005      }
1006  }
1007  
1008  #[cfg(test)]
1009  mod tests {
1010      use super::*;
1011      use console::{
1012          network::MainnetV0,
1013          program::{Locator, ValueType},
1014      };
1015  
1016      type CurrentNetwork = MainnetV0;
1017  
1018      #[test]
1019      fn test_program_mapping() -> Result<()> {
1020          // Create a new mapping.
1021          let mapping = Mapping::<CurrentNetwork>::from_str(
1022              r"
1023  mapping message:
1024      key as field.public;
1025      value as field.public;",
1026          )?;
1027  
1028          // Initialize a new program.
1029          let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.delta; {mapping}"))?;
1030          // Ensure the mapping was added.
1031          assert!(program.contains_mapping(&Identifier::from_str("message")?));
1032          // Ensure the retrieved mapping matches.
1033          assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1034  
1035          Ok(())
1036      }
1037  
1038      #[test]
1039      fn test_program_struct() -> Result<()> {
1040          // Create a new struct.
1041          let struct_ = StructType::<CurrentNetwork>::from_str(
1042              r"
1043  struct message:
1044      first as field;
1045      second as field;",
1046          )?;
1047  
1048          // Initialize a new program.
1049          let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.delta; {struct_}"))?;
1050          // Ensure the struct was added.
1051          assert!(program.contains_struct(&Identifier::from_str("message")?));
1052          // Ensure the retrieved struct matches.
1053          assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1054  
1055          Ok(())
1056      }
1057  
1058      #[test]
1059      fn test_program_record() -> Result<()> {
1060          // Create a new record.
1061          let record = RecordType::<CurrentNetwork>::from_str(
1062              r"
1063  record foo:
1064      owner as address.private;
1065      first as field.private;
1066      second as field.public;",
1067          )?;
1068  
1069          // Initialize a new program.
1070          let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.delta; {record}"))?;
1071          // Ensure the record was added.
1072          assert!(program.contains_record(&Identifier::from_str("foo")?));
1073          // Ensure the retrieved record matches.
1074          assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1075  
1076          Ok(())
1077      }
1078  
1079      #[test]
1080      fn test_program_function() -> Result<()> {
1081          // Create a new function.
1082          let function = Function::<CurrentNetwork>::from_str(
1083              r"
1084  function compute:
1085      input r0 as field.public;
1086      input r1 as field.private;
1087      add r0 r1 into r2;
1088      output r2 as field.private;",
1089          )?;
1090  
1091          // Initialize a new program.
1092          let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.delta; {function}"))?;
1093          // Ensure the function was added.
1094          assert!(program.contains_function(&Identifier::from_str("compute")?));
1095          // Ensure the retrieved function matches.
1096          assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1097  
1098          Ok(())
1099      }
1100  
1101      #[test]
1102      fn test_program_import() -> Result<()> {
1103          // Initialize a new program.
1104          let program = Program::<CurrentNetwork>::from_str(
1105              r"
1106  import eth.delta;
1107  import usdc.delta;
1108  
1109  program swap.delta;
1110  
1111  // The `swap` function transfers ownership of the record
1112  // for token A to the record owner of token B, and vice-versa.
1113  function swap:
1114      // Input the record for token A.
1115      input r0 as eth.delta/eth.record;
1116      // Input the record for token B.
1117      input r1 as usdc.delta/usdc.record;
1118  
1119      // Send the record for token A to the owner of token B.
1120      call eth.delta/transfer r0 r1.owner r0.amount into r2 r3;
1121  
1122      // Send the record for token B to the owner of token A.
1123      call usdc.delta/transfer r1 r0.owner r1.amount into r4 r5;
1124  
1125      // Output the new record for token A.
1126      output r2 as eth.delta/eth.record;
1127      // Output the new record for token B.
1128      output r4 as usdc.delta/usdc.record;
1129      ",
1130          )
1131          .unwrap();
1132  
1133          // Ensure the program imports exist.
1134          assert!(program.contains_import(&ProgramID::from_str("eth.delta")?));
1135          assert!(program.contains_import(&ProgramID::from_str("usdc.delta")?));
1136  
1137          // Retrieve the 'swap' function.
1138          let function = program.get_function(&Identifier::from_str("swap")?)?;
1139  
1140          // Ensure there are two inputs.
1141          assert_eq!(function.inputs().len(), 2);
1142          assert_eq!(function.input_types().len(), 2);
1143  
1144          // Declare the expected input types.
1145          let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.delta/eth")?);
1146          let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.delta/usdc")?);
1147  
1148          // Ensure the inputs are external records.
1149          assert_eq!(function.input_types()[0], expected_input_type_1);
1150          assert_eq!(function.input_types()[1], expected_input_type_2);
1151  
1152          // Ensure the input variants are correct.
1153          assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1154          assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1155  
1156          // Ensure there are two instructions.
1157          assert_eq!(function.instructions().len(), 2);
1158  
1159          // Ensure the instructions are calls.
1160          assert_eq!(function.instructions()[0].opcode(), Opcode::Call);
1161          assert_eq!(function.instructions()[1].opcode(), Opcode::Call);
1162  
1163          // Ensure there are two outputs.
1164          assert_eq!(function.outputs().len(), 2);
1165          assert_eq!(function.output_types().len(), 2);
1166  
1167          // Declare the expected output types.
1168          let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.delta/eth")?);
1169          let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.delta/usdc")?);
1170  
1171          // Ensure the outputs are external records.
1172          assert_eq!(function.output_types()[0], expected_output_type_1);
1173          assert_eq!(function.output_types()[1], expected_output_type_2);
1174  
1175          // Ensure the output variants are correct.
1176          assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1177          assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1178  
1179          Ok(())
1180      }
1181  
1182      #[test]
1183      fn test_program_with_constructor() {
1184          // Initialize a new program.
1185          let program_string = r"import credits.delta;
1186  
1187  program good_constructor.delta;
1188  
1189  constructor:
1190      assert.eq edition 0u16;
1191      assert.eq credits.delta/edition 0u16;
1192      assert.neq checksum 0field;
1193      assert.eq credits.delta/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1194      set 1u8 into data[0u8];
1195  
1196  mapping data:
1197      key as u8.public;
1198      value as u8.public;
1199  
1200  function dummy:
1201  
1202  function check:
1203      async check into r0;
1204      output r0 as good_constructor.delta/check.future;
1205  
1206  finalize check:
1207      get data[0u8] into r0;
1208      assert.eq r0 1u8;
1209  ";
1210          let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1211  
1212          // Check that the string and bytes (de)serialization works.
1213          let serialized = program.to_string();
1214          let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1215          assert_eq!(program, deserialized);
1216  
1217          let serialized = program.to_bytes_le().unwrap();
1218          let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1219          assert_eq!(program, deserialized);
1220  
1221          // Check that the display works.
1222          let display = format!("{program}");
1223          assert_eq!(display, program_string);
1224  
1225          // Ensure the program contains a constructor.
1226          assert!(program.contains_constructor());
1227          assert_eq!(program.constructor().unwrap().commands().len(), 5);
1228      }
1229  
1230      #[test]
1231      fn test_program_equality_and_checksum() {
1232          fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1233              println!("Comparing programs:\n{program1}\n{program2}");
1234              let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1235              let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1236              assert_eq!(program1 == program2, expected_equal);
1237              assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1238          }
1239  
1240          // Test two identical programs, with different whitespace.
1241          run_test(r"program test.delta; function dummy:    ", r"program  test.delta;     function dummy:   ", true);
1242  
1243          // Test two programs, one with a different function name.
1244          run_test(r"program test.delta; function dummy:    ", r"program test.delta; function bummy:   ", false);
1245  
1246          // Test two programs, one with a constructor and one without.
1247          run_test(
1248              r"program test.delta; function dummy:    ",
1249              r"program test.delta; constructor: assert.eq true true; function dummy: ",
1250              false,
1251          );
1252  
1253          // Test two programs, both with a struct and function, but in different order.
1254          run_test(
1255              r"program test.delta; struct foo: data as u8; function dummy:",
1256              r"program test.delta; function dummy: struct foo: data as u8;",
1257              false,
1258          );
1259      }
1260  }