1use super::*;
17
18impl<N: Network> StackMatches<N> for Stack<N> {
19 fn matches_value_type(&self, value: &Value<N>, value_type: &ValueType<N>) -> Result<()> {
21 match (value, value_type) {
23 (Value::Plaintext(plaintext), ValueType::Constant(plaintext_type))
24 | (Value::Plaintext(plaintext), ValueType::Public(plaintext_type))
25 | (Value::Plaintext(plaintext), ValueType::Private(plaintext_type)) => {
26 self.matches_plaintext(plaintext, plaintext_type)
27 }
28 (Value::Record(record), ValueType::Record(record_name)) => self.matches_record(record, record_name),
29 (Value::Record(record), ValueType::ExternalRecord(locator)) => {
30 self.matches_external_record(record, locator)
31 }
32 (Value::Future(future), ValueType::Future(locator)) => self.matches_future(future, locator),
33 _ => bail!("A value does not match its declared value type '{value_type}'"),
34 }
35 }
36
37 fn matches_register_type(&self, stack_value: &Value<N>, register_type: &RegisterType<N>) -> Result<()> {
39 match (stack_value, register_type) {
40 (Value::Plaintext(plaintext), RegisterType::Plaintext(plaintext_type)) => {
41 self.matches_plaintext(plaintext, plaintext_type)
42 }
43 (Value::Record(record), RegisterType::Record(record_name)) => self.matches_record(record, record_name),
44 (Value::Record(record), RegisterType::ExternalRecord(locator)) => {
45 self.matches_external_record(record, locator)
46 }
47 (Value::Future(future), RegisterType::Future(locator)) => self.matches_future(future, locator),
48 _ => bail!("A value does not match its declared register type '{register_type}'"),
49 }
50 }
51
52 fn matches_external_record(&self, record: &Record<N, Plaintext<N>>, locator: &Locator<N>) -> Result<()> {
54 let record_name = locator.resource();
56
57 ensure!(!Program::is_reserved_keyword(record_name), "Record name '{record_name}' is reserved");
59
60 let external_stack = self.get_external_stack(locator.program_id())?;
62 let Ok(record_type) = external_stack.program().get_record(locator.resource()) else {
64 bail!("External '{locator}' is not defined in the program")
65 };
66
67 if record_type.name() != record_name {
69 bail!("Expected external record '{record_name}', found external record '{}'", record_type.name())
70 }
71
72 self.matches_record_internal(record, record_type, 0)
73 }
74
75 fn matches_record(&self, record: &Record<N, Plaintext<N>>, record_name: &Identifier<N>) -> Result<()> {
77 ensure!(!Program::is_reserved_keyword(record_name), "Record name '{record_name}' is reserved");
79
80 let Ok(record_type) = self.program().get_record(record_name) else {
82 bail!("Record '{record_name}' is not defined in the program")
83 };
84
85 if record_type.name() != record_name {
87 bail!("Expected record '{record_name}', found record '{}'", record_type.name())
88 }
89
90 self.matches_record_internal(record, record_type, 0)
91 }
92
93 fn matches_plaintext(&self, plaintext: &Plaintext<N>, plaintext_type: &PlaintextType<N>) -> Result<()> {
95 self.matches_plaintext_internal(plaintext, plaintext_type, 0)
96 }
97
98 fn matches_future(&self, future: &Future<N>, locator: &Locator<N>) -> Result<()> {
100 self.matches_future_internal(future, locator, 0)
101 }
102}
103
104impl<N: Network> Stack<N> {
105 fn matches_record_internal(
107 &self,
108 record: &Record<N, Plaintext<N>>,
109 record_type: &RecordType<N>,
110 depth: usize,
111 ) -> Result<()> {
112 ensure!(depth <= N::MAX_DATA_DEPTH, "Plaintext exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
114
115 let record_name = record_type.name();
117 ensure!(!Program::is_reserved_keyword(record_name), "Record name '{record_name}' is reserved");
119
120 ensure!(
122 record.owner().is_public() == record_type.owner().is_public(),
123 "Visibility of record entry 'owner' does not match"
124 );
125 ensure!(
126 record.owner().is_private() == record_type.owner().is_private(),
127 "Visibility of record entry 'owner' does not match"
128 );
129
130 let num_entries = record.data().len();
132 ensure!(num_entries <= N::MAX_DATA_ENTRIES, "'{record_name}' cannot exceed {} entries", N::MAX_DATA_ENTRIES);
133
134 let expected_num_entries = record_type.entries().len();
136 if expected_num_entries != num_entries {
137 bail!("'{record_name}' expected {expected_num_entries} entries, found {num_entries} entries")
138 }
139
140 for (i, ((expected_name, expected_type), (entry_name, entry))) in
142 record_type.entries().iter().zip_eq(record.data().iter()).enumerate()
143 {
144 if expected_name != entry_name {
146 bail!("Entry '{i}' in '{record_name}' is incorrect: expected '{expected_name}', found '{entry_name}'")
147 }
148 ensure!(!Program::is_reserved_keyword(entry_name), "Entry name '{entry_name}' is reserved");
150 self.matches_entry_internal(record_name, entry_name, entry, expected_type, depth + 1)?;
152 }
153
154 Ok(())
155 }
156
157 fn matches_entry_internal(
159 &self,
160 record_name: &Identifier<N>,
161 entry_name: &Identifier<N>,
162 entry: &Entry<N, Plaintext<N>>,
163 entry_type: &EntryType<N>,
164 depth: usize,
165 ) -> Result<()> {
166 match (entry, entry_type) {
167 (Entry::Constant(plaintext), EntryType::Constant(plaintext_type))
168 | (Entry::Public(plaintext), EntryType::Public(plaintext_type))
169 | (Entry::Private(plaintext), EntryType::Private(plaintext_type)) => {
170 match self.matches_plaintext_internal(plaintext, plaintext_type, depth) {
171 Ok(()) => Ok(()),
172 Err(error) => bail!("Invalid record entry '{record_name}.{entry_name}': {error}"),
173 }
174 }
175 _ => bail!(
176 "Type mismatch in record entry '{record_name}.{entry_name}':\n'{entry}'\n does not match\n'{entry_type}'"
177 ),
178 }
179 }
180
181 fn matches_plaintext_internal(
183 &self,
184 plaintext: &Plaintext<N>,
185 plaintext_type: &PlaintextType<N>,
186 depth: usize,
187 ) -> Result<()> {
188 ensure!(depth <= N::MAX_DATA_DEPTH, "Plaintext exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
190
191 match plaintext_type {
193 PlaintextType::Literal(literal_type) => match plaintext {
194 Plaintext::Literal(literal, ..) => {
196 match literal.to_type() == *literal_type {
198 true => Ok(()),
199 false => bail!("'{plaintext_type}' is invalid: expected {literal_type}, found {literal}"),
200 }
201 }
202 Plaintext::Struct(..) => bail!("'{plaintext_type}' is invalid: expected literal, found struct"),
204 Plaintext::Array(..) => bail!("'{plaintext_type}' is invalid: expected literal, found array"),
206 },
207 PlaintextType::Struct(struct_name) => {
208 ensure!(!Program::is_reserved_keyword(struct_name), "Struct '{struct_name}' is reserved");
210
211 let Ok(struct_) = self.program().get_struct(struct_name) else {
213 bail!("Struct '{struct_name}' is not defined in the program")
214 };
215
216 if struct_.name() != struct_name {
218 bail!("Expected struct '{struct_name}', found struct '{}'", struct_.name())
219 }
220
221 let members = match plaintext {
223 Plaintext::Literal(..) => bail!("'{struct_name}' is invalid: expected struct, found literal"),
224 Plaintext::Struct(members, ..) => members,
225 Plaintext::Array(..) => bail!("'{struct_name}' is invalid: expected struct, found array"),
226 };
227
228 let num_members = members.len();
229 ensure!(
231 num_members >= N::MIN_STRUCT_ENTRIES,
232 "'{struct_name}' cannot be less than {} entries",
233 N::MIN_STRUCT_ENTRIES
234 );
235 ensure!(
237 num_members <= N::MAX_STRUCT_ENTRIES,
238 "'{struct_name}' cannot exceed {} entries",
239 N::MAX_STRUCT_ENTRIES
240 );
241
242 let expected_num_members = struct_.members().len();
244 if expected_num_members != num_members {
245 bail!("'{struct_name}' expected {expected_num_members} members, found {num_members} members")
246 }
247
248 for (i, ((expected_name, expected_type), (member_name, member))) in
250 struct_.members().iter().zip_eq(members.iter()).enumerate()
251 {
252 if expected_name != member_name {
254 bail!(
255 "Member '{i}' in '{struct_name}' is incorrect: expected '{expected_name}', found '{member_name}'"
256 )
257 }
258 ensure!(!Program::is_reserved_keyword(member_name), "Member name '{member_name}' is reserved");
260 self.matches_plaintext_internal(member, expected_type, depth + 1)?;
262 }
263
264 Ok(())
265 }
266 PlaintextType::Array(array_type) => match plaintext {
267 Plaintext::Literal(..) => bail!("'{plaintext_type}' is invalid: expected array, found literal"),
269 Plaintext::Struct(..) => bail!("'{plaintext_type}' is invalid: expected array, found struct"),
271 Plaintext::Array(array, ..) => {
273 let (actual_length, expected_length) = (array.len(), array_type.length());
275 if **expected_length as usize != actual_length {
276 bail!(
277 "'{plaintext_type}' is invalid: expected {expected_length} elements, found {actual_length} elements"
278 )
279 }
280 for element in array.iter() {
282 self.matches_plaintext_internal(element, array_type.next_element_type(), depth + 1)?;
283 }
284 Ok(())
285 }
286 },
287 }
288 }
289
290 fn matches_future_internal(&self, future: &Future<N>, locator: &Locator<N>, depth: usize) -> Result<()> {
292 ensure!(depth <= N::MAX_DATA_DEPTH, "Future exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
294
295 ensure!(future.program_id() == locator.program_id(), "Future program ID does not match");
297
298 ensure!(future.function_name() == locator.resource(), "Future name does not match");
300
301 let external_stack = match locator.program_id() == self.program_id() {
303 true => None,
304 false => Some(self.get_external_stack(locator.program_id())?),
307 };
308 let function = match &external_stack {
310 Some(external_stack) => external_stack.get_function_ref(locator.resource())?,
311 None => self.get_function_ref(locator.resource())?,
312 };
313 let inputs = match function.finalize_logic() {
315 Some(finalize_logic) => finalize_logic.inputs(),
316 None => bail!("Function '{locator}' does not have a finalize block"),
317 };
318
319 ensure!(future.arguments().len() == inputs.len(), "Future arguments do not match");
321
322 for (argument, input) in future.arguments().iter().zip_eq(inputs.iter()) {
324 match (argument, input.finalize_type()) {
325 (Argument::Plaintext(plaintext), FinalizeType::Plaintext(plaintext_type)) => {
326 self.matches_plaintext_internal(plaintext, plaintext_type, depth + 1)?
327 }
328 (Argument::Future(future), FinalizeType::Future(locator)) => {
329 self.matches_future_internal(future, locator, depth + 1)?
330 }
331 (_, input_type) => {
332 bail!("Argument type does not match input type: expected '{input_type}'")
333 }
334 }
335 }
336
337 Ok(())
338 }
339}