use std::sync::Arc;
use crate::{FinalizeGlobalState, Function, Operand, Program};
use console::{
account::Group,
network::Network,
prelude::{Result, bail},
program::{
Future,
Identifier,
Literal,
Locator,
Plaintext,
PlaintextType,
ProgramID,
Record,
Register,
RegisterType,
Request,
StructType,
Value,
ValueType,
},
types::{Address, Field, U8, U16},
};
use rand::{CryptoRng, Rng};
use snarkvm_synthesizer_snark::{ProvingKey, VerifyingKey};
pub trait StackTrait<N: Network> {
fn contains_proving_key(&self, function_or_record_name: &Identifier<N>) -> bool;
fn get_proving_key(&self, function_or_record_name: &Identifier<N>) -> Result<ProvingKey<N>>;
fn insert_proving_key(&self, function_or_record_name: &Identifier<N>, proving_key: ProvingKey<N>) -> Result<()>;
fn remove_proving_key(&self, function_or_record_name: &Identifier<N>);
fn contains_verifying_key(&self, function_or_record_name: &Identifier<N>) -> bool;
fn get_verifying_key(&self, function_or_record_name: &Identifier<N>) -> Result<VerifyingKey<N>>;
fn insert_verifying_key(
&self,
function_or_record_name: &Identifier<N>,
verifying_key: VerifyingKey<N>,
) -> Result<()>;
fn remove_verifying_key(&self, function_or_record_name: &Identifier<N>);
fn matches_value_type(&self, value: &Value<N>, value_type: &ValueType<N>) -> Result<()>;
fn matches_register_type(&self, stack_value: &Value<N>, register_type: &RegisterType<N>) -> Result<()>;
fn matches_external_record(&self, record: &Record<N, Plaintext<N>>, locator: &Locator<N>) -> Result<()>;
fn matches_record(&self, record: &Record<N, Plaintext<N>>, record_name: &Identifier<N>) -> Result<()>;
fn matches_plaintext(&self, plaintext: &Plaintext<N>, plaintext_type: &PlaintextType<N>) -> Result<()>;
fn matches_future(&self, future: &Future<N>, locator: &Locator<N>) -> Result<()>;
fn program(&self) -> &Program<N>;
fn program_id(&self) -> &ProgramID<N>;
fn program_address(&self) -> &Address<N>;
fn program_checksum(&self) -> &[U8<N>; 32];
fn program_checksum_as_field(&self) -> Result<Field<N>>;
fn program_edition(&self) -> U16<N>;
fn program_owner(&self) -> &Option<Address<N>>;
fn set_program_owner(&mut self, program_owner: Option<Address<N>>);
fn get_external_stack(&self, program_id: &ProgramID<N>) -> Result<Arc<Self>>;
fn get_stack_global(&self, program_id: &ProgramID<N>) -> Result<Arc<Self>>;
fn get_function(&self, function_name: &Identifier<N>) -> Result<Function<N>>;
fn get_function_ref(&self, function_name: &Identifier<N>) -> Result<&Function<N>>;
fn get_minimum_number_of_calls(&self, function_name: &Identifier<N>) -> Result<usize>;
fn contains_dynamic_call(&self, function_name: &Identifier<N>) -> Result<bool>;
fn sample_value<R: Rng + CryptoRng>(
&self,
burner_address: &Address<N>,
value_type: &RegisterType<N>,
rng: &mut R,
) -> Result<Value<N>>;
fn sample_record<R: Rng + CryptoRng>(
&self,
burner_address: &Address<N>,
record_name: &Identifier<N>,
record_nonce: Group<N>,
rng: &mut R,
) -> Result<Record<N, Plaintext<N>>>;
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>>>;
}
pub fn register_types_equivalent<N: Network>(
stack0: &impl StackTrait<N>,
type0: &RegisterType<N>,
stack1: &impl StackTrait<N>,
type1: &RegisterType<N>,
) -> Result<bool> {
use RegisterType::*;
if let (Plaintext(plaintext0), Plaintext(plaintext1)) = (type0, type1) {
types_equivalent(stack0, plaintext0, stack1, plaintext1)
} else {
Ok(type0 == type1)
}
}
pub fn types_equivalent<N: Network>(
stack0: &impl StackTrait<N>,
type0: &PlaintextType<N>,
stack1: &impl StackTrait<N>,
type1: &PlaintextType<N>,
) -> Result<bool> {
use PlaintextType::*;
let struct_compare = |stack0, st0: &StructType<N>, stack1, st1: &StructType<N>| -> Result<bool> {
if st0.members().len() != st1.members().len() {
return Ok(false);
}
for ((name0, type0), (name1, type1)) in st0.members().iter().zip(st1.members()) {
if name0 != name1 || !types_equivalent(stack0, type0, stack1, type1)? {
return Ok(false);
}
}
Ok(true)
};
match (type0, type1) {
(Array(array0), Array(array1)) => Ok(array0.length() == array1.length()
&& types_equivalent(stack0, array0.next_element_type(), stack1, array1.next_element_type())?),
(Literal(lit0), Literal(lit1)) => Ok(lit0 == lit1),
(Struct(id0), Struct(id1)) => {
if id0 != id1 {
return Ok(false);
}
let struct_type0 = stack0.program().get_struct(id0)?;
let struct_type1 = stack1.program().get_struct(id1)?;
struct_compare(stack0, struct_type0, stack1, struct_type1)
}
(ExternalStruct(loc0), ExternalStruct(loc1)) => {
if loc0.resource() != loc1.resource() {
return Ok(false);
}
let external_stack0 = stack0.get_external_stack(loc0.program_id())?;
let struct_type0 = external_stack0.program().get_struct(loc0.resource())?;
let external_stack1 = stack1.get_external_stack(loc1.program_id())?;
let struct_type1 = external_stack1.program().get_struct(loc1.resource())?;
struct_compare(&*external_stack0, struct_type0, &*external_stack1, struct_type1)
}
(ExternalStruct(loc), Struct(id)) => {
if loc.resource() != id {
return Ok(false);
}
let external_stack = stack0.get_external_stack(loc.program_id())?;
let struct_type0 = external_stack.program().get_struct(loc.resource())?;
let struct_type1 = stack1.program().get_struct(id)?;
struct_compare(&*external_stack, struct_type0, stack1, struct_type1)
}
(Struct(id), ExternalStruct(loc)) => {
if id != loc.resource() {
return Ok(false);
}
let struct_type0 = stack0.program().get_struct(id)?;
let external_stack = stack1.get_external_stack(loc.program_id())?;
let struct_type1 = external_stack.program().get_struct(loc.resource())?;
struct_compare(stack0, struct_type0, &*external_stack, struct_type1)
}
_ => Ok(false),
}
}
pub trait FinalizeRegistersState<N: Network>: RegistersTrait<N> {
fn state(&self) -> &FinalizeGlobalState;
fn transition_id(&self) -> &N::TransitionID;
fn function_name(&self) -> &Identifier<N>;
fn nonce(&self) -> u64;
}
pub trait RegistersSigner<N: Network>: RegistersTrait<N> {
fn signer(&self) -> Result<Address<N>>;
fn set_signer(&mut self, signer: Address<N>);
fn root_tvk(&self) -> Result<Field<N>>;
fn set_root_tvk(&mut self, root_tvk: Field<N>);
fn caller(&self) -> Result<Address<N>>;
fn set_caller(&mut self, caller: Address<N>);
fn tvk(&self) -> Result<Field<N>>;
fn set_tvk(&mut self, tvk: Field<N>);
fn request(&self) -> Result<&Request<N>>;
fn set_request(&mut self, request: Request<N>);
}
pub trait RegistersTrait<N: Network> {
fn load(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Value<N>>;
fn load_literal(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Literal<N>> {
match self.load(stack, operand)? {
Value::Plaintext(Plaintext::Literal(literal, ..)) => Ok(literal),
Value::Plaintext(Plaintext::Struct(..))
| Value::Plaintext(Plaintext::Array(..))
| Value::Record(..)
| Value::Future(..)
| Value::DynamicRecord(..)
| Value::DynamicFuture(..) => {
bail!("Operand must be a literal")
}
}
}
fn load_plaintext(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Plaintext<N>> {
match self.load(stack, operand)? {
Value::Plaintext(plaintext) => Ok(plaintext),
Value::Record(..) | Value::Future(..) | Value::DynamicRecord(..) | Value::DynamicFuture(..) => {
bail!("Operand must be a plaintext")
}
}
}
fn store(&mut self, stack: &impl StackTrait<N>, register: &Register<N>, stack_value: Value<N>) -> Result<()>;
fn store_literal(&mut self, stack: &impl StackTrait<N>, register: &Register<N>, literal: Literal<N>) -> Result<()> {
self.store(stack, register, Value::Plaintext(Plaintext::from(literal)))
}
}
pub trait RegistersCircuit<N: Network, A: circuit::Aleo<Network = N>> {
fn signer_circuit(&self) -> Result<circuit::Address<A>>;
fn set_signer_circuit(&mut self, signer_circuit: circuit::Address<A>);
fn root_tvk_circuit(&self) -> Result<circuit::Field<A>>;
fn set_root_tvk_circuit(&mut self, root_tvk_circuit: circuit::Field<A>);
fn caller_circuit(&self) -> Result<circuit::Address<A>>;
fn set_caller_circuit(&mut self, caller_circuit: circuit::Address<A>);
fn tvk_circuit(&self) -> Result<circuit::Field<A>>;
fn set_tvk_circuit(&mut self, tvk_circuit: circuit::Field<A>);
fn load_circuit(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<circuit::Value<A>>;
fn load_literal_circuit(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<circuit::Literal<A>> {
match self.load_circuit(stack, operand)? {
circuit::Value::Plaintext(circuit::Plaintext::Literal(literal, ..)) => Ok(literal),
circuit::Value::Plaintext(circuit::Plaintext::Struct(..))
| circuit::Value::Plaintext(circuit::Plaintext::Array(..))
| circuit::Value::Record(..)
| circuit::Value::Future(..)
| circuit::Value::DynamicRecord(..)
| circuit::Value::DynamicFuture(..) => bail!("Operand must be a literal"),
}
}
fn load_plaintext_circuit(
&self,
stack: &impl StackTrait<N>,
operand: &Operand<N>,
) -> Result<circuit::Plaintext<A>> {
match self.load_circuit(stack, operand)? {
circuit::Value::Plaintext(plaintext) => Ok(plaintext),
circuit::Value::Record(..)
| circuit::Value::Future(..)
| circuit::Value::DynamicRecord(..)
| circuit::Value::DynamicFuture(..) => bail!("Operand must be a plaintext"),
}
}
fn store_circuit(
&mut self,
stack: &impl StackTrait<N>,
register: &Register<N>,
stack_value: circuit::Value<A>,
) -> Result<()>;
fn store_literal_circuit(
&mut self,
stack: &impl StackTrait<N>,
register: &Register<N>,
literal: circuit::Literal<A>,
) -> Result<()> {
self.store_circuit(stack, register, circuit::Value::Plaintext(circuit::Plaintext::from(literal)))
}
}