mod authorization;
pub use authorization::*;
mod call;
pub use call::*;
mod finalize_registers;
pub use finalize_registers::*;
mod finalize_types;
pub use finalize_types::*;
mod register_types;
pub use register_types::*;
mod registers;
pub use registers::*;
mod authorize;
mod deploy;
mod evaluate;
mod execute;
mod helpers;
use crate::{CallMetrics, Process, Trace, TranslationAssignment, compute_console_dynamic_or_external_record_id};
use console::{
account::{Address, PrivateKey},
network::prelude::*,
program::{
Argument,
DynamicFuture,
DynamicRecord,
Entry,
EntryType,
FinalizeType,
Future,
Identifier,
Literal,
Locator,
Owner as RecordOwner,
Plaintext,
PlaintextType,
ProgramID,
Record,
RecordType,
RegisterType,
Request,
Response,
ToFields,
U8,
U16,
Value,
ValueType,
},
types::{Field, Group},
};
use snarkvm_ledger_block::{Deployment, Transaction, Transition};
use snarkvm_synthesizer_error::*;
use snarkvm_synthesizer_program::{
CallOperator,
Closure,
FinalizeGlobalState,
FinalizeStoreTrait,
Function,
Instruction,
Operand,
Program,
RegistersCircuit,
RegistersSigner,
RegistersTrait,
StackTrait,
};
use snarkvm_synthesizer_snark::{Certificate, ProvingKey, UniversalSRS, VerifyingKey};
use aleo_std::prelude::{finish, lap, timer};
use indexmap::IndexMap;
#[cfg(feature = "locktick")]
use locktick::parking_lot::RwLock;
#[cfg(not(feature = "locktick"))]
use parking_lot::RwLock;
use rand::{CryptoRng, RngExt as Rng, SeedableRng, rngs::StdRng};
use std::{
cell::OnceCell,
sync::{Arc, Weak},
};
#[cfg(not(feature = "serial"))]
use rayon::prelude::*;
pub type Assignments<N> = Arc<RwLock<Vec<(circuit::Assignment<<N as Environment>::Field>, CallMetrics<N>)>>>;
pub type Translations<N> = Arc<RwLock<Vec<Vec<(TranslationAssignment<N>, ProvingKey<N>)>>>>;
#[derive(Clone, Debug)]
pub enum CallStack<N: Network> {
Authorize(Vec<Request<N>>, Option<PrivateKey<N>>, Authorization<N>),
AuthorizeMocked(Vec<Request<N>>, Address<N>, Authorization<N>),
Synthesize(Vec<Request<N>>, PrivateKey<N>, Authorization<N>),
CheckDeployment(Vec<Request<N>>, PrivateKey<N>, Assignments<N>, Option<u64>, Option<u64>),
Evaluate(Authorization<N>),
Execute(Authorization<N>, Arc<RwLock<Trace<N>>>, Translations<N>),
PackageRun(Vec<Request<N>>, PrivateKey<N>, Assignments<N>),
}
impl<N: Network> CallStack<N> {
fn type_as_string(&self) -> String {
match self {
CallStack::Authorize(..) => "Authorize".to_string(),
CallStack::AuthorizeMocked(..) => "Mock".to_string(),
CallStack::Synthesize(..) => "Synthesize".to_string(),
CallStack::CheckDeployment(..) => "CheckDeployment".to_string(),
CallStack::Evaluate(..) => "Evaluate".to_string(),
CallStack::Execute(..) => "Execute".to_string(),
CallStack::PackageRun(..) => "PackageRun".to_string(),
}
}
}
impl<N: Network> CallStack<N> {
pub fn evaluate(authorization: Authorization<N>) -> Result<Self> {
Ok(CallStack::Evaluate(authorization))
}
pub fn execute(
authorization: Authorization<N>,
trace: Arc<RwLock<Trace<N>>>,
translations: Translations<N>,
) -> Result<Self> {
Ok(CallStack::Execute(authorization, trace, translations))
}
}
impl<N: Network> CallStack<N> {
pub fn replicate(&self) -> Self {
match self {
CallStack::Authorize(requests, private_key, authorization) => {
CallStack::Authorize(requests.clone(), *private_key, authorization.replicate())
}
CallStack::AuthorizeMocked(requests, address, authorization) => {
CallStack::AuthorizeMocked(requests.clone(), *address, authorization.replicate())
}
CallStack::Synthesize(requests, private_key, authorization) => {
CallStack::Synthesize(requests.clone(), *private_key, authorization.replicate())
}
CallStack::CheckDeployment(requests, private_key, assignments, constraint_limit, variable_limit) => {
CallStack::CheckDeployment(
requests.clone(),
*private_key,
Arc::new(RwLock::new(assignments.read().clone())),
*constraint_limit,
*variable_limit,
)
}
CallStack::Evaluate(authorization) => CallStack::Evaluate(authorization.replicate()),
CallStack::Execute(authorization, trace, translations) => CallStack::Execute(
authorization.replicate(),
Arc::new(RwLock::new(trace.read().clone())),
Arc::new(RwLock::new(translations.read().clone())),
),
CallStack::PackageRun(requests, private_key, assignments) => {
CallStack::PackageRun(requests.clone(), *private_key, Arc::new(RwLock::new(assignments.read().clone())))
}
}
}
pub fn push(&mut self, request: Request<N>) -> Result<()> {
match self {
CallStack::Authorize(requests, ..)
| CallStack::AuthorizeMocked(requests, ..)
| CallStack::Synthesize(requests, ..)
| CallStack::CheckDeployment(requests, ..)
| CallStack::PackageRun(requests, ..) => {
ensure!(
requests.len() < Transaction::<N>::MAX_TRANSITIONS,
"The number of requests in the authorization must be less than '{}'.",
Transaction::<N>::MAX_TRANSITIONS
);
requests.push(request)
}
CallStack::Evaluate(authorization) => authorization.push(request)?,
CallStack::Execute(authorization, ..) => authorization.push(request)?,
}
Ok(())
}
pub fn pop(&mut self) -> Result<Request<N>> {
match self {
CallStack::Authorize(requests, ..)
| CallStack::AuthorizeMocked(requests, ..)
| CallStack::Synthesize(requests, ..)
| CallStack::CheckDeployment(requests, ..)
| CallStack::PackageRun(requests, ..) => {
requests.pop().ok_or_else(|| anyhow!("No more requests on the stack"))
}
CallStack::Evaluate(authorization) => authorization.next(),
CallStack::Execute(authorization, ..) => authorization.next(),
}
}
pub fn peek(&self) -> Result<Request<N>> {
match self {
CallStack::Authorize(requests, ..)
| CallStack::AuthorizeMocked(requests, ..)
| CallStack::Synthesize(requests, ..)
| CallStack::CheckDeployment(requests, ..)
| CallStack::PackageRun(requests, ..) => {
requests.last().cloned().ok_or_else(|| anyhow!("No more requests on the stack"))
}
CallStack::Evaluate(authorization) => authorization.peek_next(),
CallStack::Execute(authorization, ..) => authorization.peek_next(),
}
}
}
#[derive(Clone)]
pub struct Stack<N: Network> {
program: Program<N>,
stacks: Weak<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>,
constructor_types: Arc<RwLock<Option<FinalizeTypes<N>>>>,
register_types: Arc<RwLock<IndexMap<Identifier<N>, RegisterTypes<N>>>>,
finalize_types: Arc<RwLock<IndexMap<Identifier<N>, FinalizeTypes<N>>>>,
view_types: Arc<RwLock<IndexMap<Identifier<N>, FinalizeTypes<N>>>>,
universal_srs: UniversalSRS<N>,
proving_keys: Arc<RwLock<IndexMap<Identifier<N>, ProvingKey<N>>>>,
verifying_keys: Arc<RwLock<IndexMap<Identifier<N>, VerifyingKey<N>>>>,
program_address: Address<N>,
program_checksum: [U8<N>; 32],
program_edition: U16<N>,
program_amendment_count: u64,
program_owner: Option<Address<N>>,
}
impl<N: Network> Stack<N> {
pub fn new(process: &Process<N>, program: &Program<N>) -> Result<Self> {
let program_id = program.id();
check_program_is_well_formed(program)?;
if let Ok(existing_stack) = process.get_stack(program_id) {
ensure!(program_id != &ProgramID::from_str("credits.aleo")?, "Cannot re-initialize the 'credits.aleo'.");
let existing_program = existing_stack.program();
match existing_program.contains_constructor() {
false => ensure!(
existing_stack.program() == program,
"Program '{program_id}' already exists with different contents."
),
true => Self::check_upgrade_is_valid(existing_program, program)?,
}
}
Stack::initialize(process, program)
}
pub fn new_raw(process: &Process<N>, program: &Program<N>, edition: u16) -> Result<Self> {
check_program_is_well_formed(program)?;
Stack::create_raw(process, program, edition)
}
pub fn initialize_and_check(&self, process: &Process<N>) -> Result<()> {
let mut constructor_types = self.constructor_types.write();
let mut register_types = self.register_types.write();
let mut finalize_types = self.finalize_types.write();
let mut view_types = self.view_types.write();
constructor_types.take();
register_types.clear();
finalize_types.clear();
view_types.clear();
for import in self.program.imports().keys() {
ensure!(import != self.program.id(), "Program cannot import itself");
if !process.contains_program(import) {
bail!("Cannot add program '{}' because its import '{import}' must be added first", self.program.id())
}
}
if let Some(constructor) = self.program.constructor() {
let types = FinalizeTypes::from_constructor(self, constructor)?;
constructor_types.replace(types);
}
for closure in self.program.closures().values() {
let name = closure.name();
ensure!(!register_types.contains_key(name), "Closure '{name}' already exists");
let types = RegisterTypes::from_closure(self, closure)?;
register_types.insert(*name, types);
}
for function in self.program.functions().values() {
let name = function.name();
ensure!(!register_types.contains_key(name), "Function '{name}' already exists");
let types = RegisterTypes::from_function(self, function)?;
register_types.insert(*name, types);
if let Some(finalize) = function.finalize_logic() {
let types = FinalizeTypes::from_finalize(self, finalize)?;
finalize_types.insert(*name, types);
}
}
for view in self.program.views().values() {
let name = view.name();
ensure!(!view_types.contains_key(name), "View '{name}' already exists");
let types = FinalizeTypes::from_view(self, view)?;
view_types.insert(*name, types);
}
drop(constructor_types);
drop(register_types);
drop(finalize_types);
drop(view_types);
for function in self.program.functions().values() {
self.get_minimum_number_of_calls(function.name())?;
}
Ok(())
}
#[inline]
pub fn get_constructor_types(&self) -> Result<FinalizeTypes<N>> {
match self.constructor_types.read().as_ref() {
Some(constructor_types) => Ok(constructor_types.clone()),
None => bail!("Constructor types do not exist"),
}
}
#[inline]
pub fn get_register_types(&self, name: &Identifier<N>) -> Result<RegisterTypes<N>> {
match self.register_types.read().get(name) {
Some(register_types) => Ok(register_types.clone()),
None => bail!("Register types for '{name}' do not exist"),
}
}
#[inline]
pub fn get_finalize_types(&self, name: &Identifier<N>) -> Result<FinalizeTypes<N>> {
match self.finalize_types.read().get(name) {
Some(finalize_types) => Ok(finalize_types.clone()),
None => bail!("Finalize types for '{name}' do not exist"),
}
}
#[inline]
pub fn get_view_types(&self, name: &Identifier<N>) -> Result<FinalizeTypes<N>> {
match self.view_types.read().get(name) {
Some(view_types) => Ok(view_types.clone()),
None => bail!("View types for '{name}' do not exist"),
}
}
fn try_insert_credits_function_proving_key(&self, function_name: &Identifier<N>) -> Result<()> {
if self.program_id() == &ProgramID::from_str("credits.aleo")?
&& !self.proving_keys.read().contains_key(function_name)
{
let proving_key = N::get_credits_proving_key(function_name.to_string())?;
self.insert_proving_key(function_name, ProvingKey::new(proving_key.clone()))?;
}
Ok(())
}
}
impl<N: Network> PartialEq for Stack<N> {
fn eq(&self, other: &Self) -> bool {
self.program == other.program
&& *self.register_types.read() == *other.register_types.read()
&& *self.finalize_types.read() == *other.finalize_types.read()
}
}
impl<N: Network> Eq for Stack<N> {}
#[derive(Clone)]
pub(crate) enum StackRef<'a, N: Network> {
Internal(&'a Stack<N>),
External(Arc<Stack<N>>),
}
impl<N: Network> Deref for StackRef<'_, N> {
type Target = Stack<N>;
fn deref(&self) -> &Self::Target {
match self {
StackRef::Internal(stack) => stack,
StackRef::External(stack) => stack,
}
}
}
fn check_program_is_well_formed<N: Network>(program: &Program<N>) -> Result<()> {
ensure!(!program.functions().is_empty(), "No functions present in the deployment for program '{}'", program.id());
let program_bytes = program.to_bytes_le()?;
ensure!(program == &Program::from_bytes_le(&program_bytes)?, "Program byte serialization failed");
let program_string = program.to_string();
ensure!(program == &Program::from_str(&program_string)?, "Program string serialization failed");
Ok(())
}