Skip to main content

snarkvm_circuit_program/request/
mod.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
16mod to_tpk;
17mod verify;
18
19use crate::{Identifier, Plaintext, ProgramID, Record, Value, compute_function_id};
20use snarkvm_circuit_account::Signature;
21use snarkvm_circuit_network::Aleo;
22use snarkvm_circuit_types::{Address, Boolean, Field, Group, U16, environment::prelude::*};
23
24pub enum InputID<A: Aleo> {
25    /// The hash of the constant input.
26    Constant(Field<A>),
27    /// The hash of the public input.
28    Public(Field<A>),
29    /// The ciphertext hash of the private input.
30    Private(Field<A>),
31    /// The `(commitment, gamma, record_view_key, serial_number, tag)` tuple of the record input.
32    Record(Field<A>, Box<Group<A>>, Field<A>, Field<A>, Field<A>),
33    /// The hash of the external record's (function_id, record, tvk, input index).
34    ExternalRecord(Field<A>),
35    /// The hash of a dynamic record's (function_id, record, tvk, input index).
36    DynamicRecord(Field<A>),
37}
38
39impl<A: Aleo> Inject for InputID<A> {
40    type Primitive = console::InputID<A::Network>;
41
42    /// Initializes the input ID from the given mode and console input ID.
43    fn new(_: Mode, input: Self::Primitive) -> Self {
44        match input {
45            // Inject the expected hash as `Mode::Public`.
46            console::InputID::Constant(field) => Self::Constant(Field::new(Mode::Public, field)),
47            // Inject the expected hash as `Mode::Public`.
48            console::InputID::Public(field) => Self::Public(Field::new(Mode::Public, field)),
49            // Inject the ciphertext hash as `Mode::Public`.
50            console::InputID::Private(field) => Self::Private(Field::new(Mode::Public, field)),
51            // Inject commitment, gamma, and record view key as `Mode::Private`, and the expected serial number and tag as `Mode::Public`.
52            console::InputID::Record(commitment, gamma, record_view_key, serial_number, tag) => Self::Record(
53                Field::new(Mode::Private, commitment),
54                Box::new(Group::new(Mode::Private, gamma)),
55                Field::new(Mode::Private, record_view_key),
56                Field::new(Mode::Public, serial_number),
57                Field::new(Mode::Public, tag),
58            ),
59            // Inject the commitment of the external record as `Mode::Public`.
60            console::InputID::ExternalRecord(field) => Self::ExternalRecord(Field::new(Mode::Public, field)),
61            // Inject the commitment of the dynamic record as `Mode::Public`.
62            console::InputID::DynamicRecord(field) => Self::DynamicRecord(Field::new(Mode::Public, field)),
63        }
64    }
65}
66
67impl<A: Aleo> Eject for InputID<A> {
68    type Primitive = console::InputID<A::Network>;
69
70    /// Ejects the mode of the input ID.
71    fn eject_mode(&self) -> Mode {
72        match self {
73            Self::Constant(field) => field.eject_mode(),
74            Self::Public(field) => field.eject_mode(),
75            Self::Private(field) => field.eject_mode(),
76            Self::Record(commitment, gamma, record_view_key, serial_number, tag) => {
77                Mode::combine(commitment.eject_mode(), [
78                    gamma.eject_mode(),
79                    record_view_key.eject_mode(),
80                    serial_number.eject_mode(),
81                    tag.eject_mode(),
82                ])
83            }
84            Self::ExternalRecord(field) => field.eject_mode(),
85            Self::DynamicRecord(field) => field.eject_mode(),
86        }
87    }
88
89    /// Ejects the input ID as a primitive.
90    fn eject_value(&self) -> Self::Primitive {
91        match self {
92            Self::Constant(field) => console::InputID::Constant(field.eject_value()),
93            Self::Public(field) => console::InputID::Public(field.eject_value()),
94            Self::Private(field) => console::InputID::Private(field.eject_value()),
95            Self::Record(commitment, gamma, record_view_key, serial_number, tag) => console::InputID::Record(
96                commitment.eject_value(),
97                gamma.eject_value(),
98                record_view_key.eject_value(),
99                serial_number.eject_value(),
100                tag.eject_value(),
101            ),
102            Self::ExternalRecord(field) => console::InputID::ExternalRecord(field.eject_value()),
103            Self::DynamicRecord(field) => console::InputID::DynamicRecord(field.eject_value()),
104        }
105    }
106}
107
108impl<A: Aleo> ToFields for InputID<A> {
109    type Field = Field<A>;
110
111    /// Returns the input as a list of field elements.
112    fn to_fields(&self) -> Vec<Self::Field> {
113        match self {
114            InputID::Constant(field) => vec![field.clone()],
115            InputID::Public(field) => vec![field.clone()],
116            InputID::Private(field) => vec![field.clone()],
117            InputID::Record(commitment, gamma, record_view_key, serial_number, tag) => {
118                vec![
119                    commitment.clone(),
120                    gamma.to_x_coordinate(),
121                    record_view_key.clone(),
122                    serial_number.clone(),
123                    tag.clone(),
124                ]
125            }
126            InputID::ExternalRecord(field) => vec![field.clone()],
127            InputID::DynamicRecord(field) => vec![field.clone()],
128        }
129    }
130}
131
132pub struct Request<A: Aleo> {
133    /// The request signer.
134    signer: Address<A>,
135    /// The network ID.
136    network_id: U16<A>,
137    /// The program ID.
138    program_id: ProgramID<A>,
139    /// The function name.
140    function_name: Identifier<A>,
141    /// The function input IDs.
142    input_ids: Vec<InputID<A>>,
143    /// The function inputs.
144    inputs: Vec<Value<A>>,
145    /// The signature for the transition.
146    signature: Signature<A>,
147    /// The tag secret key.
148    sk_tag: Field<A>,
149    /// The transition view key.
150    tvk: Field<A>,
151    /// The transition commitment.
152    tcm: Field<A>,
153    /// The signer commitment.
154    scm: Field<A>,
155    /// A flag indicating whether or not the request is dynamic.
156    is_dynamic: bool,
157}
158
159impl<A: Aleo> Inject for Request<A> {
160    type Primitive = console::Request<A::Network>;
161
162    /// Initializes the request from the given mode and console request.
163    fn new(mode: Mode, request: Self::Primitive) -> Self {
164        // Inject the transition commitment `tcm` as `Mode::Public`.
165        let tcm = Field::new(Mode::Public, *request.tcm());
166
167        // Inject the signer commitment `scm` as `Mode::Public`.
168        let scm = Field::new(Mode::Public, *request.scm());
169
170        // Inject the inputs.
171        let inputs = match request
172            .input_ids()
173            .iter()
174            .zip_eq(request.inputs())
175            .map(|(input_id, input)| {
176                match input_id {
177                    // A constant input is injected as `Mode::Constant`.
178                    console::InputID::Constant(..) => {
179                        // Inject the input as `Mode::Constant`.
180                        let input = Value::new(Mode::Constant, input.clone());
181                        // Ensure the input is a plaintext.
182                        ensure!(matches!(input, Value::Plaintext(..)), "Expected a plaintext input");
183                        // Return the input.
184                        Ok(input)
185                    }
186                    // A public input is injected as `Mode::Private`.
187                    console::InputID::Public(..) => {
188                        // Inject the input as `Mode::Private`.
189                        let input = Value::new(Mode::Private, input.clone());
190                        // Ensure the input is a plaintext.
191                        ensure!(matches!(input, Value::Plaintext(..)), "Expected a plaintext input");
192                        // Return the input.
193                        Ok(input)
194                    }
195                    // A private input is injected as `Mode::Private`.
196                    console::InputID::Private(..) => {
197                        // Inject the input as `Mode::Private`.
198                        let input = Value::new(Mode::Private, input.clone());
199                        // Ensure the input is a plaintext.
200                        ensure!(matches!(input, Value::Plaintext(..)), "Expected a plaintext input");
201                        // Return the input.
202                        Ok(input)
203                    }
204                    // A record input is injected as `Mode::Private`.
205                    console::InputID::Record(..) => {
206                        // Inject the input as `Mode::Private`.
207                        let input = Value::new(Mode::Private, input.clone());
208                        // Ensure the input is a record.
209                        ensure!(matches!(input, Value::Record(..)), "Expected a record input");
210                        // Return the input.
211                        Ok(input)
212                    }
213                    // An external record input is injected as `Mode::Private`.
214                    console::InputID::ExternalRecord(..) => {
215                        // Inject the input as `Mode::Private`.
216                        let input = Value::new(Mode::Private, input.clone());
217                        // Ensure the input is a record.
218                        ensure!(matches!(input, Value::Record(..)), "Expected an external record input");
219                        // Return the input.
220                        Ok(input)
221                    }
222                    // A dynamic record input is injected as `Mode::Private`.
223                    console::InputID::DynamicRecord(..) => {
224                        // Inject the input as `Mode::Private`.
225                        let input = Value::new(Mode::Private, input.clone());
226                        // Ensure the input is a dynamic record.
227                        ensure!(matches!(input, Value::DynamicRecord(..)), "Expected a dynamic record input");
228                        // Return the input.
229                        Ok(input)
230                    }
231                }
232            })
233            .collect::<Result<Vec<_>, _>>()
234        {
235            Ok(inputs) => inputs,
236            Err(error) => A::halt(format!("{error}")),
237        };
238
239        Self {
240            signer: Address::new(mode, *request.signer()),
241            network_id: U16::new(Mode::Constant, *request.network_id()),
242            program_id: ProgramID::constant(*request.program_id()),
243            function_name: Identifier::constant(*request.function_name()),
244            input_ids: request.input_ids().iter().map(|input_id| InputID::new(Mode::Public, *input_id)).collect(),
245            inputs,
246            signature: Signature::new(mode, *request.signature()),
247            sk_tag: Field::new(mode, *request.sk_tag()),
248            tvk: Field::new(mode, *request.tvk()),
249            tcm,
250            scm,
251            is_dynamic: request.is_dynamic(),
252        }
253    }
254}
255
256impl<A: Aleo> Request<A> {
257    /// Returns the request signer.
258    pub const fn signer(&self) -> &Address<A> {
259        &self.signer
260    }
261
262    /// Returns the network ID.
263    pub const fn network_id(&self) -> &U16<A> {
264        &self.network_id
265    }
266
267    /// Returns the program ID.
268    pub const fn program_id(&self) -> &ProgramID<A> {
269        &self.program_id
270    }
271
272    /// Returns the function name.
273    pub const fn function_name(&self) -> &Identifier<A> {
274        &self.function_name
275    }
276
277    /// Returns the input IDs for the transition.
278    pub fn input_ids(&self) -> &[InputID<A>] {
279        &self.input_ids
280    }
281
282    /// Returns the function inputs.
283    pub fn inputs(&self) -> &[Value<A>] {
284        &self.inputs
285    }
286
287    /// Returns the signature for the transition.
288    pub const fn signature(&self) -> &Signature<A> {
289        &self.signature
290    }
291
292    /// Returns the tag secret key.
293    pub const fn sk_tag(&self) -> &Field<A> {
294        &self.sk_tag
295    }
296
297    /// Returns the transition view key.
298    pub const fn tvk(&self) -> &Field<A> {
299        &self.tvk
300    }
301
302    /// Returns the transition commitment.
303    pub const fn tcm(&self) -> &Field<A> {
304        &self.tcm
305    }
306
307    /// Returns the signer commitment.
308    pub const fn scm(&self) -> &Field<A> {
309        &self.scm
310    }
311
312    /// Returns whether or not the request is dynamic.
313    pub fn is_dynamic(&self) -> bool {
314        self.is_dynamic
315    }
316}
317
318impl<A: Aleo> Eject for Request<A> {
319    type Primitive = console::Request<A::Network>;
320
321    /// Ejects the mode of the request.
322    fn eject_mode(&self) -> Mode {
323        Mode::combine(self.signer.eject_mode(), [
324            self.network_id.eject_mode(),
325            self.program_id.eject_mode(),
326            self.function_name.eject_mode(),
327            self.input_ids.eject_mode(),
328            self.inputs.eject_mode(),
329            self.signature.eject_mode(),
330            self.sk_tag.eject_mode(),
331            self.tvk.eject_mode(),
332            self.tcm.eject_mode(),
333            self.scm.eject_mode(),
334        ])
335    }
336
337    /// Ejects the request as a primitive.
338    fn eject_value(&self) -> Self::Primitive {
339        Self::Primitive::from((
340            self.signer.eject_value(),
341            self.network_id.eject_value(),
342            self.program_id.eject_value(),
343            self.function_name.eject_value(),
344            self.input_ids.iter().map(|input_id| input_id.eject_value()).collect(),
345            self.inputs.eject_value(),
346            self.signature.eject_value(),
347            self.sk_tag.eject_value(),
348            self.tvk.eject_value(),
349            self.tcm.eject_value(),
350            self.scm.eject_value(),
351            self.is_dynamic,
352        ))
353    }
354}