use super::*;
impl<N: Network> Stack<N> {
pub 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)
}
_ => bail!("A value does not match its declared value type '{value_type}'"),
}
}
pub 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)
}
_ => bail!("A value does not match its declared register type '{register_type}'"),
}
}
pub 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 record_type = match self.get_external_record(locator) {
Ok(record_type) => record_type,
Err(..) => 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)
}
pub 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 record_type = match self.program().get_record(record_name) {
Ok(record_type) => record_type,
Err(..) => 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)
}
pub fn matches_plaintext(&self, plaintext: &Plaintext<N>, plaintext_type: &PlaintextType<N>) -> Result<()> {
self.matches_plaintext_internal(plaintext, plaintext_type, 0)
}
}
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"
);
ensure!(
record.gates().is_public() == record_type.gates().is_public(),
"Visibility of record entry 'gates' does not match"
);
ensure!(
record.gates().is_private() == record_type.gates().is_private(),
"Visibility of record entry 'gates' 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!("'{plaintext_type}' is invalid: expected {literal_type}, found {literal}"),
}
}
Plaintext::Interface(..) => bail!("'{plaintext_type}' is invalid: expected literal, found interface"),
},
PlaintextType::Interface(interface_name) => {
ensure!(!Program::is_reserved_keyword(interface_name), "Interface '{interface_name}' is reserved");
let interface = match self.program().get_interface(interface_name) {
Ok(interface) => interface,
Err(..) => bail!("Interface '{interface_name}' is not defined in the program"),
};
if interface.name() != interface_name {
bail!("Expected interface '{interface_name}', found interface '{}'", interface.name())
}
let members = match plaintext {
Plaintext::Literal(..) => bail!("'{interface_name}' is invalid: expected interface, found literal"),
Plaintext::Interface(members, ..) => members,
};
let num_members = members.len();
ensure!(
num_members <= N::MAX_DATA_ENTRIES,
"'{interface_name}' cannot exceed {} entries",
N::MAX_DATA_ENTRIES
);
let expected_num_members = interface.members().len();
if expected_num_members != num_members {
bail!("'{interface_name}' expected {expected_num_members} members, found {num_members} members")
}
for (i, ((expected_name, expected_type), (member_name, member))) in
interface.members().iter().zip_eq(members.iter()).enumerate()
{
if expected_name != member_name {
bail!(
"Member '{i}' in '{interface_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(())
}
}
}
}