use crate::Instruction;
mod input;
use input::*;
mod output;
use output::*;
mod bytes;
mod parse;
use console::{
network::{error, prelude::*},
program::{Identifier, Register, RegisterType},
};
use indexmap::IndexSet;
#[derive(Clone, PartialEq, Eq)]
pub struct ClosureCore<N: Network> {
name: Identifier<N>,
inputs: IndexSet<Input<N>>,
instructions: Vec<Instruction<N>>,
outputs: IndexSet<Output<N>>,
}
impl<N: Network> ClosureCore<N> {
pub fn new(name: Identifier<N>) -> Self {
Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new() }
}
pub const fn name(&self) -> &Identifier<N> {
&self.name
}
pub const fn inputs(&self) -> &IndexSet<Input<N>> {
&self.inputs
}
pub fn instructions(&self) -> &[Instruction<N>] {
&self.instructions
}
pub const fn outputs(&self) -> &IndexSet<Output<N>> {
&self.outputs
}
pub fn output_types(&self) -> Vec<RegisterType<N>> {
self.outputs.iter().map(|output| output.register_type()).cloned().collect()
}
pub fn contains_external_struct(&self) -> bool {
self.inputs.iter().any(|input| input.register_type().contains_external_struct())
|| self.outputs.iter().any(|output| output.register_type().contains_external_struct())
|| self.instructions.iter().any(|instruction| instruction.contains_external_struct())
}
pub fn contains_string_type(&self) -> bool {
self.instructions.iter().any(|instruction| instruction.contains_string_type())
}
pub fn contains_identifier_type(&self) -> Result<bool> {
for input in &self.inputs {
if input.register_type().contains_identifier_type()? {
return Ok(true);
}
}
for output in &self.outputs {
if output.register_type().contains_identifier_type()? {
return Ok(true);
}
}
for instruction in &self.instructions {
if instruction.contains_identifier_type()? {
return Ok(true);
}
}
Ok(false)
}
pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
self.inputs.iter().any(|input| input.register_type().exceeds_max_array_size(max_array_size))
|| self.outputs.iter().any(|output| output.register_type().exceeds_max_array_size(max_array_size))
|| self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
}
}
impl<N: Network> ClosureCore<N> {
#[inline]
fn add_input(&mut self, input: Input<N>) -> Result<()> {
ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
self.inputs.insert(input);
Ok(())
}
#[inline]
pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
ensure!(
self.instructions.len() < N::MAX_INSTRUCTIONS,
"Cannot add more than {} instructions",
N::MAX_INSTRUCTIONS
);
for register in instruction.destinations() {
ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
}
self.instructions.push(instruction);
Ok(())
}
#[inline]
fn add_output(&mut self, output: Output<N>) -> Result<()> {
ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
ensure!(!matches!(output.register_type(), RegisterType::Record(..)), "Output register cannot be a record");
self.outputs.insert(output);
Ok(())
}
}
impl<N: Network> TypeName for ClosureCore<N> {
#[inline]
fn type_name() -> &'static str {
"closure"
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Closure, Instruction};
type CurrentNetwork = console::network::MainnetV0;
#[test]
fn test_add_input() {
let name = Identifier::from_str("closure_core_test").unwrap();
let mut closure = Closure::<CurrentNetwork>::new(name);
let input = Input::<CurrentNetwork>::from_str("input r0 as field;").unwrap();
assert!(closure.add_input(input.clone()).is_ok());
assert!(closure.add_input(input).is_err());
for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field;")).unwrap();
match closure.inputs.len() < CurrentNetwork::MAX_INPUTS {
true => assert!(closure.add_input(input).is_ok()),
false => assert!(closure.add_input(input).is_err()),
}
}
}
#[test]
fn test_add_instruction() {
let name = Identifier::from_str("closure_core_test").unwrap();
let mut closure = Closure::<CurrentNetwork>::new(name);
let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
assert!(closure.add_instruction(instruction).is_ok());
for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
match closure.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
true => assert!(closure.add_instruction(instruction).is_ok()),
false => assert!(closure.add_instruction(instruction).is_err()),
}
}
}
#[test]
fn test_add_output() {
let name = Identifier::from_str("closure_core_test").unwrap();
let mut closure = Closure::<CurrentNetwork>::new(name);
let output = Output::<CurrentNetwork>::from_str("output r0 as field;").unwrap();
assert!(closure.add_output(output).is_ok());
for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field;")).unwrap();
match closure.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
true => assert!(closure.add_output(output).is_ok()),
false => assert!(closure.add_output(output).is_err()),
}
}
}
}