mod closure;
pub use closure::*;
pub mod finalize;
mod function;
pub use function::*;
mod import;
pub use import::*;
mod instruction;
pub use instruction::*;
mod mapping;
pub use mapping::*;
mod bytes;
mod parse;
mod serialize;
use console::{
network::prelude::*,
program::{EntryType, Identifier, PlaintextType, ProgramID, RecordType, Struct},
};
use indexmap::IndexMap;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum ProgramDefinition {
Mapping,
Struct,
Record,
Closure,
Function,
}
#[derive(Clone, PartialEq, Eq)]
pub struct Program<N: Network> {
id: ProgramID<N>,
imports: IndexMap<ProgramID<N>, Import<N>>,
identifiers: IndexMap<Identifier<N>, ProgramDefinition>,
mappings: IndexMap<Identifier<N>, Mapping<N>>,
structs: IndexMap<Identifier<N>, Struct<N>>,
records: IndexMap<Identifier<N>, RecordType<N>>,
closures: IndexMap<Identifier<N>, Closure<N>>,
functions: IndexMap<Identifier<N>, Function<N>>,
}
impl<N: Network> Program<N> {
#[inline]
pub fn new(id: ProgramID<N>) -> Result<Self> {
ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
ensure!(id.is_aleo(), "Program network is invalid: {}", id.network());
Ok(Self {
id,
imports: IndexMap::new(),
identifiers: IndexMap::new(),
mappings: IndexMap::new(),
structs: IndexMap::new(),
records: IndexMap::new(),
closures: IndexMap::new(),
functions: IndexMap::new(),
})
}
#[inline]
pub fn credits() -> Result<Self> {
Self::from_str(
r"
program credits.aleo;
record credits:
owner as address.private;
microcredits as u64.private;
function mint:
input r0 as address.public;
input r1 as u64.public;
cast r0 r1 into r2 as credits.record;
output r2 as credits.record;
function transfer:
input r0 as credits.record;
input r1 as address.private;
input r2 as u64.private;
sub r0.microcredits r2 into r3;
cast r1 r2 into r4 as credits.record;
cast r0.owner r3 into r5 as credits.record;
output r4 as credits.record;
output r5 as credits.record;
function join:
input r0 as credits.record;
input r1 as credits.record;
add r0.microcredits r1.microcredits into r2;
cast r0.owner r2 into r3 as credits.record;
output r3 as credits.record;
function split:
input r0 as credits.record;
input r1 as u64.private;
sub r0.microcredits r1 into r2;
cast r0.owner r1 into r3 as credits.record;
cast r0.owner r2 into r4 as credits.record;
output r3 as credits.record;
output r4 as credits.record;
function fee:
input r0 as credits.record;
input r1 as u64.public;
assert.neq r1 0u64;
sub r0.microcredits r1 into r2;
cast r0.owner r2 into r3 as credits.record;
output r3 as credits.record;
",
)
}
pub const fn id(&self) -> &ProgramID<N> {
&self.id
}
pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
&self.imports
}
pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
&self.mappings
}
pub const fn closures(&self) -> &IndexMap<Identifier<N>, Closure<N>> {
&self.closures
}
pub const fn functions(&self) -> &IndexMap<Identifier<N>, Function<N>> {
&self.functions
}
pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
self.imports.contains_key(id)
}
pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
self.mappings.contains_key(name)
}
pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
self.structs.contains_key(name)
}
pub fn contains_record(&self, name: &Identifier<N>) -> bool {
self.records.contains_key(name)
}
pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
self.closures.contains_key(name)
}
pub fn contains_function(&self, name: &Identifier<N>) -> bool {
self.functions.contains_key(name)
}
pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
Ok(mapping)
}
pub fn get_struct(&self, name: &Identifier<N>) -> Result<Struct<N>> {
let struct_ = self.structs.get(name).cloned().ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
Ok(struct_)
}
pub fn get_record(&self, name: &Identifier<N>) -> Result<RecordType<N>> {
let record = self.records.get(name).cloned().ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
Ok(record)
}
pub fn get_closure(&self, name: &Identifier<N>) -> Result<Closure<N>> {
let closure = self.closures.get(name).cloned().ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
Ok(closure)
}
pub fn get_function(&self, name: &Identifier<N>) -> Result<Function<N>> {
let function = self.functions.get(name).cloned().ok_or_else(|| anyhow!("Function '{name}' is not defined."))?;
ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
Ok(function)
}
}
impl<N: Network> Program<N> {
#[inline]
fn add_import(&mut self, import: Import<N>) -> Result<()> {
let import_name = *import.name();
ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
ensure!(
!self.imports.contains_key(import.program_id()),
"Import '{}' is already defined.",
import.program_id()
);
if self.imports.insert(*import.program_id(), import.clone()).is_some() {
bail!("'{}' already exists in the program.", import.program_id())
}
Ok(())
}
#[inline]
fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
let mapping_name = *mapping.name();
ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
if self.identifiers.insert(mapping_name, ProgramDefinition::Mapping).is_some() {
bail!("'{mapping_name}' already exists in the program.")
}
if self.mappings.insert(mapping_name, mapping).is_some() {
bail!("'{mapping_name}' already exists in the program.")
}
Ok(())
}
#[inline]
fn add_struct(&mut self, struct_: Struct<N>) -> Result<()> {
let struct_name = *struct_.name();
ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
for (identifier, plaintext_type) in struct_.members() {
ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
match plaintext_type {
PlaintextType::Literal(..) => continue,
PlaintextType::Struct(member_identifier) => {
if !self.structs.contains_key(member_identifier) {
bail!("'{member_identifier}' in struct '{}' is not defined.", struct_name)
}
}
}
}
if self.identifiers.insert(struct_name, ProgramDefinition::Struct).is_some() {
bail!("'{}' already exists in the program.", struct_name)
}
if self.structs.insert(struct_name, struct_).is_some() {
bail!("'{}' already exists in the program.", struct_name)
}
Ok(())
}
#[inline]
fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
let record_name = *record.name();
ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
for (identifier, entry_type) in record.entries() {
ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
match entry_type {
EntryType::Constant(plaintext_type)
| EntryType::Public(plaintext_type)
| EntryType::Private(plaintext_type) => match plaintext_type {
PlaintextType::Literal(..) => continue,
PlaintextType::Struct(identifier) => {
if !self.structs.contains_key(identifier) {
bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
}
}
},
}
}
if self.identifiers.insert(record_name, ProgramDefinition::Record).is_some() {
bail!("'{record_name}' already exists in the program.")
}
if self.records.insert(record_name, record).is_some() {
bail!("'{record_name}' already exists in the program.")
}
Ok(())
}
#[inline]
fn add_closure(&mut self, closure: Closure<N>) -> Result<()> {
let closure_name = *closure.name();
ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
if self.identifiers.insert(closure_name, ProgramDefinition::Closure).is_some() {
bail!("'{closure_name}' already exists in the program.")
}
if self.closures.insert(closure_name, closure).is_some() {
bail!("'{closure_name}' already exists in the program.")
}
Ok(())
}
#[inline]
fn add_function(&mut self, function: Function<N>) -> Result<()> {
let function_name = *function.name();
ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
if self.identifiers.insert(function_name, ProgramDefinition::Function).is_some() {
bail!("'{function_name}' already exists in the program.")
}
if self.functions.insert(function_name, function).is_some() {
bail!("'{function_name}' already exists in the program.")
}
Ok(())
}
}
impl<N: Network> Program<N> {
#[rustfmt::skip]
const KEYWORDS: &'static [&'static str] = &[
"const",
"constant",
"public",
"private",
"address",
"boolean",
"field",
"group",
"i8",
"i16",
"i32",
"i64",
"i128",
"u8",
"u16",
"u32",
"u64",
"u128",
"scalar",
"string",
"true",
"false",
"input",
"output",
"as",
"into",
"record",
"owner",
"function",
"struct",
"closure",
"program",
"aleo",
"self",
"storage",
"mapping",
"key",
"value",
"global",
"return",
"break",
"assert",
"continue",
"let",
"if",
"else",
"while",
"for",
"switch",
"case",
"default",
"match",
"enum",
"struct",
"union",
"trait",
"impl",
"type",
];
fn is_unique_name(&self, name: &Identifier<N>) -> bool {
!self.identifiers.contains_key(name)
}
pub fn is_reserved_opcode(name: &str) -> bool {
Instruction::<N>::OPCODES.iter().any(|opcode| **opcode == name)
}
pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
let name = name.to_string();
Self::KEYWORDS.iter().any(|keyword| *keyword == name)
}
}
impl<N: Network> TypeName for Program<N> {
#[inline]
fn type_name() -> &'static str {
"program"
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CallStack, Execution, Inclusion};
use circuit::network::AleoV0;
use console::{
account::{Address, PrivateKey},
network::Testnet3,
program::{Locator, Plaintext, Record, Value, ValueType},
types::Field,
};
use parking_lot::RwLock;
use std::sync::Arc;
type CurrentNetwork = Testnet3;
type CurrentAleo = AleoV0;
#[test]
fn test_program_mapping() -> Result<()> {
let mapping = Mapping::<CurrentNetwork>::from_str(
r"
mapping message:
key first as field.public;
value second as field.public;",
)?;
let mut program = Program::<CurrentNetwork>::new(ProgramID::from_str("unknown.aleo")?)?;
program.add_mapping(mapping.clone())?;
assert!(program.contains_mapping(&Identifier::from_str("message")?));
assert_eq!(mapping, program.get_mapping(&Identifier::from_str("message")?)?);
Ok(())
}
#[test]
fn test_program_struct() -> Result<()> {
let struct_ = Struct::<CurrentNetwork>::from_str(
r"
struct message:
first as field;
second as field;",
)?;
let mut program = Program::<CurrentNetwork>::new(ProgramID::from_str("unknown.aleo")?)?;
program.add_struct(struct_.clone())?;
assert!(program.contains_struct(&Identifier::from_str("message")?));
assert_eq!(struct_, program.get_struct(&Identifier::from_str("message")?)?);
Ok(())
}
#[test]
fn test_program_record() -> Result<()> {
let record = RecordType::<CurrentNetwork>::from_str(
r"
record foo:
owner as address.private;
first as field.private;
second as field.public;",
)?;
let mut program = Program::<CurrentNetwork>::new(ProgramID::from_str("unknown.aleo")?)?;
program.add_record(record.clone())?;
assert!(program.contains_record(&Identifier::from_str("foo")?));
assert_eq!(record, program.get_record(&Identifier::from_str("foo")?)?);
Ok(())
}
#[test]
fn test_program_function() -> Result<()> {
let function = Function::<CurrentNetwork>::from_str(
r"
function compute:
input r0 as field.public;
input r1 as field.private;
add r0 r1 into r2;
output r2 as field.private;",
)?;
let mut program = Program::<CurrentNetwork>::new(ProgramID::from_str("unknown.aleo")?)?;
program.add_function(function.clone())?;
assert!(program.contains_function(&Identifier::from_str("compute")?));
assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
Ok(())
}
#[test]
fn test_program_import() -> Result<()> {
let program = Program::<CurrentNetwork>::from_str(
r"
import eth.aleo;
import usdc.aleo;
program swap.aleo;
// The `swap` function transfers ownership of the record
// for token A to the record owner of token B, and vice-versa.
function swap:
// Input the record for token A.
input r0 as eth.aleo/eth.record;
// Input the record for token B.
input r1 as usdc.aleo/usdc.record;
// Send the record for token A to the owner of token B.
call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
// Send the record for token B to the owner of token A.
call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
// Output the new record for token A.
output r2 as eth.aleo/eth.record;
// Output the new record for token B.
output r4 as usdc.aleo/usdc.record;
",
)
.unwrap();
assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
let function = program.get_function(&Identifier::from_str("swap")?)?;
assert_eq!(function.inputs().len(), 2);
assert_eq!(function.input_types().len(), 2);
assert_eq!(function.input_types()[0], ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?));
assert_eq!(function.input_types()[1], ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?));
assert_eq!(function.instructions().len(), 2);
assert_eq!(function.instructions()[0].opcode(), Opcode::Call);
assert_eq!(function.instructions()[1].opcode(), Opcode::Call);
assert_eq!(function.outputs().len(), 2);
assert_eq!(function.output_types().len(), 2);
assert_eq!(function.output_types()[0], ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?));
assert_eq!(function.output_types()[1], ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?));
Ok(())
}
#[test]
fn test_program_evaluate_function() {
let program = Program::<CurrentNetwork>::from_str(
r"
program example.aleo;
function foo:
input r0 as field.public;
input r1 as field.private;
add r0 r1 into r2;
output r2 as field.private;
",
)
.unwrap();
let function_name = Identifier::from_str("foo").unwrap();
let inputs = [
Value::<CurrentNetwork>::Plaintext(Plaintext::from_str("2field").unwrap()),
Value::Plaintext(Plaintext::from_str("3field").unwrap()),
];
let process = crate::process::test_helpers::sample_process(&program);
let authorization = {
let rng = &mut TestRng::default();
let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let authorization = process
.authorize::<CurrentAleo, _>(&caller_private_key, program.id(), function_name, inputs.iter(), rng)
.unwrap();
assert_eq!(authorization.len(), 1);
authorization
};
let stack = process.get_stack(program.id()).unwrap();
let expected = Value::Plaintext(Plaintext::<CurrentNetwork>::from_str("5field").unwrap());
let response =
stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization.replicate()).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
let response = stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
}
#[test]
fn test_program_evaluate_struct_and_function() {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program example.aleo;
struct message:
first as field;
second as field;
function compute:
input r0 as message.private;
add r0.first r0.second into r1;
output r1 as field.private;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let function_name = Identifier::from_str("compute").unwrap();
let input =
Value::<CurrentNetwork>::Plaintext(Plaintext::from_str("{ first: 2field, second: 3field }").unwrap());
let expected = Value::Plaintext(Plaintext::from_str("5field").unwrap());
let process = crate::process::test_helpers::sample_process(&program);
let authorization = {
let rng = &mut TestRng::default();
let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let authorization = process
.authorize::<CurrentAleo, _>(&caller_private_key, program.id(), function_name, [input].iter(), rng)
.unwrap();
assert_eq!(authorization.len(), 1);
authorization
};
let stack = process.get_stack(program.id()).unwrap();
let response =
stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization.replicate()).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
let response = stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
}
#[test]
fn test_program_evaluate_record_and_function() {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program token.aleo;
record token:
owner as address.private;
token_amount as u64.private;
function compute:
input r0 as token.record;
add r0.token_amount r0.token_amount into r1;
output r1 as u64.private;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let function_name = Identifier::from_str("compute").unwrap();
let rng = &mut TestRng::default();
let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let caller = Address::try_from(&caller_private_key).unwrap();
let input_record = Record::from_str(&format!(
"{{ owner: {caller}.private, token_amount: 100u64.private, _nonce: 0group.public }}"
))
.unwrap();
let input = Value::<CurrentNetwork>::Record(input_record);
let expected = Value::Plaintext(Plaintext::from_str("200u64").unwrap());
let process = crate::process::test_helpers::sample_process(&program);
let authorization = process
.authorize::<CurrentAleo, _>(&caller_private_key, program.id(), function_name, [input].iter(), rng)
.unwrap();
assert_eq!(authorization.len(), 1);
let stack = process.get_stack(program.id()).unwrap();
let response =
stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization.replicate()).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
let response = stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
}
#[test]
fn test_program_evaluate_call() {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program example_call.aleo;
// (a + (a + b)) + (a + b) == (3a + 2b)
closure execute:
input r0 as field;
input r1 as field;
add r0 r1 into r2;
add r0 r2 into r3;
add r2 r3 into r4;
output r4 as field;
output r3 as field;
output r2 as field;
function compute:
input r0 as field.private;
input r1 as field.public;
call execute r0 r1 into r2 r3 r4;
output r2 as field.private;
output r3 as field.private;
output r4 as field.private;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let function_name = Identifier::from_str("compute").unwrap();
let r0 = Value::<CurrentNetwork>::Plaintext(Plaintext::from_str("3field").unwrap());
let r1 = Value::<CurrentNetwork>::Plaintext(Plaintext::from_str("5field").unwrap());
let r2 = Value::Plaintext(Plaintext::from_str("19field").unwrap());
let r3 = Value::Plaintext(Plaintext::from_str("11field").unwrap());
let r4 = Value::Plaintext(Plaintext::from_str("8field").unwrap());
{
let process = crate::process::test_helpers::sample_process(&program);
process.synthesize_key::<CurrentAleo, _>(program.id(), &function_name, &mut TestRng::default()).unwrap();
}
let process = crate::process::test_helpers::sample_process(&program);
let authorization = {
let rng = &mut TestRng::default();
let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let authorization = process
.authorize::<CurrentAleo, _>(
&caller_private_key,
program.id(),
function_name,
[r0.clone(), r1.clone()].iter(),
rng,
)
.unwrap();
assert_eq!(authorization.len(), 1);
authorization
};
let stack = process.get_stack(program.id()).unwrap();
let response =
stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization.replicate()).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(3, candidate.len());
assert_eq!(r2, candidate[0]);
assert_eq!(r3, candidate[1]);
assert_eq!(r4, candidate[2]);
let response = stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(3, candidate.len());
assert_eq!(r2, candidate[0]);
assert_eq!(r3, candidate[1]);
assert_eq!(r4, candidate[2]);
use circuit::Environment;
assert_eq!(0, CurrentAleo::num_constants());
assert_eq!(1, CurrentAleo::num_public());
assert_eq!(0, CurrentAleo::num_private());
assert_eq!(0, CurrentAleo::num_constraints());
let rng = &mut TestRng::default();
let burner_private_key = PrivateKey::new(rng).unwrap();
let authorization = process
.authorize::<CurrentAleo, _>(&burner_private_key, program.id(), function_name, [r0, r1].iter(), rng)
.unwrap();
assert_eq!(authorization.len(), 1);
let execution = Arc::new(RwLock::new(Execution::new()));
let inclusion = Arc::new(RwLock::new(Inclusion::new()));
let metrics = Arc::new(RwLock::new(Vec::new()));
let call_stack = CallStack::execute(authorization, execution, inclusion, metrics).unwrap();
let response = stack.execute_function::<CurrentAleo, _>(call_stack, rng).unwrap();
let candidate = response.outputs();
assert_eq!(3, candidate.len());
assert_eq!(r2, candidate[0]);
assert_eq!(r3, candidate[1]);
assert_eq!(r4, candidate[2]);
}
#[test]
fn test_program_evaluate_cast() {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program token_with_cast.aleo;
record token:
owner as address.private;
token_amount as u64.private;
function compute:
input r0 as token.record;
cast r0.owner r0.token_amount into r1 as token.record;
output r1 as token.record;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let function_name = Identifier::from_str("compute").unwrap();
let rng = &mut TestRng::default();
let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let caller = Address::try_from(&caller_private_key).unwrap();
let input_record = Record::from_str(&format!(
"{{ owner: {caller}.private, token_amount: 100u64.private, _nonce: 0group.public }}"
))
.unwrap();
let input = Value::<CurrentNetwork>::Record(input_record);
let process = crate::process::test_helpers::sample_process(&program);
let authorization = process
.authorize::<CurrentAleo, _>(&caller_private_key, program.id(), function_name, [input].iter(), rng)
.unwrap();
assert_eq!(authorization.len(), 1);
let request = authorization.peek_next().unwrap();
let randomizer = CurrentNetwork::hash_to_scalar_psd2(&[*request.tvk(), Field::from_u64(1)]).unwrap();
let nonce = CurrentNetwork::g_scalar_multiply(&randomizer);
let expected = Value::from_str(&format!(
"{{ owner: {caller}.private, token_amount: 100u64.private, _nonce: {nonce}.public }}"
))
.unwrap();
let stack = process.get_stack(program.id()).unwrap();
let response =
stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization.replicate()).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
let response = stack.evaluate_function::<CurrentAleo>(CallStack::evaluate(authorization).unwrap()).unwrap();
let candidate = response.outputs();
assert_eq!(1, candidate.len());
assert_eq!(expected, candidate[0]);
}
}