snarkvm_circuit_program/request/
mod.rs

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