diff --git a/Cargo.lock b/Cargo.lock index 4704d76bfa..3e45bda583 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -823,6 +823,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "disassembler" +version = "0.1.0" +dependencies = [ + "leo-ast", + "leo-span", + "snarkvm", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1380,6 +1389,7 @@ dependencies = [ "serde", "serde_json", "smallvec", + "snarkvm", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1b72c1fa2d..a74604ec70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,8 @@ members = [ "docs/grammar", "errors", "leo/package", - "tests/test-framework" + "tests/test-framework", + "utils/disassembler" ] [workspace.dependencies.snarkvm] diff --git a/compiler/ast/Cargo.toml b/compiler/ast/Cargo.toml index 7e85eab976..95ddf55f07 100644 --- a/compiler/ast/Cargo.toml +++ b/compiler/ast/Cargo.toml @@ -18,6 +18,9 @@ license = "GPL-3.0" edition = "2021" rust-version = "1.69" +[dependencies.snarkvm] +workspace = true + [dependencies.leo-errors] path = "../../errors" version = "=1.10.0" diff --git a/compiler/ast/src/common/identifier.rs b/compiler/ast/src/common/identifier.rs index 59ec2482e4..b2974b8004 100644 --- a/compiler/ast/src/common/identifier.rs +++ b/compiler/ast/src/common/identifier.rs @@ -16,6 +16,7 @@ use leo_errors::Result; use leo_span::{Span, Symbol}; +use snarkvm::console::program::Identifier as IdentifierCore; use crate::{simple_node_impl, Node, NodeID}; use serde::{ @@ -28,6 +29,9 @@ use serde::{ Serialize, Serializer, }; +use snarkvm::{ + prelude::Network, +}; use std::{ collections::BTreeMap, fmt, @@ -152,3 +156,11 @@ impl<'de> Deserialize<'de> for Identifier { deserializer.deserialize_str(IdentifierVisitor) } } +impl From> for Identifier { + fn from(id: IdentifierCore) -> Self { + let id_str = id.to_string(); + dbg!(id_str.clone()); + let a = "Dummy"; + Self { name: Symbol::intern(a), span: Default::default(), id: Default::default() } + } +} diff --git a/compiler/ast/src/program/program_id.rs b/compiler/ast/src/program/program_id.rs index 77f36c9010..39f3a6e53d 100644 --- a/compiler/ast/src/program/program_id.rs +++ b/compiler/ast/src/program/program_id.rs @@ -18,6 +18,7 @@ use crate::Identifier; use core::fmt; use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; +use snarkvm::{console::program::ProgramID, prelude::Network}; use std::collections::BTreeMap; /// An identifier for a program that is eventually deployed to the network. @@ -92,3 +93,9 @@ impl<'de> Deserialize<'de> for ProgramId { deserializer.deserialize_str(ProgramIdVisitor) } } + +impl From> for ProgramId { + fn from(program: ProgramID) -> Self { + Self { name: Identifier::from(*program.name()), network: Identifier::from(*program.network()) } + } +} diff --git a/compiler/ast/src/struct/mod.rs b/compiler/ast/src/struct/mod.rs index bec8684036..b3fa57d594 100644 --- a/compiler/ast/src/struct/mod.rs +++ b/compiler/ast/src/struct/mod.rs @@ -17,12 +17,20 @@ pub mod member; pub use member::*; -use crate::{Identifier, Node, NodeID}; +use crate::{Identifier, Mode, Node, NodeID, Type}; use leo_span::{Span, Symbol}; use serde::{Deserialize, Serialize}; use std::fmt; +use snarkvm::{ + console::program::{RecordType, StructType}, + prelude::{ + EntryType::{Private, Public, Constant}, + Network, + }, +}; + /// A struct type definition, e.g., `struct Foo { my_field: Bar }`. /// In some languages these are called `struct`s. /// @@ -77,3 +85,51 @@ impl fmt::Display for Struct { } crate::simple_node_impl!(Struct); + +// impl From<&StructType> for Struct { +// fn from(input: &StructType) -> Self { +// Self { +// identifier: Identifier::from(input.name()), +// members: input +// .members() +// .iter() +// .map(|(id, type_)| Member { +// mode: Mode::None, +// identifier: Identifier::from(id), +// type_: Type::from(type_), +// span: Default::default(), +// id: Default::default(), +// }) +// .collect(), +// is_record: false, +// span: Default::default(), +// id: Default::default(), +// } +// } +// } +// +// impl From<&RecordType> for Struct { +// fn from(input: &RecordType) -> Self { +// Self { +// identifier: Identifier::from(input.name()), +// members: input +// .entries() +// .iter() +// .map(|(id, entry)| Member { +// mode: Mode::None, +// identifier: Identifier::from(id), +// type_: match entry { +// Public(t) => Type::from(t), +// Private(t) => Type::from(t), +// Constant(t) => Type::from(t), +// }, +// span: Default::default(), +// id: Default::default(), +// }) +// .collect(), +// is_record: true, +// span: Default::default(), +// id: Default::default(), +// } +// } +// } diff --git a/compiler/ast/src/stub/mod.rs b/compiler/ast/src/stub/mod.rs index dfeb23cde4..d17e4667aa 100644 --- a/compiler/ast/src/stub/mod.rs +++ b/compiler/ast/src/stub/mod.rs @@ -21,7 +21,8 @@ pub use finalize_stub::*; pub mod function_stub; pub use function_stub::*; -use crate::{ConstDeclaration, Identifier, Mapping, ProgramId, Struct}; + +use crate::{ConstDeclaration, Mapping, ProgramId, Struct}; use leo_span::{Span, Symbol}; use serde::{Deserialize, Serialize}; use std::fmt; @@ -30,7 +31,7 @@ use std::fmt; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct Stub { /// A vector of imported programs. - pub imports: Vec, + pub imports: Vec, /// The stub id pub stub_id: ProgramId, /// A vector of const definitions. diff --git a/compiler/ast/src/types/type_.rs b/compiler/ast/src/types/type_.rs index 928a80df78..db28be2cf8 100644 --- a/compiler/ast/src/types/type_.rs +++ b/compiler/ast/src/types/type_.rs @@ -14,10 +14,15 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{ArrayType, Identifier, IntegerType, MappingType, TupleType}; +use crate::{ArrayType, common, Identifier, IntegerType, MappingType, TupleType}; use itertools::Itertools; use serde::{Deserialize, Serialize}; +use snarkvm::prelude::{ + Network, + PlaintextType, + PlaintextType::{Array, Literal, Struct}, +}; use std::fmt; /// Explicit type used for defining a variable or expression type diff --git a/tests/expectations/parser/program/stub.out b/tests/expectations/parser/program/stub.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/tests/compiler/function/test.leo b/tests/tests/compiler/function/test.leo new file mode 100644 index 0000000000..a4fdeb0a2d --- /dev/null +++ b/tests/tests/compiler/function/test.leo @@ -0,0 +1,34 @@ +/* +namespace: Compile +expectation: Pass +*/ + +program test.aleo { + struct message { + sender: u8, + object: u8, + } + mapping balances: u8 => u8; + + transition a(arg1: message) -> message { + return message { + sender: 1u8, + object: 1u8, + }; + } + + function b(arg1: message) -> message { + return message { + sender: 1u8, + object: 1u8, + }; + } + + function c(arg1: balances) -> balances { + return arg1; + } + + transition d(arg1: balances) -> balances { + return arg1; + } +} diff --git a/utils/disassembler/Cargo.toml b/utils/disassembler/Cargo.toml new file mode 100644 index 0000000000..5b7089db05 --- /dev/null +++ b/utils/disassembler/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "disassembler" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies.snarkvm] +workspace = true + +[dependencies.leo-ast] +path = "../../compiler/ast" +version = "=1.9.4" + +[dependencies.leo-span] +path = "../../compiler/span" +version = "1.9.3" diff --git a/utils/disassembler/src/main.rs b/utils/disassembler/src/main.rs new file mode 100644 index 0000000000..300f1bc189 --- /dev/null +++ b/utils/disassembler/src/main.rs @@ -0,0 +1,383 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use snarkvm::{ + console::network::Testnet3, + prelude::{Itertools, Network, RegisterType, ValueType}, + synthesizer::{ + program::{CommandTrait, InstructionTrait, Program, ProgramCore}, + Command, + Instruction, + }, +}; +use snarkvm::console::program::Identifier as IdentifierCore; +use leo_span::Symbol; +use std::{ops::Add, str::FromStr}; + +use leo_ast::{Identifier, ProgramId, Struct, Stub}; +use leo_ast::Type::Identifier as IdentifierType; + +type CurrentNetwork = Testnet3; + +fn main() { + let a = Symbol::intern("aleo"); +} + +// fn old() { +// // let credits_aleo = +// // std::fs::read_to_string("/Users/evanschott/work/leo/utils/disassembler/src/tests/credits.aleo").unwrap(); +// // println!("{}", code_gen(credits_aleo)); +// let aleo_prog_1 = r"import credits.aleo; +// +// import battleship.aleo; +// import juice.aleo; +// +// program to_parse.aleo; +// +// function new_board_state: +// input r0 as u64.private; +// input r1 as address.private; +// cast self.caller 0u64 0u64 r0 self.caller r1 false into r2 as board_state.record; +// output r2 as board_state.record; +// +// closure add_up: +// input r0 as u8; +// input r1 as u8; +// add r0 r1 into r2; +// output r2 as u8; +// +// function transfer_public_to_private: +// input r0 as address.private; +// input r1 as u64.public; +// cast r0 r1 into r2 as credits.record; +// async transfer_public_to_private self.caller r1 into r3; +// output r2 as credits.record; +// output r3 as credits.aleo/transfer_public_to_private.future; +// +// finalize transfer_public_to_private: +// input r0 as address.public; +// input r1 as u64.public; +// get.or_use account[r0] 0u64 into r2; +// sub r2 r1 into r3; +// set r3 into account[r0]; +// "; +// let program = Program::::from_str(aleo_prog_1); +// match program { +// Ok(p) => { +// let disassembled = disassemble(p); +// println!("{}", disassembled) +// } +// Err(e) => { +// println!("{}", e); +// } +// } +// // println!("{}", disassemble(Program::::from_str(aleo_prog_1).unwrap())); +// } + +// fn disassemble, Command: CommandTrait>( +// program: ProgramCore, +// ) -> Stub { +// dbg!(program.records()); +// +// let mut stub = Stub { +// imports: Vec::new(), +// stub_id: ProgramId::from(program.id()), +// consts: Vec::new(), +// structs: Vec::new(), +// mappings: Vec::new(), +// functions: Vec::new(), +// span: Default::default(), +// }; +// +// +// let structs = program.structs().iter().map(|(id,s)| { +// (Identifier::from(id).name, Struct::from(s)) +// }).collect_vec(); +// let records = program.records().iter().map(|(id,s)| { +// dbg!(id.clone()); +// dbg!(s); +// println!("{}", id.clone()); +// let id_str = id.clone().to_string(); +// let id_sym = Symbol::intern(&id_str); +// ((id_sym, Struct::from(s))) +// }).collect_vec(); +// +// +// stub +// +// // Stub { +// // imports: program.imports().into_iter().map(|(id, import)| ProgramId::from(id)).collect(), +// // stub_id: ProgramId::from(program.id()), +// // consts: Vec::new(), +// // structs: [structs,records].concat(), +// // mappings: Vec::new(), +// // functions: Vec::new(), // TODO: Add functions AND closures +// // span: Default::default(), +// // } +// } + +fn print_visibility(visibility: &ValueType) -> String { + match visibility { + ValueType::Public(_) => "public ".to_string(), + _ => "".to_string(), + } +} + +fn print_transition_input(input: &ValueType, index: usize, len: usize) -> String { + format!("{}a{}: {}", print_visibility(input), index + 1, print_transition_value(input, index, len)) +} + +fn print_transition_value(value: &ValueType, index: usize, len: usize) -> String { + let value_str = match value { + ValueType::Constant(val) => format!("{}", val.to_string().replace("boolean", "bool")), + ValueType::Public(val) => format!("{}", val.to_string().replace("boolean", "bool")), + ValueType::Private(val) => format!("{}", val.to_string().replace("boolean", "bool")), + ValueType::Record(id) => format!("{}", id.to_string()), + ValueType::ExternalRecord(loc) => format!("{}.aleo/{}", loc.name(), loc.resource().to_string()), + ValueType::Future(_) => panic!("Futures must be filtered out by this stage"), + }; + let situational_comma = if index == len - 1 { "" } else { ", " }; + format!("{}{}", value_str, situational_comma) +} + +fn print_transition_output(output: &ValueType, index: usize, len: usize) -> String { + let (left, right) = if len == 1 { + (" -> ", "") + } else if index == 0 { + (" -> (", "") + } else if index == len - 1 { + ("", ")") + } else { + ("", "") + }; + format!("{}{}{}", left, print_transition_value(output, index, len), right) +} + +fn print_function_output(output: &RegisterType, index: usize, len: usize) -> String { + let (left, right) = if len == 1 { + (" -> ", "") + } else if index == 0 { + (" -> (", "") + } else if index == len - 1 { + ("", ")") + } else { + ("", "") + }; + format!("{}{}{}", left, print_function_value(output, index, len), right) +} + +fn print_function_input(input: &RegisterType, index: usize, len: usize) -> String { + format!("a{}: {}", index + 1, print_function_value(input, index, len)) +} + +fn print_function_value(value: &RegisterType, index: usize, len: usize) -> String { + let value_str = match value { + RegisterType::Plaintext(val) => format!("{}", val.to_string().replace("boolean", "bool")), + RegisterType::Record(val) => format!("{}", val.to_string()), + RegisterType::ExternalRecord(loc) => format!("{}.aleo/{}", loc.name(), loc.resource().to_string()), + RegisterType::Future(_) => panic!("Futures must be filtered out by this stage"), + }; + let situational_comma = if index == len - 1 { "" } else { ", " }; + format!("{}{}", value_str, situational_comma) +} + +fn code_gen(input: String) -> String { + // Parse a new program. + let result = Program::::from_str(&input).unwrap(); + + let mut output = format!("stub {}.aleo {{", result.id().name()); + + // Write imports + output.push_str( + &result.imports().into_iter().map(|(name, _)| format!("\n import {};\n", name)).collect::(), + ); + + // Write records + output.push_str( + &result + .records() + .into_iter() + .map(|(name, fields)| { + format!("\n record {} {{\n owner: address,\n", name).add( + &(fields + .entries() + .into_iter() + .map(|(id, entry_type)| { + if entry_type.plaintext_type().to_string() == "boolean" { + return format!(" {}: bool,\n", id); + } else { + format!(" {}: {},\n", id, entry_type.plaintext_type().to_string()) + } + }) + .collect::() + + " }\n") + .to_string(), + ) + }) + .collect::(), + ); + + // Write transitions + output.push_str( + &result + .functions() + .into_iter() + .map(|(name, value)| { + let (inputs, outputs) = (value.inputs(), value.outputs().clone()); + + // Can assume that last output is a future if the function has associated finalize + let outputs_vec = if value.finalize_logic().is_some() { + outputs[..outputs.len() - 1].iter().collect_vec() + } else { + outputs.iter().collect_vec() + }; + let len = outputs_vec.len(); + + format!("\n transition {}(", name).add( + &(inputs + .into_iter() + .enumerate() + .map(|(index, input)| print_transition_input(&input.value_type(), index, inputs.len())) + .collect::() + + ")") + .add( + &(outputs_vec + .into_iter() + .enumerate() + .map(|(index, output)| print_transition_output(&output.value_type(), index, len)) + .collect::() + + ";\n") + .to_string(), + ), + ) + }) + .collect::(), + ); + + // Write functions + output.push_str( + &result + .closures() + .into_iter() + .map(|(name, value)| { + let (inputs, outputs) = (value.inputs(), value.outputs().clone()); + let len = outputs.len(); + + format!("\n function {}(", name).add( + &(inputs + .into_iter() + .enumerate() + .map(|(index, input)| print_function_input(&input.register_type(), index, inputs.len())) + .collect::() + + ")") + .add( + &(outputs + .into_iter() + .enumerate() + .map(|(index, output)| print_function_output(&output.register_type(), index, len)) + .collect::() + + ";\n") + .to_string(), + ), + ) + }) + .collect::(), + ); + + // Append final closing bracket + output.push_str("}\n"); + + output +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn code_gen_test() { + let aleo_prog_1 = r"import credits.aleo; + +import battleship.aleo; +import juice.aleo; + +program to_parse.aleo; + +record board_state: + owner as address.private; + hits_and_misses as u64.private; + played_tiles as u64.private; + ships as u64.private; + player_1 as address.private; + player_2 as address.private; + game_started as boolean.private; + +function new_board_state: + input r0 as u64.private; + input r1 as address.private; + cast self.caller 0u64 0u64 r0 self.caller r1 false into r2 as board_state.record; + output r2 as board_state.record; + +closure add_up: + input r0 as u8; + input r1 as u8; + add r0 r1 into r2; + output r2 as u8; + +function transfer_public_to_private: + input r0 as address.private; + input r1 as u64.public; + cast r0 r1 into r2 as credits.record; + async transfer_public_to_private self.caller r1 into r3; + output r2 as credits.record; + output r3 as credits.aleo/transfer_public_to_private.future; + +finalize transfer_public_to_private: + input r0 as address.public; + input r1 as u64.public; + get.or_use account[r0] 0u64 into r2; + sub r2 r1 into r3; + set r3 into account[r0]; +"; + let leo_stub_1: &str = r"stub to_parse.aleo { + import credits.aleo; + + import battleship.aleo; + + import juice.aleo; + + record board_state { + owner: address, + hits_and_misses: u64, + played_tiles: u64, + ships: u64, + player_1: address, + player_2: address, + game_started: bool, + } + + transition new_board_state(a1: u64, a2: address) -> board_state; + + transition transfer_public_to_private(a1: address, public a2: u64) -> credits; + + function add_up(a1: u8, a2: u8) -> u8; +} +"; + let code_gen_1 = code_gen(aleo_prog_1.to_string()); + assert_eq!(code_gen_1, leo_stub_1); + + } +} diff --git a/utils/disassembler/src/tests/credits.aleo b/utils/disassembler/src/tests/credits.aleo new file mode 100644 index 0000000000..43596f55e1 --- /dev/null +++ b/utils/disassembler/src/tests/credits.aleo @@ -0,0 +1,845 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the snarkVM library. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/**********************************************************************************************************************/ + +program credits.aleo; + +/**********************************************************************************************************************/ + +/// The `committee` mapping contains the active validator set and their corresponding stake. +mapping committee: + // The key represents the address of the validator. + key as address.public; + // The value represents the committee state of the validator. + value as committee_state.public; + +// The `committee_state` struct tracks the total stake of the validator, and whether they are open to stakers. +struct committee_state: + // The amount of microcredits bonded to the validator, by the validator and its delegators. + microcredits as u64; + // The boolean flag indicating if the validator is open to stakers. + is_open as boolean; + +/**********************************************************************************************************************/ + +// The `bonded` mapping represents the amount of microcredits that are currently bonded. +mapping bonded: + // The key represents the address of the staker, which includes the validators and their delegators. + key as address.public; + // The value represents the bond state. + value as bond_state.public; + +// The `bond_state` struct tracks the amount of microcredits that are currently bonded to the specified validator. +struct bond_state: + // The address of the validator. + validator as address; + // The amount of microcredits that are currently bonded to the specified validator. + microcredits as u64; + +/**********************************************************************************************************************/ + +// The `unbonding` mapping contains a set of stakers with their unbonding microcredits and unlock height. +mapping unbonding: + // The key represents the address of the staker, which includes the validators and their delegators. + key as address.public; + // The value represents the unbond state. + value as unbond_state.public; + +// The `unbond_state` struct tracks the microcredits that are currently unbonding, along with the unlock height. +struct unbond_state: + // The amount of microcredits that are currently unbonding. + microcredits as u64; + // The block height at which the unbonding will be complete, and can be claimed. + height as u32; + +/**********************************************************************************************************************/ + +// The `account` mapping is used to store credits publicly. +mapping account: + // The key represents the address of the owner. + key as address.public; + // The value represents the amount of public microcredits that belong to the specified owner. + value as u64.public; + +/**********************************************************************************************************************/ + +// The `credits` record is used to store credits privately. +record credits: + // The address of the owner. + owner as address.private; + // The amount of private microcredits that belong to the specified owner. + microcredits as u64.private; + +/**********************************************************************************************************************/ + +// This function allows any staker to bond their microcredits to a validator. +// The corresponding functions for 'bond_public' are 'unbond_public' and 'claim_unbond_public'. +function bond_public: + // Input the validator's address. + input r0 as address.public; + // Input the amount of microcredits to bond. + input r1 as u64.public; + + // Determine if the amount is at least one credit. + gte r1 1_000_000u64 into r2; + // Enforce the amount is at least one credit. + assert.eq r2 true; + + // Bond the specified amount of microcredits to the specified validator. + async bond_public self.caller r0 r1 into r3; + // Output the finalize future. + output r3 as credits.aleo/bond_public.future; + +finalize bond_public: + // Input the staker's address. + input r0 as address.public; + // Input the validator's address. + input r1 as address.public; + // Input the amount of microcredits to bond. + input r2 as u64.public; + + // Determine whether the caller is a validator. + is.eq r0 r1 into r3; + // If the caller is a validator, jump to the `bond_validator` logic. + branch.eq r3 true to bond_validator; + // If the caller is not a validator, jump to the `bond_delegator` logic. + branch.eq r3 false to bond_delegator; + + /******* Bond Validator *******/ + + // Starts the `bond_validator` logic. + position bond_validator; + + /* Committee */ + + // Construct the initial committee state. + // Note: We set the initial 'is_open' state to 'true'. + cast 0u64 true into r4 as committee_state; + // Retrieve the committee state of the specified validator. + get.or_use committee[r0] r4 into r5; + // Ensure that the validator is open to stakers. + assert.eq r5.is_open true; + + // Increment the stake for the specified validator. + add r5.microcredits r2 into r6; + // Construct the updated committee state. + cast r6 r5.is_open into r7 as committee_state; + + /* Bonded */ + + // Construct the initial bond state. + cast r1 0u64 into r8 as bond_state; + // Get the bond state for the caller, or default to the initial bond state. + get.or_use bonded[r0] r8 into r9; + // Enforce the validator matches in the bond state. + assert.eq r9.validator r1; + + // Increment the microcredits in the bond state. + add r9.microcredits r2 into r10; + // Determine if the amount is at least one million credits. + gte r10 1_000_000_000_000u64 into r11; + // Enforce the amount is at least one million credits. + assert.eq r11 true; + + // Construct the updated bond state. + cast r1 r10 into r12 as bond_state; + + /* Account */ + + // Get the balance of the caller. + // If the account does not exist, this finalize scope will fail. + get account[r0] into r13; + // Decrement the balance of the caller. + sub r13 r2 into r14; + + /* Writes */ + + // Update the committee state of the specified validator. + set r7 into committee[r0]; + // Update the bond state for the caller. + set r12 into bonded[r0]; + // Update the balance of the caller. + set r14 into account[r0]; + + // Ends the `bond_validator` logic. + branch.eq true true to end; + + /******* Bond Delegator *******/ + + // Starts the `bond_delegator` logic. + position bond_delegator; + + /* Committee */ + + // Check if the caller is a validator. + contains committee[r0] into r15; + // Enforce the caller is *not* a validator. + assert.eq r15 false; + + // Get the stake for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r1] into r16; + // Ensure that the validator is open to stakers. + assert.eq r16.is_open true; + + // Increment the stake for the specified validator. + add r16.microcredits r2 into r17; + // Construct the updated committee state. + cast r17 r16.is_open into r18 as committee_state; + + /* Bonded */ + + // Construct the initial bond state. + cast r1 0u64 into r19 as bond_state; + // Get the bond state for the caller, or default to the initial bond state. + get.or_use bonded[r0] r19 into r20; + // Enforce the validator matches in the bond state. + assert.eq r20.validator r1; + + // Increment the microcredits in the bond state. + add r20.microcredits r2 into r21; + // Determine if the amount is at least 10 credits. + gte r21 10_000_000u64 into r22; + // Enforce the amount is at least 10 credits. + assert.eq r22 true; + + // Construct the updated bond state. + cast r1 r21 into r23 as bond_state; + + /* Account */ + + // Get the balance of the caller. + // If the account does not exist, this finalize scope will fail. + get account[r0] into r24; + // Decrement the balance of the caller. + sub r24 r2 into r25; + + /* Writes */ + + // Update the committee state for the specified validator. + set r18 into committee[r1]; + // Update the bond state for the caller. + set r23 into bonded[r0]; + // Update the balance of the caller. + set r25 into account[r0]; + + // The terminus. + position end; + +/**********************************************************************************************************************/ + +// This function allows any staker to unbond their microcredits from a validator. +// The corresponding functions for 'unbond_public' is 'claim_unbond_public'. +function unbond_public: + // Input the amount of microcredits to unbond. + input r0 as u64.public; + + // Unbond the specified amount of microcredits to the caller. + async unbond_public self.caller r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/unbond_public.future; + +finalize unbond_public: + // Input the staker's address. + input r0 as address.public; + // Input the amount of microcredits to unbond. + input r1 as u64.public; + + // Construct the initial unbond state. + cast 0u64 0u32 into r2 as unbond_state; + // Get the unbond state for the caller, or default to the initial unbond state. + get.or_use unbonding[r0] r2 into r3; + + // Compute the height at which the unbonding will be complete, starting from the current block. + // Note: Calling unbond across multiple blocks before the unbonding is complete will reset the height each time. + add block.height 360u32 into r4; + + // Determine if the caller is a validator or delegator. + contains committee[r0] into r5; + + // If the caller is a validator, jump to the `unbond_validator` logic. + branch.eq r5 true to unbond_validator; + // If the caller is not a validator, jump to the `unbond_delegator` logic. + branch.eq r5 false to unbond_delegator; + + /******* Unbond Validator *******/ + + // Starts the `unbond_validator` logic. + position unbond_validator; + + /* Committee */ + + // Get the committee state for the specified validator. + get committee[r0] into r6; + // Decrement the stake for the specified validator. + sub r6.microcredits r1 into r7; + + /* Bonded */ + + // Get the bond state for the validator, or fail if it does not exist. + get bonded[r0] into r8; + // Ensure that the validator matches in the bond state. + assert.eq r8.validator r0; + // Decrement the microcredits in the bond state. + sub r8.microcredits r1 into r9; + + // Determine if the remaining bond is at least one million credits. + gte r9 1_000_000_000_000u64 into r10; + + // If the remaining balance is at least 1 million credits, jump to the `decrement_validator` logic. + branch.eq r10 true to decrement_validator; + // If the remaining balance is less than 1 million credits, jump to the `remove_validator` logic. + branch.eq r10 false to remove_validator; + + /*** Decrement Validator ***/ + + // Starts the `decrement_validator` logic. + position decrement_validator; + + /* Committee */ + + // Construct the updated committee state. + cast r7 r6.is_open into r11 as committee_state; + // Update the committee state for the validator. + set r11 into committee[r0]; + + /* Bonded */ + + // Construct the updated bond state. + cast r0 r9 into r12 as bond_state; + // Update the bond state for the validator. + set r12 into bonded[r0]; + + /* Unbonding */ + + // Increment the microcredits in the unbond state. + add r3.microcredits r1 into r13; + + // Construct the updated unbond state. + cast r13 r4 into r14 as unbond_state; + // Update the unbond state for the caller. + set r14 into unbonding[r0]; + + // Ends the `decrement_validator` logic. + branch.eq true true to end; + + /*** Remove Validator ***/ + + // Starts the `remove_validator` logic. + position remove_validator; + + // Ensure that the validator has no delegators. + assert.eq r6.microcredits r8.microcredits; + + /* Committee */ + + // Remove the validator from the committee. + remove committee[r0]; + + /* Bonded */ + + // Remove the bond state for the validator. + remove bonded[r0]; + + /* Unbonding */ + + // Increment the microcredits in the unbond state. + add r3.microcredits r8.microcredits into r15; + + // Construct the updated unbond state. + cast r15 r4 into r16 as unbond_state; + // Update the unbond state for the caller. + set r16 into unbonding[r0]; + + // Ends the `remove_validator` logic. + branch.eq true true to end; + + /******* Unbond Delegator *******/ + + // Starts the `unbond_delegator` logic. + position unbond_delegator; + + // Get the bond state for the caller, or fail if it does not exist. + get bonded[r0] into r17; + // Decrement the microcredits in the bond state. + sub r17.microcredits r1 into r18; + + // Determine if the remaining bond is at least 10 credits. + gte r18 10_000_000u64 into r19; + + // If the remaining balance is at least 10 credits, jump to the `decrement_delegator` logic. + branch.eq r19 true to decrement_delegator; + // If the remaining balance is less than 10 credits, jump to the `remove_delegator` logic. + branch.eq r19 false to remove_delegator; + + /*** Decrement Delegator ***/ + + // Starts the `decrement_delegator` logic. + position decrement_delegator; + + /* Committee */ + + // Get the stake for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r17.validator] into r20; + // Decrement the stake for the specified validator. + sub r20.microcredits r1 into r21; + // Construct the updated committee state. + cast r21 r20.is_open into r22 as committee_state; + // Update the stake for the specified validator. + set r22 into committee[r17.validator]; + + /* Bonded */ + + // Construct the updated bond state. + cast r17.validator r18 into r23 as bond_state; + // Update the bond state for the caller. + set r23 into bonded[r0]; + + /* Unbonding */ + + // Increment the microcredits in the unbond state. + add r3.microcredits r1 into r24; + + // Construct the updated unbond state. + cast r24 r4 into r25 as unbond_state; + // Update the unbond state for the caller. + set r25 into unbonding[r0]; + + // Ends the `decrement_delegator` logic. + branch.eq true true to end; + + /*** Remove Delegator ***/ + + // Starts the `remove_delegator` logic. + position remove_delegator; + + /* Committee */ + + // Get the stake for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r17.validator] into r26; + // Decrement the stake for the specified validator. + sub r26.microcredits r17.microcredits into r27; + // Construct the updated committee state. + cast r27 r26.is_open into r28 as committee_state; + // Update the stake for the specified validator. + set r28 into committee[r17.validator]; + + /* Bonded */ + + // Remove the caller from the bonded mapping. + remove bonded[r0]; + + /* Unbonding */ + + // Increment the microcredits in the unbond state. + add r3.microcredits r17.microcredits into r29; + + // Construct the updated unbond state. + cast r29 r4 into r30 as unbond_state; + // Update the unbond state for the caller. + set r30 into unbonding[r0]; + + // The terminus. + position end; + +/**********************************************************************************************************************/ + +// This function allows a validator to unbond any delegator that is bonded to them. +function unbond_delegator_as_validator: + // Input the delegator's address. + input r0 as address.public; + + // Unbond the delegator as the validator. + async unbond_delegator_as_validator self.caller r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/unbond_delegator_as_validator.future; + +finalize unbond_delegator_as_validator: + // Input the validator's address. + input r0 as address.public; + // Input the delegator's address. + input r1 as address.public; + + /* Start Committee */ + + // Get the committee state for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r0] into r2; + // Enforce that the validator is closed to stakers. + assert.eq r2.is_open false; + + // Check if the delegator is a validator. + contains committee[r1] into r3; + // Enforce the delegator is *not* a validator. + assert.eq r3 false; + + /* End Committee */ + + /* Start Bonded */ + + // Get the bond state for the delegator, or fail if it does not exist. + get bonded[r1] into r4; + // Enforce that the delegator is bonded to the validator. + assert.eq r4.validator r0; + + /* End Bonded */ + + /* Start Committee */ + + // Decrement the stake for the specified validator. + sub r2.microcredits r4.microcredits into r5; + // Construct the updated committee state. + cast r5 r2.is_open into r6 as committee_state; + + /* End Committee */ + + /* Start Unbond */ + + // Construct the initial unbond state. + cast 0u64 0u32 into r7 as unbond_state; + // Get the unbond state for the delegator, or default to the initial unbond state. + get.or_use unbonding[r1] r7 into r8; + + // Increment the microcredits in the unbond state. + add r8.microcredits r4.microcredits into r9; + // Compute the height at which the unbonding will be complete, starting from the current block. + // Note: Calling unbond across multiple blocks before the unbonding is complete will reset the height each time. + add block.height 360u32 into r10; + + // Construct the updated unbond state. + cast r9 r10 into r11 as unbond_state; + + /* End Unbond */ + + /* Start Writes */ + + // Update the committee state for the specified validator. + set r6 into committee[r0]; + // Remove the bond state for the delegator. + remove bonded[r1]; + // Update the unbond state for the delegator. + set r11 into unbonding[r1]; + + /* End Writes */ + +/**********************************************************************************************************************/ + +// This function allows any staker to claim their microcredits after the unbonding period. +function claim_unbond_public: + // Claim the unbonded microcredits. + async claim_unbond_public self.caller into r0; + // Output the finalize future. + output r0 as credits.aleo/claim_unbond_public.future; + +finalize claim_unbond_public: + // Input the staker's address. + input r0 as address.public; + + // Get the unbond state for the caller, or fail if it does not exist. + get unbonding[r0] into r1; + // Determine if unbonding is complete. + gte block.height r1.height into r2; + // Enforce the unbonding is complete. + assert.eq r2 true; + + // Add the unbonded amount to the stakers's public balance. + // Increments `account[r0]` by `r1`. + // If `account[r0]` does not exist, 0u64 is used. + // If `account[r0] + r2` overflows, `claim_unbond_public` is reverted. + get.or_use account[r0] 0u64 into r3; + add r1.microcredits r3 into r4; + set r4 into account[r0]; + + // Remove the unbond state for the caller. + remove unbonding[r0]; + +/**********************************************************************************************************************/ + +// This function allows a validator to set their state to be either opened or closed to stakers. +// When the validator is open to stakers, any staker (including the validator) can bond or unbond from the validator. +// When the validator is closed to stakers, all stakers can only unbond from the validator. +// +// This function serves two primary purposes: +// 1. Allow a validator to leave the committee, by closing themselves to stakers and then unbonding all of their stakers. +// 2. Allow a validator to maintain their % of stake, by closing themselves to allowing more stakers to bond to them. +function set_validator_state: + // Input the 'is_open' state. + input r0 as boolean.public; + // Set the validator to be either open or closed to stakers. + async set_validator_state self.caller r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/set_validator_state.future; + +finalize set_validator_state: + // Input the validator's address. + input r0 as address.public; + // Input the 'is_open' state. + input r1 as boolean.public; + + // Get the committee state for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r0] into r2; + + // Construct the updated committee state. + cast r2.microcredits r1 into r3 as committee_state; + // Update the committee state for the specified validator. + set r3 into committee[r0]; + +/**********************************************************************************************************************/ + +// The `transfer_public` function sends the specified amount +// from the sender's `account` to the receiver's `account`. +function transfer_public: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Transfer the credits publicly. + async transfer_public self.caller r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/transfer_public.future; + +finalize transfer_public: + // Input the sender. + input r0 as address.public; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Decrements `account[r0]` by `r2`. + // If `account[r0]` does not exist, 0u64 is used. + // If `account[r0] - r2` underflows, `transfer_public` is reverted. + get.or_use account[r0] 0u64 into r3; + sub r3 r2 into r4; + set r4 into account[r0]; + // Increments `account[r1]` by `r2`. + // If `account[r1]` does not exist, 0u64 is used. + // If `account[r1] + r2` overflows, `transfer_public` is reverted. + get.or_use account[r1] 0u64 into r5; + add r5 r2 into r6; + set r6 into account[r1]; + +/**********************************************************************************************************************/ + +// The `transfer_private` function sends the specified amount +// from the sender's record to the receiver in a record. +function transfer_private: + // Input the sender's record. + input r0 as credits.record; + // Input the receiver. + input r1 as address.private; + // Input the amount. + input r2 as u64.private; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount to be returned to the sender. + sub r0.microcredits r2 into r3; + // Construct a record for the specified receiver. + cast r1 r2 into r4 as credits.record; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r5 as credits.record; + // Output the receiver's record. + output r4 as credits.record; + // Output the sender's change record. + output r5 as credits.record; + +/**********************************************************************************************************************/ + +// The `transfer_private_to_public` function turns a specified amount +// from a record into public credits for the specified receiver. +// +// This function preserves privacy for the sender's record, however +// it publicly reveals the receiver and the amount. +function transfer_private_to_public: + // Input the sender's record. + input r0 as credits.record; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount for the sender. + sub r0.microcredits r2 into r3; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r4 as credits.record; + // Increment the amount publicly for the receiver. + async transfer_private_to_public r1 r2 into r5; + // Output the sender's change record. + output r4 as credits.record; + // Output the finalize future. + output r5 as credits.aleo/transfer_private_to_public.future; + +finalize transfer_private_to_public: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + // If `account[r0]` does not exist, 0u64 is used. + get.or_use account[r0] 0u64 into r2; + // Increments `account[r0]` by `r1`. + // If `r1 + r2` overflows, `transfer_private_to_public` is reverted. + add r1 r2 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `transfer_public_to_private` function turns a specified amount +// from the mapping `account` into a record for the specified receiver. +// +// This function publicly reveals the sender, the receiver, and the specified amount. +// However, subsequent methods using the receiver's record can preserve the receiver's privacy. +function transfer_public_to_private: + // Input the receiver. + input r0 as address.private; + // Input the amount. + input r1 as u64.public; + // Construct a record for the receiver. + cast r0 r1 into r2 as credits.record; + // Decrement the balance of the sender publicly. + async transfer_public_to_private self.caller r1 into r3; + // Output the record of the receiver. + output r2 as credits.record; + // Output the finalize future. + output r3 as credits.aleo/transfer_public_to_private.future; + +finalize transfer_public_to_private: + // Input the sender. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + // If `account[r0]` does not exist, 0u64 is used. + get.or_use account[r0] 0u64 into r2; + // Decrements `account[r0]` by `r1`. + // If `r2 - r1` underflows, `transfer_public_to_private` is reverted. + sub r2 r1 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `join` function combines two records into one. +function join: + // Input the first record. + input r0 as credits.record; + // Input the second record. + input r1 as credits.record; + // Combines the amount of the first record and the second record. + // This `add` operation is safe, and the proof will fail + // if an overflow occurs. + add r0.microcredits r1.microcredits into r2; + // Construct a record with the combined amount. + cast r0.owner r2 into r3 as credits.record; + // Output the record. + output r3 as credits.record; + +/**********************************************************************************************************************/ + +// The `split` function splits a record into two records. The given input amount will be stored in the first record, +// and the remaining amount will be stored in the second record, with the fee deducted from the remaining amount. +// If the caller executes a transaction that contains only a call to this function, then the transaction does not +// require a fee, unless the caller wishes to provide an additional fee. Transactions that contain multiple transitions +// (that include one or more calls to this function) will require a fee as per standard consensus rules. +function split: + // Input the record. + input r0 as credits.record; + // Input the amount to split. + input r1 as u64.private; + // Checks the given record has a sufficient amount to split. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. + sub r0.microcredits r1 into r2; + // Checks the given record has a sufficient fee to remove. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. + sub r2 10_000u64 into r3; + // Construct the first record. + cast r0.owner r1 into r4 as credits.record; + // Construct the second record. + cast r0.owner r3 into r5 as credits.record; + // Output the first record. + output r4 as credits.record; + // Output the second record. + output r5 as credits.record; + +/**********************************************************************************************************************/ + +// The `fee_private` function charges the specified amount from the sender's record. +function fee_private: + // Input the sender's record. + input r0 as credits.record; + // Input the amount. + input r1 as u64.public; + // Input the deployment or execution root. + input r2 as field.public; + // Ensure the amount is nonzero. + assert.neq r1 0u64; + // Ensure the deployment or execution root is nonzero. + assert.neq r2 0field; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount for the sender. + sub r0.microcredits r1 into r3; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r4 as credits.record; + // Output the sender's change record. + output r4 as credits.record; + +/**********************************************************************************************************************/ + +// The `fee_public` function charges the specified amount from the sender's account. +function fee_public: + // Input the amount. + input r0 as u64.public; + // Input the deployment or execution root. + input r1 as field.public; + // Ensure the amount is nonzero. + assert.neq r0 0u64; + // Ensure the deployment or execution root is nonzero. + assert.neq r1 0field; + // Decrement the balance of the sender publicly. + async fee_public self.caller r0 into r2; + // Output the finalize future. + output r2 as credits.aleo/fee_public.future; + +finalize fee_public: + // Input the sender's address. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + // If `account[r0]` does not exist, `fee_public` is reverted. + get account[r0] into r2; + // Decrements `account[r0]` by `r1`. + // If `r2 - r1` underflows, `fee_public` is reverted. + sub r2 r1 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// Open Questions: +// fn bond +// - if the bond is now 33% or more, close the validator. (determine how hard to impl this) + +/**********************************************************************************************************************/ diff --git a/utils/disassembler/src/tests/large_functions.aleo b/utils/disassembler/src/tests/large_functions.aleo new file mode 100644 index 0000000000..7d80e01b5f --- /dev/null +++ b/utils/disassembler/src/tests/large_functions.aleo @@ -0,0 +1,242 @@ +import credits.aleo; + +program large_functions.aleo; + +function join_3: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + call credits.aleo/join r0 r1 into r3; + call credits.aleo/join r3 r2 into r4; + output r4 as credits.aleo/credits.record; + +function join_5: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + call credits.aleo/join r0 r1 into r5; + call credits.aleo/join r2 r3 into r6; + call credits.aleo/join r5 r6 into r7; + call credits.aleo/join r7 r4 into r8; + output r8 as credits.aleo/credits.record; + +function join6: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + call credits.aleo/join r0 r1 into r6; + call credits.aleo/join r2 r3 into r7; + call credits.aleo/join r4 r5 into r8; + call credits.aleo/join r6 r7 into r9; + call credits.aleo/join r9 r8 into r10; + output r10 as credits.aleo/credits.record; + +function join7: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + input r6 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + assert.eq r5.owner r6.owner; + call credits.aleo/join r0 r1 into r7; + call credits.aleo/join r2 r3 into r8; + call credits.aleo/join r4 r5 into r9; + call credits.aleo/join r7 r8 into r10; + call credits.aleo/join r10 r9 into r11; + call credits.aleo/join r11 r6 into r12; + output r12 as credits.aleo/credits.record; + +function join8: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + input r6 as credits.aleo/credits.record; + input r7 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + assert.eq r5.owner r6.owner; + assert.eq r6.owner r7.owner; + call credits.aleo/join r0 r1 into r8; + call credits.aleo/join r2 r3 into r9; + call credits.aleo/join r4 r5 into r10; + call credits.aleo/join r6 r7 into r11; + call credits.aleo/join r8 r9 into r12; + call credits.aleo/join r11 r10 into r13; + call credits.aleo/join r12 r13 into r14; + output r14 as credits.aleo/credits.record; + +function join9: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + input r6 as credits.aleo/credits.record; + input r7 as credits.aleo/credits.record; + input r8 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + assert.eq r5.owner r6.owner; + assert.eq r6.owner r7.owner; + assert.eq r7.owner r8.owner; + call credits.aleo/join r0 r1 into r9; + call credits.aleo/join r2 r3 into r10; + call credits.aleo/join r4 r5 into r11; + call credits.aleo/join r6 r7 into r12; + call credits.aleo/join r9 r10 into r13; + call credits.aleo/join r12 r11 into r14; + call credits.aleo/join r13 r14 into r15; + call credits.aleo/join r15 r8 into r16; + output r16 as credits.aleo/credits.record; + +function join10: + input r0 as credits.aleo/credits.record; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + input r6 as credits.aleo/credits.record; + input r7 as credits.aleo/credits.record; + input r8 as credits.aleo/credits.record; + input r9 as credits.aleo/credits.record; + assert.eq r0.owner r1.owner; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + assert.eq r5.owner r6.owner; + assert.eq r6.owner r7.owner; + assert.eq r7.owner r8.owner; + assert.eq r8.owner r9.owner; + call credits.aleo/join r0 r1 into r10; + call credits.aleo/join r2 r3 into r11; + call credits.aleo/join r4 r5 into r12; + call credits.aleo/join r6 r7 into r13; + call credits.aleo/join r8 r9 into r14; + call credits.aleo/join r10 r11 into r15; + call credits.aleo/join r13 r12 into r16; + call credits.aleo/join r15 r16 into r17; + call credits.aleo/join r17 r14 into r18; + output r18 as credits.aleo/credits.record; + +function transfer_3: + input r0 as address.private; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + call credits.aleo/transfer_private r1 r0 r1.microcredits into r4 r5; + call credits.aleo/transfer_private r2 r0 r2.microcredits into r6 r7; + call credits.aleo/transfer_private r3 r0 r3.microcredits into r8 r9; + output r4 as credits.aleo/credits.record; + output r5 as credits.aleo/credits.record; + output r6 as credits.aleo/credits.record; + output r7 as credits.aleo/credits.record; + output r8 as credits.aleo/credits.record; + output r9 as credits.aleo/credits.record; + +function transfer_5: + input r0 as address.private; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + call credits.aleo/transfer_private r1 r0 r1.microcredits into r6 r7; + call credits.aleo/transfer_private r2 r0 r2.microcredits into r8 r9; + call credits.aleo/transfer_private r3 r0 r3.microcredits into r10 r11; + call credits.aleo/transfer_private r4 r0 r4.microcredits into r12 r13; + call credits.aleo/transfer_private r5 r0 r5.microcredits into r14 r15; + output r6 as credits.aleo/credits.record; + output r7 as credits.aleo/credits.record; + output r8 as credits.aleo/credits.record; + output r9 as credits.aleo/credits.record; + output r10 as credits.aleo/credits.record; + output r11 as credits.aleo/credits.record; + output r12 as credits.aleo/credits.record; + output r13 as credits.aleo/credits.record; + output r14 as credits.aleo/credits.record; + output r15 as credits.aleo/credits.record; + +function split_3: + input r0 as u64.private; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + call credits.aleo/split r1 r0 into r4 r5; + call credits.aleo/split r2 r0 into r6 r7; + call credits.aleo/split r3 r0 into r8 r9; + output r4 as credits.aleo/credits.record; + output r5 as credits.aleo/credits.record; + output r6 as credits.aleo/credits.record; + output r7 as credits.aleo/credits.record; + output r8 as credits.aleo/credits.record; + output r9 as credits.aleo/credits.record; + +function split_5: + input r0 as u64.private; + input r1 as credits.aleo/credits.record; + input r2 as credits.aleo/credits.record; + input r3 as credits.aleo/credits.record; + input r4 as credits.aleo/credits.record; + input r5 as credits.aleo/credits.record; + assert.eq r1.owner r2.owner; + assert.eq r2.owner r3.owner; + assert.eq r3.owner r4.owner; + assert.eq r4.owner r5.owner; + call credits.aleo/split r1 r0 into r6 r7; + call credits.aleo/split r2 r0 into r8 r9; + call credits.aleo/split r3 r0 into r10 r11; + call credits.aleo/split r4 r0 into r12 r13; + call credits.aleo/split r5 r0 into r14 r15; + output r6 as credits.aleo/credits.record; + output r7 as credits.aleo/credits.record; + output r8 as credits.aleo/credits.record; + output r9 as credits.aleo/credits.record; + output r10 as credits.aleo/credits.record; + output r11 as credits.aleo/credits.record; + output r12 as credits.aleo/credits.record; + output r13 as credits.aleo/credits.record; + output r14 as credits.aleo/credits.record; + output r15 as credits.aleo/credits.record;