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, cost_in_microcredits_v2, traits::*};
use console::{
account::{Address, PrivateKey},
network::prelude::*,
program::{
Argument,
Entry,
EntryType,
FinalizeType,
Future,
Identifier,
Literal,
Locator,
Owner as RecordOwner,
Plaintext,
PlaintextType,
ProgramID,
Record,
RecordType,
RegisterType,
Request,
Response,
U8,
U16,
Value,
ValueType,
},
types::{Field, Group},
};
use ledger_block::{Deployment, Transaction, Transition};
use synthesizer_program::{CallOperator, Closure, Function, Instruction, Operand, Program, traits::*};
use 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 std::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>)>>>;
#[derive(Clone)]
pub enum CallStack<N: Network> {
Authorize(Vec<Request<N>>, PrivateKey<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>>>),
PackageRun(Vec<Request<N>>, PrivateKey<N>, Assignments<N>),
}
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>>>) -> Result<Self> {
Ok(CallStack::Execute(authorization, trace))
}
}
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::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) => {
CallStack::Execute(authorization.replicate(), Arc::new(RwLock::new(trace.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::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::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(&mut self) -> Result<Request<N>> {
match self {
CallStack::Authorize(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>>>>>,
register_types: IndexMap<Identifier<N>, RegisterTypes<N>>,
finalize_types: 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_edition: U16<N>,
}
impl<N: Network> Stack<N> {
#[inline]
pub fn new(process: &Process<N>, program: &Program<N>) -> Result<Self> {
let program_id = program.id();
ensure!(!program.functions().is_empty(), "No functions present in the deployment for program '{program_id}'");
if let Ok(existing_stack) = process.get_stack(program_id) {
ensure!(program_id != &ProgramID::from_str("credits.aleo")?, "Cannot re-initialize the 'credits.aleo'.");
ensure!(
existing_stack.program() == program,
"Program '{program_id}' already exists with different contents."
);
}
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");
Stack::initialize(process, program)
}
}
impl<N: Network> StackKeys<N> for Stack<N> {
#[inline]
fn contains_proving_key(&self, function_name: &Identifier<N>) -> bool {
self.proving_keys.read().contains_key(function_name)
}
#[inline]
fn get_proving_key(&self, function_name: &Identifier<N>) -> Result<ProvingKey<N>> {
self.try_insert_credits_function_proving_key(function_name)?;
match self.proving_keys.read().get(function_name) {
Some(pk) => Ok(pk.clone()),
None => bail!("Proving key not found for: {}/{}", self.program.id(), function_name),
}
}
#[inline]
fn insert_proving_key(&self, function_name: &Identifier<N>, proving_key: ProvingKey<N>) -> Result<()> {
ensure!(
self.program.contains_function(function_name),
"Function '{function_name}' does not exist in program '{}'.",
self.program.id()
);
self.proving_keys.write().insert(*function_name, proving_key);
Ok(())
}
#[inline]
fn remove_proving_key(&self, function_name: &Identifier<N>) {
self.proving_keys.write().shift_remove(function_name);
}
#[inline]
fn contains_verifying_key(&self, function_name: &Identifier<N>) -> bool {
self.verifying_keys.read().contains_key(function_name)
}
#[inline]
fn get_verifying_key(&self, function_name: &Identifier<N>) -> Result<VerifyingKey<N>> {
match self.verifying_keys.read().get(function_name) {
Some(vk) => Ok(vk.clone()),
None => bail!("Verifying key not found for: {}/{}", self.program.id(), function_name),
}
}
#[inline]
fn insert_verifying_key(&self, function_name: &Identifier<N>, verifying_key: VerifyingKey<N>) -> Result<()> {
ensure!(
self.program.contains_function(function_name),
"Function '{function_name}' does not exist in program '{}'.",
self.program.id()
);
self.verifying_keys.write().insert(*function_name, verifying_key);
Ok(())
}
#[inline]
fn remove_verifying_key(&self, function_name: &Identifier<N>) {
self.verifying_keys.write().shift_remove(function_name);
}
}
impl<N: Network> StackProgram<N> for Stack<N> {
#[inline]
fn program(&self) -> &Program<N> {
&self.program
}
#[inline]
fn program_id(&self) -> &ProgramID<N> {
self.program.id()
}
#[inline]
fn program_address(&self) -> &Address<N> {
&self.program_address
}
#[inline]
fn program_edition(&self) -> U16<N> {
self.program_edition
}
#[inline]
fn get_external_stack(&self, program_id: &ProgramID<N>) -> Result<Arc<Stack<N>>> {
ensure!(
program_id != self.program.id(),
"Attempted to get the main program '{program_id}' as an external program."
);
ensure!(self.program.contains_import(program_id), "External program '{program_id}' is not imported.");
self.stacks
.upgrade()
.ok_or_else(|| anyhow!("Process-level stack map does not exist"))?
.read()
.get(program_id)
.cloned()
.ok_or_else(|| anyhow!("External stack for '{program_id}' does not exist"))
}
#[inline]
fn get_function(&self, function_name: &Identifier<N>) -> Result<Function<N>> {
self.program.get_function(function_name)
}
#[inline]
fn get_function_ref(&self, function_name: &Identifier<N>) -> Result<&Function<N>> {
self.program.get_function_ref(function_name)
}
#[inline]
fn get_number_of_calls(&self, function_name: &Identifier<N>) -> Result<usize> {
let mut num_calls = 1;
let mut queue = vec![(StackRef::Internal(self), *function_name)];
while let Some((stack_ref, function_name)) = queue.pop() {
ensure!(
num_calls < Transaction::<N>::MAX_TRANSITIONS,
"Number of calls must be less than '{}'",
Transaction::<N>::MAX_TRANSITIONS
);
for instruction in stack_ref.get_function_ref(&function_name)?.instructions() {
if let Instruction::Call(call) = instruction {
if call.is_function_call(&*stack_ref)? {
num_calls += 1;
match call.operator() {
CallOperator::Locator(locator) => {
queue.push((
StackRef::External(stack_ref.get_external_stack(locator.program_id())?),
*locator.resource(),
));
}
CallOperator::Resource(resource) => {
queue.push((stack_ref.clone(), *resource));
}
}
}
}
}
}
Ok(num_calls)
}
fn sample_value<R: Rng + CryptoRng>(
&self,
burner_address: &Address<N>,
value_type: &ValueType<N>,
rng: &mut R,
) -> Result<Value<N>> {
match value_type {
ValueType::Constant(plaintext_type)
| ValueType::Public(plaintext_type)
| ValueType::Private(plaintext_type) => Ok(Value::Plaintext(self.sample_plaintext(plaintext_type, rng)?)),
ValueType::Record(record_name) => {
Ok(Value::Record(self.sample_record(burner_address, record_name, Group::rand(rng), rng)?))
}
ValueType::ExternalRecord(locator) => {
let stack = self.get_external_stack(locator.program_id())?;
Ok(Value::Record(stack.sample_record(burner_address, locator.resource(), Group::rand(rng), rng)?))
}
ValueType::Future(locator) => Ok(Value::Future(self.sample_future(locator, rng)?)),
}
}
fn sample_record<R: Rng + CryptoRng>(
&self,
burner_address: &Address<N>,
record_name: &Identifier<N>,
nonce: Group<N>,
rng: &mut R,
) -> Result<Record<N, Plaintext<N>>> {
let record = self.sample_record_internal(burner_address, record_name, nonce, 0, rng)?;
self.matches_record(&record, record_name)?;
Ok(record)
}
fn sample_record_using_tvk<R: Rng + CryptoRng>(
&self,
burner_address: &Address<N>,
record_name: &Identifier<N>,
tvk: Field<N>,
index: Field<N>,
rng: &mut R,
) -> Result<Record<N, Plaintext<N>>> {
let randomizer = N::hash_to_scalar_psd2(&[tvk, index])?;
let record_nonce = N::g_scalar_multiply(&randomizer);
self.sample_record(burner_address, record_name, record_nonce, rng)
}
}
impl<N: Network> StackProgramTypes<N> for Stack<N> {
#[inline]
fn get_register_types(&self, name: &Identifier<N>) -> Result<&RegisterTypes<N>> {
self.register_types.get(name).ok_or_else(|| anyhow!("Register types for '{name}' do not exist"))
}
#[inline]
fn get_finalize_types(&self, name: &Identifier<N>) -> Result<&FinalizeTypes<N>> {
self.finalize_types.get(name).ok_or_else(|| anyhow!("Finalize types for '{name}' do not exist"))
}
}
impl<N: Network> Stack<N> {
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 == other.register_types
&& self.finalize_types == other.finalize_types
}
}
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,
}
}
}