use super::*;
impl<N: Network> StackTrait<N> for Stack<N> {
fn matches_value_type(&self, value: &Value<N>, value_type: &ValueType<N>) -> Result<()> {
match (value, value_type) {
(Value::Plaintext(plaintext), ValueType::Constant(plaintext_type))
| (Value::Plaintext(plaintext), ValueType::Public(plaintext_type))
| (Value::Plaintext(plaintext), ValueType::Private(plaintext_type)) => {
self.matches_plaintext(plaintext, plaintext_type)
}
(Value::Record(record), ValueType::Record(record_name)) => self.matches_record(record, record_name),
(Value::Record(record), ValueType::ExternalRecord(locator)) => {
self.matches_external_record(record, locator)
}
(Value::Future(future), ValueType::Future(locator)) => self.matches_future(future, locator),
_ => bail!("A value does not match its declared value type '{value_type}'"),
}
}
fn matches_register_type(&self, stack_value: &Value<N>, register_type: &RegisterType<N>) -> Result<()> {
match (stack_value, register_type) {
(Value::Plaintext(plaintext), RegisterType::Plaintext(plaintext_type)) => {
self.matches_plaintext(plaintext, plaintext_type)
}
(Value::Record(record), RegisterType::Record(record_name)) => self.matches_record(record, record_name),
(Value::Record(record), RegisterType::ExternalRecord(locator)) => {
self.matches_external_record(record, locator)
}
(Value::Future(future), RegisterType::Future(locator)) => self.matches_future(future, locator),
_ => bail!("A value does not match its declared register type '{register_type}'"),
}
}
fn matches_external_record(&self, record: &Record<N, Plaintext<N>>, locator: &Locator<N>) -> Result<()> {
let record_name = locator.resource();
ensure!(!Program::is_reserved_keyword(record_name), "Record name '{record_name}' is reserved");
let external_stack = self.get_external_stack(locator.program_id())?;
let Ok(record_type) = external_stack.program().get_record(locator.resource()) else {
bail!("External '{locator}' is not defined in the program")
};
if record_type.name() != record_name {
bail!("Expected external record '{record_name}', found external record '{}'", record_type.name())
}
self.matches_record_internal(record, record_type, 0)
}
fn matches_record(&self, record: &Record<N, Plaintext<N>>, record_name: &Identifier<N>) -> Result<()> {
ensure!(!Program::is_reserved_keyword(record_name), "Record name '{record_name}' is reserved");
let Ok(record_type) = self.program().get_record(record_name) else {
bail!("Record '{record_name}' is not defined in the program")
};
if record_type.name() != record_name {
bail!("Expected record '{record_name}', found record '{}'", record_type.name())
}
self.matches_record_internal(record, record_type, 0)
}
fn matches_plaintext(&self, plaintext: &Plaintext<N>, plaintext_type: &PlaintextType<N>) -> Result<()> {
self.matches_plaintext_internal(plaintext, plaintext_type, 0)
}
fn matches_future(&self, future: &Future<N>, locator: &Locator<N>) -> Result<()> {
self.matches_future_internal(future, locator, 0)
}
fn contains_proving_key(&self, function_name: &Identifier<N>) -> bool {
self.proving_keys.read().contains_key(function_name)
}
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),
}
}
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(())
}
fn remove_proving_key(&self, function_name: &Identifier<N>) {
self.proving_keys.write().shift_remove(function_name);
}
fn contains_verifying_key(&self, function_name: &Identifier<N>) -> bool {
self.verifying_keys.read().contains_key(function_name)
}
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),
}
}
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(())
}
fn remove_verifying_key(&self, function_name: &Identifier<N>) {
self.verifying_keys.write().shift_remove(function_name);
}
fn program(&self) -> &Program<N> {
&self.program
}
fn program_id(&self) -> &ProgramID<N> {
self.program.id()
}
fn program_address(&self) -> &Address<N> {
&self.program_address
}
fn program_checksum(&self) -> &[U8<N>; 32] {
&self.program_checksum
}
#[inline]
fn program_checksum_as_field(&self) -> Result<Field<N>> {
let bits = self
.program_checksum
.iter()
.flat_map(|byte| byte.to_bits_le())
.take(Field::<N>::SIZE_IN_DATA_BITS)
.collect::<Vec<_>>();
Field::from_bits_le(&bits)
}
#[inline]
fn program_edition(&self) -> U16<N> {
self.program_edition
}
#[inline]
fn program_owner(&self) -> &Option<Address<N>> {
&self.program_owner
}
fn set_program_owner(&mut self, program_owner: Option<Address<N>>) {
self.program_owner = program_owner;
}
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"))
}
fn get_function(&self, function_name: &Identifier<N>) -> Result<Function<N>> {
self.program.get_function(function_name)
}
fn get_function_ref(&self, function_name: &Identifier<N>) -> Result<&Function<N>> {
self.program.get_function_ref(function_name)
}
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) => {
let stack = if locator.program_id() == self.program().id() {
StackRef::Internal(self)
} else {
StackRef::External(stack_ref.get_external_stack(locator.program_id())?)
};
queue.push((stack, *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>,
register_type: &RegisterType<N>,
rng: &mut R,
) -> Result<Value<N>> {
match register_type {
RegisterType::Plaintext(plaintext_type) => {
Ok(Value::Plaintext(self.sample_plaintext(plaintext_type, rng)?))
}
RegisterType::Record(record_name) => {
Ok(Value::Record(self.sample_record(burner_address, record_name, Group::rand(rng), rng)?))
}
RegisterType::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)?))
}
RegisterType::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> Stack<N> {
fn matches_record_internal(
&self,
record: &Record<N, Plaintext<N>>,
record_type: &RecordType<N>,
depth: usize,
) -> Result<()> {
ensure!(depth <= N::MAX_DATA_DEPTH, "Plaintext exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
let record_name = record_type.name();
ensure!(!Program::is_reserved_keyword(record_name), "Record name '{record_name}' is reserved");
ensure!(
record.owner().is_public() == record_type.owner().is_public(),
"Visibility of record entry 'owner' does not match"
);
ensure!(
record.owner().is_private() == record_type.owner().is_private(),
"Visibility of record entry 'owner' does not match"
);
let num_entries = record.data().len();
ensure!(num_entries <= N::MAX_DATA_ENTRIES, "'{record_name}' cannot exceed {} entries", N::MAX_DATA_ENTRIES);
let expected_num_entries = record_type.entries().len();
if expected_num_entries != num_entries {
bail!("'{record_name}' expected {expected_num_entries} entries, found {num_entries} entries")
}
for (i, ((expected_name, expected_type), (entry_name, entry))) in
record_type.entries().iter().zip_eq(record.data().iter()).enumerate()
{
if expected_name != entry_name {
bail!("Entry '{i}' in '{record_name}' is incorrect: expected '{expected_name}', found '{entry_name}'")
}
ensure!(!Program::is_reserved_keyword(entry_name), "Entry name '{entry_name}' is reserved");
self.matches_entry_internal(record_name, entry_name, entry, expected_type, depth + 1)?;
}
Ok(())
}
fn matches_entry_internal(
&self,
record_name: &Identifier<N>,
entry_name: &Identifier<N>,
entry: &Entry<N, Plaintext<N>>,
entry_type: &EntryType<N>,
depth: usize,
) -> Result<()> {
match (entry, entry_type) {
(Entry::Constant(plaintext), EntryType::Constant(plaintext_type))
| (Entry::Public(plaintext), EntryType::Public(plaintext_type))
| (Entry::Private(plaintext), EntryType::Private(plaintext_type)) => {
match self.matches_plaintext_internal(plaintext, plaintext_type, depth) {
Ok(()) => Ok(()),
Err(error) => bail!("Invalid record entry '{record_name}.{entry_name}': {error}"),
}
}
_ => bail!(
"Type mismatch in record entry '{record_name}.{entry_name}':\n'{entry}'\n does not match\n'{entry_type}'"
),
}
}
fn matches_plaintext_internal(
&self,
plaintext: &Plaintext<N>,
plaintext_type: &PlaintextType<N>,
depth: usize,
) -> Result<()> {
ensure!(depth <= N::MAX_DATA_DEPTH, "Plaintext exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
match plaintext_type {
PlaintextType::Literal(literal_type) => match plaintext {
Plaintext::Literal(literal, ..) => {
match literal.to_type() == *literal_type {
true => Ok(()),
false => bail!("'{literal}' is invalid: expected {literal_type}"),
}
}
Plaintext::Struct(..) => bail!("'{plaintext_type}' is invalid: expected literal, found struct"),
Plaintext::Array(..) => bail!("'{plaintext_type}' is invalid: expected literal, found array"),
},
PlaintextType::Struct(struct_name) => {
ensure!(!Program::is_reserved_keyword(struct_name), "Struct '{struct_name}' is reserved");
let Ok(struct_) = self.program().get_struct(struct_name) else {
bail!("Struct '{struct_name}' is not defined in the program")
};
if struct_.name() != struct_name {
bail!("Expected struct '{struct_name}', found struct '{}'", struct_.name())
}
let members = match plaintext {
Plaintext::Literal(..) => bail!("'{struct_name}' is invalid: expected struct, found literal"),
Plaintext::Struct(members, ..) => members,
Plaintext::Array(..) => bail!("'{struct_name}' is invalid: expected struct, found array"),
};
let num_members = members.len();
ensure!(
num_members >= N::MIN_STRUCT_ENTRIES,
"'{struct_name}' cannot be less than {} entries",
N::MIN_STRUCT_ENTRIES
);
ensure!(
num_members <= N::MAX_STRUCT_ENTRIES,
"'{struct_name}' cannot exceed {} entries",
N::MAX_STRUCT_ENTRIES
);
let expected_num_members = struct_.members().len();
if expected_num_members != num_members {
bail!("'{struct_name}' expected {expected_num_members} members, found {num_members} members")
}
for (i, ((expected_name, expected_type), (member_name, member))) in
struct_.members().iter().zip_eq(members.iter()).enumerate()
{
if expected_name != member_name {
bail!(
"Member '{i}' in '{struct_name}' is incorrect: expected '{expected_name}', found '{member_name}'"
)
}
ensure!(!Program::is_reserved_keyword(member_name), "Member name '{member_name}' is reserved");
self.matches_plaintext_internal(member, expected_type, depth + 1)?;
}
Ok(())
}
PlaintextType::Array(array_type) => match plaintext {
Plaintext::Literal(..) => bail!("'{plaintext_type}' is invalid: expected array, found literal"),
Plaintext::Struct(..) => bail!("'{plaintext_type}' is invalid: expected array, found struct"),
Plaintext::Array(array, ..) => {
let (actual_length, expected_length) = (array.len(), array_type.length());
if **expected_length as usize != actual_length {
bail!(
"'{plaintext_type}' is invalid: expected {expected_length} elements, found {actual_length} elements"
)
}
for element in array.iter() {
self.matches_plaintext_internal(element, array_type.next_element_type(), depth + 1)?;
}
Ok(())
}
},
}
}
fn matches_future_internal(&self, future: &Future<N>, locator: &Locator<N>, depth: usize) -> Result<()> {
ensure!(depth <= N::MAX_DATA_DEPTH, "Future exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
ensure!(future.program_id() == locator.program_id(), "Future program ID does not match");
ensure!(future.function_name() == locator.resource(), "Future name does not match");
let external_stack = match locator.program_id() == self.program_id() {
true => None,
false => Some(self.get_external_stack(locator.program_id())?),
};
let function = match &external_stack {
Some(external_stack) => external_stack.get_function_ref(locator.resource())?,
None => self.get_function_ref(locator.resource())?,
};
let inputs = match function.finalize_logic() {
Some(finalize_logic) => finalize_logic.inputs(),
None => bail!("Function '{locator}' does not have a finalize block"),
};
ensure!(future.arguments().len() == inputs.len(), "Future arguments do not match");
for (argument, input) in future.arguments().iter().zip_eq(inputs.iter()) {
match (argument, input.finalize_type()) {
(Argument::Plaintext(plaintext), FinalizeType::Plaintext(plaintext_type)) => {
self.matches_plaintext_internal(plaintext, plaintext_type, depth + 1)?
}
(Argument::Future(future), FinalizeType::Future(locator)) => {
self.matches_future_internal(future, locator, depth + 1)?
}
(_, input_type) => {
bail!("Argument type does not match input type: expected '{input_type}'")
}
}
}
Ok(())
}
}