Skip to main content

snarkvm_circuit_program/request/
verify.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18impl<A: Aleo> Request<A> {
19    /// Returns `true` if the input IDs are derived correctly, the input records all belong to the signer,
20    /// and the signature is valid.
21    ///
22    /// Verifies (challenge == challenge') && (address == address') && (serial_numbers == serial_numbers') where:
23    ///     challenge' := HashToScalar(r * G, pk_sig, pr_sig, signer, \[tvk, tcm, function ID, is_root, program checksum?, input IDs\])
24    /// The program checksum must be provided if the program has a constructor and should not be provided otherwise.
25    pub fn verify(
26        &self,
27        input_types: &[console::ValueType<A::Network>],
28        tpk: &Group<A>,
29        root_tvk: Option<Field<A>>,
30        is_root: Boolean<A>,
31        program_checksum: Option<Field<A>>,
32    ) -> Boolean<A> {
33        // Compute the function ID.
34        let function_id = compute_function_id(&self.network_id, &self.program_id, &self.function_name);
35
36        // Compute 'is_root' as a field element.
37        let is_root = Ternary::ternary(&is_root, &Field::<A>::one(), &Field::<A>::zero());
38
39        // Construct the signature message as `[tvk, tcm, function ID, is_root, program checksum?, input IDs]`.
40        let mut message = Vec::with_capacity(3 + 4 * self.input_ids.len());
41        message.push(self.tvk.clone());
42        message.push(self.tcm.clone());
43        message.push(function_id);
44        message.push(is_root);
45        // Add the program checksum to the signature message if it was provided.
46        if let Some(program_checksum) = program_checksum {
47            message.push(program_checksum);
48        }
49
50        // Check the input IDs and construct the rest of the signature message.
51        let (input_checks, append_to_message) = Self::check_input_ids::<true>(
52            &self.network_id,
53            &self.program_id,
54            &self.function_name,
55            &self.input_ids,
56            &self.inputs,
57            input_types,
58            &self.signer,
59            &self.sk_tag,
60            &self.tvk,
61            &self.tcm,
62            Some(&self.signature),
63            None, // The function ID is intentionally not passed here to ensure that the existing circuit does not change.
64        );
65        // Append the input elements to the message.
66        match append_to_message {
67            Some(append_to_message) => message.extend(append_to_message),
68            None => A::halt("Missing input elements in request verification"),
69        }
70
71        // Determine the root transition view key.
72        let root_tvk = root_tvk.unwrap_or(Field::<A>::new(Mode::Private, self.tvk.eject_value()));
73
74        // Verify the transition public key and commitments are well-formed.
75        let tpk_checks = {
76            // Compute the transition commitment as `Hash(tvk)`.
77            let tcm = A::hash_psd2(&[self.tvk.clone()]);
78            // Compute the signer commitment as `Hash(signer || root_tvk)`.
79            let scm = A::hash_psd2(&[self.signer.to_field(), root_tvk]);
80
81            // Ensure the transition public key matches with the saved one from the signature.
82            tpk.is_equal(&self.to_tpk())
83            // Ensure the computed transition commitment matches.
84            & tcm.is_equal(&self.tcm)
85            // Ensure the computed signer commitment matches.
86            & scm.is_equal(&self.scm)
87        };
88
89        // Verify the signature.
90        // Note: We copy/paste the Aleo signature verification code here in order to compute `tpk` only once.
91        let signature_checks = {
92            // Retrieve pk_sig.
93            let pk_sig = self.signature.compute_key().pk_sig();
94            // Retrieve pr_sig.
95            let pr_sig = self.signature.compute_key().pr_sig();
96
97            // Construct the hash input as (r * G, pk_sig, pr_sig, address, message).
98            let mut preimage = Vec::with_capacity(4 + message.len());
99            preimage.extend([tpk, pk_sig, pr_sig].map(|point| point.to_x_coordinate()));
100            preimage.push(self.signer.to_field());
101            preimage.extend_from_slice(&message);
102
103            // Compute the candidate verifier challenge.
104            let candidate_challenge = A::hash_to_scalar_psd8(&preimage);
105            // Compute the candidate address.
106            let candidate_address = self.signature.compute_key().to_address();
107
108            // Return `true` if the challenge and address is valid.
109            self.signature.challenge().is_equal(&candidate_challenge) & self.signer.is_equal(&candidate_address)
110        };
111
112        // Verify the signature, inputs, and `tpk` are valid.
113        signature_checks & input_checks & tpk_checks
114    }
115
116    /// Returns `true` if the inputs match their input IDs.
117    /// Note: This method does **not** perform signature checks.
118    ///
119    /// The `function_id` parameter is optional for backwards compatibility. When `None`, the
120    /// function ID is computed from the network ID, program ID, and function name. When `Some`,
121    /// the provided function ID is used directly. This is critical for dynamic dispatch where the
122    /// function ID must be passed in to ensure circuit size does not depend on the length of the
123    /// program or function name.
124    pub fn check_input_ids<const CREATE_MESSAGE: bool>(
125        network_id: &U16<A>,
126        program_id: &ProgramID<A>,
127        function_name: &Identifier<A>,
128        input_ids: &[InputID<A>],
129        inputs: &[Value<A>],
130        input_types: &[console::ValueType<A::Network>],
131        signer: &Address<A>,
132        sk_tag: &Field<A>,
133        tvk: &Field<A>,
134        tcm: &Field<A>,
135        signature: Option<&Signature<A>>,
136        function_id: Option<Field<A>>,
137    ) -> (Boolean<A>, Option<Vec<Field<A>>>) {
138        // Ensure the signature response matches the `CREATE_MESSAGE` flag.
139        match CREATE_MESSAGE {
140            true => assert!(signature.is_some()),
141            false => assert!(signature.is_none()),
142        }
143
144        // Compute the function ID.
145        let function_id = match function_id {
146            Some(function_id) => function_id,
147            None => compute_function_id(network_id, program_id, function_name),
148        };
149
150        // Initialize a vector for a message.
151        let mut message = Vec::new();
152
153        // Perform the input ID checks.
154        let input_checks = input_ids
155            .iter()
156            .zip_eq(inputs)
157            .zip_eq(input_types)
158            .enumerate()
159            .map(|(index, ((input_id, input), input_type))| {
160                match input_id {
161                    // A constant input is hashed (using `tcm`) to a field element.
162                    InputID::Constant(input_hash) => {
163                        // Add the input hash to the message.
164                        if CREATE_MESSAGE {
165                            message.push(input_hash.clone());
166                        }
167
168                        // Prepare the index as a constant field element.
169                        let input_index = Field::constant(console::Field::from_u16(index as u16));
170                        // Construct the preimage as `(function ID || input || tcm || index)`.
171                        let mut preimage = Vec::new();
172                        preimage.push(function_id.clone());
173                        preimage.extend(input.to_fields());
174                        preimage.push(tcm.clone());
175                        preimage.push(input_index);
176
177                        // Ensure the expected hash matches the computed hash.
178                        match &input {
179                            Value::Plaintext(..) => input_hash.is_equal(&A::hash_psd8(&preimage)),
180                            // Ensure the input is not a record, future, or dynamic value.
181                            Value::Record(..) => A::halt("Expected a constant plaintext input, found a record input"),
182                            Value::Future(..) => A::halt("Expected a constant plaintext input, found a future input"),
183                            Value::DynamicRecord(..) => {
184                                A::halt("Expected a constant plaintext input, found a dynamic record input")
185                            }
186                            Value::DynamicFuture(..) => {
187                                A::halt("Expected a constant plaintext input, found a dynamic future input")
188                            }
189                        }
190                    }
191                    // A public input is hashed (using `tcm`) to a field element.
192                    InputID::Public(input_hash) => {
193                        // Add the input hash to the message.
194                        if CREATE_MESSAGE {
195                            message.push(input_hash.clone());
196                        }
197
198                        // Prepare the index as a constant field element.
199                        let input_index = Field::constant(console::Field::from_u16(index as u16));
200                        // Construct the preimage as `(function ID || input || tcm || index)`.
201                        let mut preimage = Vec::new();
202                        preimage.push(function_id.clone());
203                        preimage.extend(input.to_fields());
204                        preimage.push(tcm.clone());
205                        preimage.push(input_index);
206
207                        // Ensure the expected hash matches the computed hash.
208                        match &input {
209                            Value::Plaintext(..) => input_hash.is_equal(&A::hash_psd8(&preimage)),
210                            // Ensure the input is not a record, future, or dynamic value.
211                            Value::Record(..) => A::halt("Expected a public plaintext input, found a record input"),
212                            Value::Future(..) => A::halt("Expected a public plaintext input, found a future input"),
213                            Value::DynamicRecord(..) => {
214                                A::halt("Expected a public plaintext input, found a dynamic record input")
215                            }
216                            Value::DynamicFuture(..) => {
217                                A::halt("Expected a public plaintext input, found a dynamic future input")
218                            }
219                        }
220                    }
221                    // A private input is encrypted (using `tvk`) and hashed to a field element.
222                    InputID::Private(input_hash) => {
223                        // Add the input hash to the message.
224                        if CREATE_MESSAGE {
225                            message.push(input_hash.clone());
226                        }
227
228                        // Prepare the index as a constant field element.
229                        let input_index = Field::constant(console::Field::from_u16(index as u16));
230                        // Compute the input view key as `Hash(function ID || tvk || index)`.
231                        let input_view_key = A::hash_psd4(&[function_id.clone(), tvk.clone(), input_index]);
232                        // Compute the ciphertext.
233                        let ciphertext = match &input {
234                            Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(input_view_key),
235                            // Ensure the input is a plaintext.
236                            Value::Record(..) => A::halt("Expected a private plaintext input, found a record input"),
237                            Value::Future(..) => A::halt("Expected a private plaintext input, found a future input"),
238                            Value::DynamicRecord(..) => {
239                                A::halt("Expected a private plaintext input, found a dynamic record input")
240                            }
241                            Value::DynamicFuture(..) => {
242                                A::halt("Expected a private plaintext input, found a dynamic future input")
243                            }
244                        };
245
246                        // Ensure the expected hash matches the computed hash.
247                        input_hash.is_equal(&A::hash_psd8(&ciphertext.to_fields()))
248                    }
249                    // A record input is computed to its serial number.
250                    InputID::Record(commitment, gamma, record_view_key, serial_number, tag) => {
251                        // Retrieve the record.
252                        let record = match &input {
253                            Value::Record(record) => record,
254                            // Ensure the input is a record.
255                            Value::Plaintext(..) => A::halt("Expected a record input, found a plaintext input"),
256                            Value::Future(..) => A::halt("Expected a record input, found a future input"),
257                            Value::DynamicRecord(..) => {
258                                A::halt("Expected a record input, found a dynamic record input")
259                            }
260                            Value::DynamicFuture(..) => {
261                                A::halt("Expected a record input, found a dynamic future input")
262                            }
263                        };
264                        // Retrieve the record name as a `Mode::Constant`.
265                        let record_name = match input_type {
266                            console::ValueType::Record(record_name) => Identifier::constant(*record_name),
267                            // Ensure the input is a record.
268                            _ => A::halt(format!("Expected a record input at input {index}")),
269                        };
270                        // Compute the record commitment.
271                        let candidate_commitment = record.to_commitment(program_id, &record_name, record_view_key);
272                        // Compute the `candidate_serial_number` from `gamma`.
273                        let candidate_serial_number =
274                            Record::<A, Plaintext<A>>::serial_number_from_gamma(gamma, candidate_commitment.clone());
275                        // Compute the tag.
276                        let candidate_tag =
277                            Record::<A, Plaintext<A>>::tag(sk_tag.clone(), candidate_commitment.clone());
278
279                        if CREATE_MESSAGE {
280                            // Ensure the signature is declared.
281                            let signature = match signature {
282                                Some(signature) => signature,
283                                None => A::halt("Missing signature in logic to check input IDs"),
284                            };
285                            // Retrieve the challenge from the signature.
286                            let challenge = signature.challenge();
287                            // Retrieve the response from the signature.
288                            let response = signature.response();
289
290                            // Compute the generator `H` as `HashToGroup(commitment)`.
291                            let h = A::hash_to_group_psd2(&[A::serial_number_domain(), candidate_commitment.clone()]);
292                            // Compute `h_r` as `(challenge * gamma) + (response * H)`, equivalent to `r * H`.
293                            let h_r = (gamma.deref() * challenge) + (&h * response);
294
295                            // Add (`H`, `r * H`, `gamma`, `tag`) to the message.
296                            message.extend([h, h_r, *gamma.clone()].iter().map(|point| point.to_x_coordinate()));
297                            message.push(candidate_tag.clone());
298                        }
299
300                        // Ensure the candidate serial number matches the expected serial number.
301                        serial_number.is_equal(&candidate_serial_number)
302                            // Ensure the candidate commitment matches the expected commitment.
303                            & commitment.is_equal(&candidate_commitment)
304                            // Ensure the candidate tag matches the expected tag.
305                            & tag.is_equal(&candidate_tag)
306                            // Ensure the record belongs to the signer.
307                            & record.owner().deref().is_equal(signer)
308                    }
309                    // An external record input is hashed (using `tvk`) to a field element.
310                    InputID::ExternalRecord(input_hash) => {
311                        // Add the input hash to the message.
312                        if CREATE_MESSAGE {
313                            message.push(input_hash.clone());
314                        }
315
316                        // Retrieve the record.
317                        let record = match &input {
318                            Value::Record(record) => record,
319                            // Ensure the input is a record.
320                            Value::Plaintext(..) => {
321                                A::halt("Expected an external record input, found a plaintext input")
322                            }
323                            Value::Future(..) => A::halt("Expected an external record input, found a future input"),
324                            Value::DynamicRecord(..) => {
325                                A::halt("Expected an external record input, found a dynamic record input")
326                            }
327                            Value::DynamicFuture(..) => {
328                                A::halt("Expected an external record input, found a dynamic future input")
329                            }
330                        };
331
332                        // Prepare the index as a constant field element.
333                        let input_index = Field::constant(console::Field::from_u16(index as u16));
334                        // Construct the preimage as `(function ID || input || tvk || index)`.
335                        let mut preimage = Vec::new();
336                        preimage.push(function_id.clone());
337                        preimage.extend(record.to_fields());
338                        preimage.push(tvk.clone());
339                        preimage.push(input_index);
340
341                        // Ensure the expected hash matches the computed hash.
342                        input_hash.is_equal(&A::hash_psd8(&preimage))
343                    }
344                    // A dynamic record input is hashed (using `tvk`) to a field element.
345                    InputID::DynamicRecord(input_hash) => {
346                        // Add the input hash to the message.
347                        if CREATE_MESSAGE {
348                            message.push(input_hash.clone());
349                        }
350
351                        // Retrieve the dynamic record.
352                        let record = match &input {
353                            Value::DynamicRecord(dynamic_record) => dynamic_record,
354                            // Ensure the input is a dynamic record.
355                            Value::Plaintext(..) => A::halt("Expected a dynamic record input, found a plaintext input"),
356                            Value::Future(..) => A::halt("Expected a dynamic record input, found a future input"),
357                            Value::Record(..) => A::halt("Expected a dynamic record input, found a record input"),
358                            Value::DynamicFuture(..) => {
359                                A::halt("Expected a dynamic record input, found a dynamic future input")
360                            }
361                        };
362
363                        // Prepare the index as a constant field element.
364                        let input_index = Field::constant(console::Field::from_u16(index as u16));
365                        // Construct the preimage as `(function ID || input || tvk || index)`.
366                        let mut preimage = Vec::new();
367                        preimage.push(function_id.clone());
368                        preimage.extend(record.to_fields());
369                        preimage.push(tvk.clone());
370                        preimage.push(input_index);
371
372                        // Ensure the expected hash matches the computed hash.
373                        input_hash.is_equal(&A::hash_psd8(&preimage))
374                    }
375                }
376            })
377            .fold(Boolean::constant(true), |acc, x| acc & x);
378
379        // Return the boolean, and (optional) the message.
380        match CREATE_MESSAGE {
381            true => (input_checks, Some(message)),
382            false => match message.is_empty() {
383                true => (input_checks, None),
384                false => A::halt("Malformed synthesis of the logic to check input IDs"),
385            },
386        }
387    }
388}
389
390#[cfg(test)]
391mod tests {
392    use super::*;
393    use crate::Circuit;
394    use snarkvm_circuit_types::environment::UpdatableCount;
395    use snarkvm_utilities::TestRng;
396
397    use anyhow::Result;
398
399    pub(crate) const ITERATIONS: usize = 10;
400
401    /// Helper function to create a request given a program_id and function_name.
402    #[allow(clippy::type_complexity)]
403    fn create_request(
404        program_id: &str,
405        function_name: &str,
406        set_program_checksum: bool,
407        is_dynamic: bool,
408        use_record: bool,
409        i: usize,
410        rng: &mut TestRng,
411    ) -> Result<(
412        console::Request<<Circuit as Environment>::Network>,
413        Vec<console::ValueType<<Circuit as Environment>::Network>>,
414        bool,
415        Option<console::Field<<Circuit as Environment>::Network>>,
416    )> {
417        // Sample a random private key and address.
418        let private_key = snarkvm_console_account::PrivateKey::new(rng)?;
419        let address = snarkvm_console_account::Address::try_from(&private_key).unwrap();
420
421        // Construct a program ID and function name.
422        let program_id = console::ProgramID::from_str(program_id)?;
423        let function_name = console::Identifier::from_str(function_name)?;
424
425        // Prepare a record belonging to the address.
426        let record_string = format!(
427            "{{ owner: {address}.private, token_amount: 100u64.private, _nonce: 0group.public, _version: 1u8.public }}"
428        );
429
430        // Construct the inputs.
431        let input_constant =
432            console::Value::<<Circuit as Environment>::Network>::from_str("{ token_amount: 9876543210u128 }").unwrap();
433        let input_public =
434            console::Value::<<Circuit as Environment>::Network>::from_str("{ token_amount: 9876543210u128 }").unwrap();
435        let input_private =
436            console::Value::<<Circuit as Environment>::Network>::from_str("{ token_amount: 9876543210u128 }").unwrap();
437        let input_record = console::Value::<<Circuit as Environment>::Network>::from_str(&record_string).unwrap();
438        let input_external_record =
439            console::Value::<<Circuit as Environment>::Network>::from_str(&record_string).unwrap();
440        let inputs = if use_record {
441            vec![input_constant, input_public, input_private, input_record, input_external_record]
442        } else {
443            vec![input_constant, input_public, input_private, input_external_record]
444        };
445
446        // Construct the input types.
447        let input_types = if use_record {
448            vec![
449                console::ValueType::from_str("amount.constant").unwrap(),
450                console::ValueType::from_str("amount.public").unwrap(),
451                console::ValueType::from_str("amount.private").unwrap(),
452                console::ValueType::from_str("token.record").unwrap(),
453                console::ValueType::from_str("token.aleo/token.record").unwrap(),
454            ]
455        } else {
456            vec![
457                console::ValueType::from_str("amount.constant").unwrap(),
458                console::ValueType::from_str("amount.public").unwrap(),
459                console::ValueType::from_str("amount.private").unwrap(),
460                console::ValueType::from_str("token.aleo/token.record").unwrap(),
461            ]
462        };
463
464        // Sample 'root_tvk'.
465        let root_tvk = None;
466        // Sample 'is_root'.
467        let is_root = true;
468        // Sample 'program_checksum'.
469        let program_checksum = set_program_checksum.then(|| console::Field::from_u64(i as u64));
470
471        // Compute the signed request.
472        let request = console::Request::sign(
473            &private_key,
474            program_id,
475            function_name,
476            inputs.iter(),
477            &input_types,
478            root_tvk,
479            is_root,
480            program_checksum,
481            is_dynamic,
482            rng,
483        )?;
484        assert!(request.verify(&input_types, is_root, program_checksum));
485
486        Ok((request, input_types, is_root, program_checksum))
487    }
488
489    fn check_verify(
490        mode: Mode,
491        program_id: &str,
492        function_name: &str,
493        count: UpdatableCount,
494        set_program_checksum: bool,
495        is_dynamic: bool,
496        use_record: bool,
497    ) -> Result<()> {
498        let rng = &mut TestRng::default();
499
500        for i in 0..ITERATIONS {
501            let (request, input_types, is_root, program_checksum) =
502                create_request(program_id, function_name, set_program_checksum, is_dynamic, use_record, i, rng)?;
503
504            // Inject the request into a circuit.
505            let tpk = Group::<Circuit>::new(mode, request.to_tpk());
506            let request = Request::<Circuit>::new(mode, request);
507            let is_root = Boolean::new(mode, is_root);
508            let program_checksum = program_checksum.map(|hash| Field::<Circuit>::new(mode, hash));
509
510            Circuit::scope(format!("Request {i}"), || {
511                let root_tvk = None;
512                let candidate = request.verify(&input_types, &tpk, root_tvk, is_root, program_checksum);
513                assert!(candidate.eject_value());
514                count.assert_matches(
515                    Circuit::num_constants_in_scope(),
516                    Circuit::num_public_in_scope(),
517                    Circuit::num_private_in_scope(),
518                    Circuit::num_constraints_in_scope(),
519                );
520            });
521            Circuit::reset();
522        }
523        Ok(())
524    }
525
526    fn check_check_input_ids(
527        mode: Mode,
528        program_id: &str,
529        function_name: &str,
530        expected_count: UpdatableCount,
531        set_program_checksum: bool,
532        is_dynamic: bool,
533        use_record: bool,
534    ) -> Result<()> {
535        let rng = &mut TestRng::default();
536
537        for i in 0..ITERATIONS {
538            let (request, input_types, _, _) =
539                create_request(program_id, function_name, set_program_checksum, is_dynamic, use_record, i, rng)?;
540
541            // Inject the request into a circuit.
542            let request = Request::<Circuit>::new(mode, request);
543
544            // If the request is dynamic, compute the function ID.
545            let function_id = if is_dynamic {
546                Some(compute_function_id(request.network_id(), request.program_id(), request.function_name()))
547            } else {
548                None
549            };
550
551            Circuit::scope(format!("Request {i}"), || {
552                let (candidate, _) = Request::check_input_ids::<false>(
553                    request.network_id(),
554                    request.program_id(),
555                    request.function_name(),
556                    request.input_ids(),
557                    request.inputs(),
558                    &input_types,
559                    request.signer(),
560                    request.sk_tag(),
561                    request.tvk(),
562                    request.tcm(),
563                    None,
564                    function_id,
565                );
566                assert!(candidate.eject_value());
567                expected_count.assert_matches(
568                    Circuit::num_constants_in_scope(),
569                    Circuit::num_public_in_scope(),
570                    Circuit::num_private_in_scope(),
571                    Circuit::num_constraints_in_scope(),
572                );
573            });
574
575            Circuit::reset();
576        }
577        Ok(())
578    }
579
580    // TODO: Explain why the first runs of the tests for `Public` and `Private` modes yield a large number of constants
581
582    #[test]
583    #[rustfmt::skip]
584    fn test_sign_and_verify_constant() -> Result<()> {
585        // Note: The variable bounds are correct. At this (high) level of a program, we override the default mode in the `Record` case,
586        // based on the user-defined visibility in the record type. Thus, we have nonzero private and constraint values.
587        // These bounds are determined experimentally.
588
589        // Static requests with records.
590        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(43442, 0, 20996, 21023), false, false, true)?;
591        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(11008, 0, 21511, 21538), true, false, true)?;
592        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(10978, 0, 21098, 21125), false, false, true)?;
593        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(10978, 0, 21613, 21640), true, false, true)?;
594
595        // Dynamic requests with records.
596        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(11008, 0, 28617, 28660), false, true, true)?;
597        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(11008, 0, 29132, 29175), true, true, true)?;
598        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(10978, 0, 28789, 28832), false, true, true)?;
599        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(10978, 0, 29304, 29347), true, true, true)?;
600
601
602        // Static requests without records.
603        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(5740, 0, 4718, 4723), false, false, false)?;
604        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(5740, 0, 4718, 4723), true, false, false)?;
605        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(5780, 0, 4718, 4723), false, false, false)?;
606        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(5780, 0, 4718, 4723), true, false, false)?;
607
608        // Dynamic requests without records.
609        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(5740, 0, 11206, 11223), false, true, false)?;
610        check_verify(Mode::Constant, "test.aleo", "bark", count_less_than!(5740, 0, 11206, 11223), true, true, false)?;
611        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(5780, 0, 11206, 11223), false, true, false)?;
612        check_verify(Mode::Constant, "credits.aleo", "foo", count_less_than!(5780, 0, 11206, 11223), true, true, false)?;
613
614        Ok(())
615    }
616
617    #[test]
618    #[rustfmt::skip]
619    fn test_sign_and_verify_public() -> Result<()> {
620        // Static requests with records.
621        check_verify(Mode::Public, "test.aleo", "bark", count_is!(<=40943, 0, 29913, 29944), false, false, true)?;
622        check_verify(Mode::Public, "test.aleo", "bark", count_is!(8502, 0, 30428, 30459), true, false, true)?;
623        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(8472, 0, 30015, 30046), false, false, true)?;
624        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(8472, 0, 30530, 30561), true, false, true)?;
625
626        // Dynamic requests with records.
627        check_verify(Mode::Public, "test.aleo", "bark", count_is!(8502, 0, 29913, 29944), false, true, true)?;
628        check_verify(Mode::Public, "test.aleo", "bark", count_is!(8502, 0, 30428, 30459), true, true, true)?;
629        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(8472, 0, 30015, 30046), false, true, true)?;
630        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(8472, 0, 30530, 30561), true, true, true)?;
631
632        // Static requests without records.
633        check_verify(Mode::Public, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), false, false, false)?;
634        check_verify(Mode::Public, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), true, false, false)?;
635        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), false, false, false)?;
636        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), true, false, false)?;
637
638        // Dynamic requests without records.
639        check_verify(Mode::Public, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), false, true, false)?;
640        check_verify(Mode::Public, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), true, true, false)?;
641        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), false, true, false)?;
642        check_verify(Mode::Public, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), true, true, false)?;
643
644
645        Ok(())
646    }
647
648    #[test]
649    #[rustfmt::skip]
650    fn test_sign_and_verify_private() -> Result<()> {
651        // Static requests with records.
652        check_verify(Mode::Private, "test.aleo", "bark", count_is!(<=40943, 0, 29913, 29944), false, false, true)?;
653        check_verify(Mode::Private, "test.aleo", "bark", count_is!(8502, 0, 30428, 30459), true, false, true)?;
654        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(8472, 0, 30015, 30046), false, false, true)?;
655        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(8472, 0, 30530, 30561), true, false, true)?;
656
657        // Dynamic requests with records.
658        check_verify(Mode::Private, "test.aleo", "bark", count_is!(8502, 0, 29913, 29944), false, true, true)?;
659        check_verify(Mode::Private, "test.aleo", "bark", count_is!(8502, 0, 30428, 30459), true, true, true)?;
660        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(8472, 0, 30015, 30046), false, true, true)?;
661        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(8472, 0, 30530, 30561), true, true, true)?;
662
663        // Static requests without records.
664        check_verify(Mode::Private, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), false, false, false)?;
665        check_verify(Mode::Private, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), true, false, false)?;
666        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), false, false, false)?;
667        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), true, false, false)?;
668
669        // Dynamic requests without records.
670        check_verify(Mode::Private, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), false, true, false)?;
671        check_verify(Mode::Private, "test.aleo", "bark", count_is!(3233, 0, 12615, 12624), true, true, false)?;
672        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), false, true, false)?;
673        check_verify(Mode::Private, "credits.aleo", "foo", count_is!(3273, 0, 12615, 12624), true, true, false)?;
674
675        Ok(())
676    }
677
678    #[test]
679    #[rustfmt::skip]
680    fn test_check_input_ids_constant() -> Result<()> {
681        // Static requests with records.
682        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(<=34027, 0, 11710, 11726), false, false, true)?;
683        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(4096, 0, 11710, 11726), true, false, true)?;
684        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(4046, 0, 11812, 11828), false, false, true)?;
685        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(4046, 0, 11812, 11828), true, false, true)?;
686        
687        // Dynamic requests with records.
688        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(3480, 0, 11710, 11726), false, true, true)?;
689        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(3480, 0, 11710, 11726), true, true, true)?;
690        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(3410, 0, 11812, 11828), false, true, true)?;
691        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(3410, 0, 11812, 11828), true, true, true)?;
692
693        // Static requests without records.
694        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(858, 0, 2948, 2952), false, false, false)?;
695        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(858, 0, 2948, 2952), true, false, false)?;
696        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(878, 0, 2948, 2952), false, false, false)?;
697        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(878, 0, 2948, 2952), true, false, false)?;
698        
699        // Dynamic requests without records.
700        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(242, 0, 2948, 2952), false, true, false)?;
701        check_check_input_ids(Mode::Constant, "test.aleo", "bark", count_is!(242, 0, 2948, 2952), true, true, false)?;
702        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(242, 0, 2948, 2952), false, true, false)?;
703        check_check_input_ids(Mode::Constant, "credits.aleo", "foo", count_is!(242, 0, 2948, 2952), true, true, false)?;
704
705
706        Ok(())
707    }
708
709    #[test]
710    #[rustfmt::skip]
711    fn test_check_input_ids_public() -> Result<()> {
712        // Static requests with records.
713        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(<=34027, 0, 12530, 12546), false, false, true)?;
714        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(4096, 0, 12530, 12546), true, false, true)?;
715        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(4046, 0, 12632, 12648), false, false, true)?;
716        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(4046, 0, 12632, 12648), true, false, true)?;
717
718        // Dynamic requests with records.
719        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(3480, 0, 12530, 12546), false, true, true)?;
720        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(3480, 0, 12530, 12546), true, true, true)?;
721        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(3410, 0, 12632, 12648), false, true, true)?;
722        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(3410, 0, 12632, 12648), true, true, true)?;
723
724        // Static requests without records.
725        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(858, 0, 3763, 3767), false, false, false)?;
726        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(858, 0, 3763, 3767), true, false, false)?;
727        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(878, 0, 3763, 3767), false, false, false)?;
728        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(878, 0, 3763, 3767), true, false, false)?;
729
730        // Dynamic requests without records.
731        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(242, 0, 3763, 3767), false, true, false)?;
732        check_check_input_ids(Mode::Public,  "test.aleo", "bark", count_is!(242, 0, 3763, 3767), true, true, false)?;
733        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(242, 0, 3763, 3767), false, true, false)?;
734        check_check_input_ids(Mode::Public,  "credits.aleo", "foo", count_is!(242, 0, 3763, 3767), true, true, false)?;
735
736        Ok(())
737    }
738
739    #[test]
740    #[rustfmt::skip]
741    fn test_check_input_ids_private() -> Result<()> {
742        // Static requests with records.
743        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(<=34027, 0, 12530, 12546), false, false, true)?;
744        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(4096, 0, 12530, 12546), true, false, true)?;
745        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(4046, 0, 12632, 12648), false, false, true)?;
746        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(4046, 0, 12632, 12648), true, false, true)?;
747
748        // Dynamic requests with records.
749        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(3480, 0, 12530, 12546), false, true, true)?;
750        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(3480, 0, 12530, 12546), true, true, true)?;
751        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(3410, 0, 12632, 12648), false, true, true)?;
752        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(3410, 0, 12632, 12648), true, true, true)?;
753
754        // Static requests without records.
755        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(858, 0, 3763, 3767), false, false, false)?;
756        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(858, 0, 3763, 3767), true, false, false)?;
757        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(878, 0, 3763, 3767), false, false, false)?;
758        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(878, 0, 3763, 3767), true, false, false)?;
759
760        // Dynamic requests without records.
761        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(242, 0, 3763, 3767), false, true, false)?;
762        check_check_input_ids(Mode::Private,  "test.aleo", "bark", count_is!(242, 0, 3763, 3767), true, true, false)?;
763        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(242, 0, 3763, 3767), false, true, false)?;
764        check_check_input_ids(Mode::Private,  "credits.aleo", "foo", count_is!(242, 0, 3763, 3767), true, true, false)?;
765
766
767        Ok(())
768    }
769}